Commit 682a300f authored by Roger Barton's avatar Roger Barton
Browse files

Improved events list, UI styling, added fonts

parent 1942d9ed
Pipeline #4511 failed with stages
in 7 seconds
......@@ -70,17 +70,14 @@
<activity
android:name=".checkin.MainActivity"
android:screenOrientation="portrait"
android:label="@string/app_name"
android:label="@string/app_name_checkin"
android:theme="@style/CheckinTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".checkin.ScanActivity"
android:screenOrientation="portrait"
android:theme="@style/CheckinTheme">
android:theme="@style/CheckinTheme"
android:label="@string/app_name_checkin">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".checkin.MainActivity" />
......@@ -88,7 +85,7 @@
<activity android:name=".checkin.SettingsActivity"
android:screenOrientation="portrait"
android:label="@string/app_name"
android:label="@string/app_name_checkin"
android:theme="@style/CheckinTheme">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
......@@ -97,7 +94,7 @@
<activity android:name=".checkin.MemberListActivity"
android:theme="@style/CheckinTheme"
android:label="@string/app_name">
android:label="@string/app_name_checkin">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
......@@ -110,7 +107,7 @@
<activity android:name=".checkin.SearchMembersActivity"
android:theme="@style/CheckinTheme"
android:label="@string/app_name">
android:label="@string/app_name_checkin">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".checkin.MemberListActivity" />
......
......@@ -25,6 +25,8 @@ import android.widget.TextView;
import ch.amiv.android_app.R;
import ch.amiv.android_app.core.Settings;
public class MainActivity extends AppCompatActivity {
public static String CurrentPin;
private boolean mWaitingOnServer = false;
......@@ -32,8 +34,6 @@ public class MainActivity extends AppCompatActivity {
private EditText mPinField;
private TextView mInvalidPinLabel;
public static Vibrator vibrator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......@@ -47,15 +47,13 @@ public class MainActivity extends AppCompatActivity {
public void onPause()
{
super.onPause();
if(vibrator != null)
vibrator.cancel();
Settings.CancelVibrate();
}
private void InitialiseUI()
{
mPinField = findViewById(R.id.PinField);
mInvalidPinLabel = findViewById(R.id.InvalidPinLabel);
vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
mPinField.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View view, int keyCode, KeyEvent keyevent) {
......@@ -93,8 +91,7 @@ public class MainActivity extends AppCompatActivity {
*/
public void SubmitPin(View view)
{
if(vibrator != null)
vibrator.vibrate(50);
Settings.Vibrate(Settings.VibrateTime.SHORT, getApplicationContext());
View button = findViewById(R.id.SubmitPin);
if(button != null)
button.startAnimation(AnimationUtils.loadAnimation(this, R.anim.item_anim_pop));
......
......@@ -41,6 +41,8 @@ import java.io.IOException;
import ch.amiv.android_app.R;
import ch.amiv.android_app.core.Settings;
public class ScanActivity extends AppCompatActivity {
private static int NEXT_LEGI_DELAY = 1000; //delay between the response from the server and scanning the next legi (in ms)
......@@ -269,8 +271,7 @@ public class ScanActivity extends AppCompatActivity {
if(mWaitingOnServer_LegiSubmit)
return;
if(MainActivity.vibrator == null)
MainActivity.vibrator.vibrate(50);
Settings.Vibrate(Settings.VibrateTime.SHORT, getApplicationContext());
if(!ServerRequests.CheckConnection(getApplicationContext())) {
SetUIFromResponse_Invalid(0, getResources().getString(R.string.no_internet));
......@@ -350,7 +351,7 @@ public class ScanActivity extends AppCompatActivity {
mBGTint.setColorFilter(getResources().getColor(R.color.colorOrange));
}
MainActivity.vibrator.vibrate(100);
Settings.Vibrate(Settings.VibrateTime.NORMAL, getApplicationContext());
}
else
{
......@@ -384,7 +385,7 @@ public class ScanActivity extends AppCompatActivity {
mBGTint.setColorFilter(getResources().getColor(R.color.colorOrange));
}
MainActivity.vibrator.vibrate(100);
Settings.Vibrate(Settings.VibrateTime.NORMAL, getApplicationContext());
}
else if (statusCode == 0) //no internet
{
......@@ -404,7 +405,7 @@ public class ScanActivity extends AppCompatActivity {
mBGTint.setVisibility(View.VISIBLE);
mBGTint.setColorFilter(getResources().getColor(R.color.colorInvalid));
MainActivity.vibrator.vibrate(250);
Settings.Vibrate(Settings.VibrateTime.LONG, getApplicationContext());
}
RefreshMemberDB();
......
......@@ -29,7 +29,10 @@ import com.android.volley.toolbox.StringRequest;
import org.json.JSONException;
import org.json.JSONObject;
import java.security.acl.Group;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
......@@ -41,7 +44,21 @@ import ch.amiv.android_app.R;
*/
public class EventDetailActivity extends AppCompatActivity {
private int eventGroup = 0;
private int eventIndex = 0;
private EventInfo event(){ //Used to easily access the activities event
if(!hasEvent())
return null;
return Events.sortedEvents.get(eventGroup).get(eventIndex);
}
private boolean hasEvent(){
if(eventGroup >= Events.sortedEvents.size() || eventIndex >= Events.sortedEvents.get(eventGroup).size()){
Log.e("events", "EventDetailActivity given invalid event indexes, (group, index) = (" + eventGroup + ", " + eventIndex + "), with sortedEvents size of 1st dim: " + Events.sortedEvents.size());
return false;
}
return true;
}
private ImageView posterImage;
private ImageView posterMask;
......@@ -82,7 +99,7 @@ public class EventDetailActivity extends AppCompatActivity {
public void OnDataReceived() {
UpdateRegisterButton();
}
});
}, "");
responseIntent.putExtra("login_success", refreshLogin);
ScrollToBottom(null);
RegisterForEvent(null);
......@@ -104,11 +121,12 @@ public class EventDetailActivity extends AppCompatActivity {
}
/**
* This will retrieve the eventIndex to display, is only set when we originate from the MainActivity, where the int is added to the intent.
* This will retrieve the eventIndexes to display, is only set when we originate from the MainActivity, where the int is added to the intent.
*/
private void GetIntentData (){
if(eventIndex == 0) {
if(eventGroup == 0 && eventIndex == 0) {
Intent intent = getIntent();
eventGroup = intent.getIntExtra("eventGroup", 0);
eventIndex = intent.getIntExtra("eventIndex", 0);
}
}
......@@ -123,15 +141,15 @@ public class EventDetailActivity extends AppCompatActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//Check that we have been given an event that exists else return to the calling activity
if(eventIndex < 0 || Events.eventInfos.size() <= eventIndex) {
Log.e("events", "invlaid event index selected during InitUI(), eventIndex: " + eventIndex + ", size" + Events.eventInfos.size() + ". Ensure that you are not clearing/overwiting the events list while viewing an event.");
if(!hasEvent()) {
Log.e("events", "invlaid event index selected during InitUI(), (groupIndex, eventIndex): (" + eventGroup + "," + eventIndex + "), total event size" + Events.eventInfos.size() + ". Ensure that you are not clearing/overwiting the events list while viewing an event.");
onBackPressed();
return;
}
//Link up variables with UI elements from the layout xml
((TextView) findViewById(R.id.eventTitle)).setText(Events.eventInfos.get(eventIndex).GetTitle(getResources()));
((TextView)findViewById(R.id.eventDetail)).setText(Events.eventInfos.get(eventIndex).GetDescription(getResources()));
((TextView) findViewById(R.id.eventTitle)).setText(event().GetTitle(getResources()));
((TextView)findViewById(R.id.eventDetail)).setText(event().GetDescription(getResources()));
scrollView = findViewById(R.id.scrollView_event);
posterProgress = findViewById(R.id.progressBar);
posterImage = findViewById(R.id.eventPoster);
......@@ -160,7 +178,7 @@ public class EventDetailActivity extends AppCompatActivity {
private void LoadEventImage (boolean isRefreshing)
{
//Image loading and masking. the posterMask is a small arrow image but we use the layout margin to add some transparent 'padding' to the top of the scrollview
if(Events.eventInfos.get(eventIndex).poster_url.isEmpty() || !Requests.CheckConnection(getApplicationContext()))
if(event().poster_url.isEmpty() || !Requests.CheckConnection(getApplicationContext()))
{
//Hide the image and mask if there is no poster linked with the event or we have no internet
if(!isRefreshing) {
......@@ -192,7 +210,7 @@ public class EventDetailActivity extends AppCompatActivity {
//generate URL for the poster
StringBuilder posterUrl = new StringBuilder();
posterUrl.append(Events.eventInfos.get(eventIndex).poster_url); //To show the banner instead change this variable
posterUrl.append(event().poster_url); //To show the banner instead change this variable
if(posterUrl.charAt(0) == '/')
posterUrl.deleteCharAt(0);
posterUrl.insert(0, Settings.API_URL);
......@@ -254,7 +272,7 @@ public class EventDetailActivity extends AppCompatActivity {
LinearLayout linear = findViewById(R.id.register_details_list);
linear.removeAllViews();
ArrayList<String[]> infos = Events.eventInfos.get(eventIndex).GetInfos(getResources());
ArrayList<String[]> infos = event().GetInfos(getResources());
LayoutInflater inflater = LayoutInflater.from(getApplicationContext());
for (int i = 0; i < infos.size(); i++) {
//Create a view from the xml and then add it as a child of the listview
......@@ -285,7 +303,8 @@ public class EventDetailActivity extends AppCompatActivity {
return;
}
final int registerEventIndex = eventIndex; //declare final so it does not change
final int registerEventGroup = eventGroup; //declare final so it does not change
final int registerEventIndex = eventIndex;
String url = Settings.API_URL + "eventsignups";
StringRequest request = new StringRequest(Request.Method.POST, url,null, null)
......@@ -298,23 +317,24 @@ public class EventDetailActivity extends AppCompatActivity {
try {
Log.e("request", new String(response.data));
JSONObject json = new JSONObject(new String(response.data));
Events.eventInfos.get(registerEventIndex).AddSignup(json); //Register signup
event().AddSignup(json); //Register signup
//We need to fetch signups again as the response for registering for an event is not a complete signup object
//Fetch event signup object again for this event id
Requests.FetchEventSignups(getApplicationContext(), new Requests.OnDataReceivedCallback() {
@Override
public void OnDataReceived() {
UpdateRegisterButton();
}
});
}, event()._id);
//Interpret notification to show from the signup
int notification = 0;
if(Events.eventInfos.get(eventIndex).accepted) {
if(Events.eventInfos.get(eventIndex).confirmed)
if(event().accepted) {
if(event().confirmed)
notification = R.string.register_success;
else
notification = R.string.register_success_confirm_required;
Settings.Vibrate(Settings.VibrateTime.NORMAL, getApplicationContext());
} else
notification = R.string.added_to_waiting_list;
Snackbar.make(scrollView, notification, Snackbar.LENGTH_LONG).show();
......@@ -357,7 +377,7 @@ public class EventDetailActivity extends AppCompatActivity {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
params.put("event", Events.eventInfos.get(eventIndex)._id);
params.put("event", event()._id);
params.put("user", UserInfo.current._id);
return params;
}
......@@ -366,14 +386,36 @@ public class EventDetailActivity extends AppCompatActivity {
Requests.SendRequest(request, getApplicationContext());
}
/**
* Will set the register button appearance accordingly to the dates of the event and whether the user is signed up
*/
private void UpdateRegisterButton() {
if (Events.eventInfos.get(eventIndex).IsSignedUp()) {
if (event().IsSignedUp()) {
registerButton.setEnabled(false);
registerButton.setText(R.string.already_registered);
} else {
registerButton.setEnabled(true);
registerButton.setText(R.string.register_title);
return;
}
Date today = Calendar.getInstance().getTime();
if(event().time_register_start.before(today))
{
if(event().time_register_end.after(today))
{
registerButton.setEnabled(true);
registerButton.setText(R.string.register_title);
}
else
{
registerButton.setEnabled(false);
registerButton.setText(R.string.registration_closed);
}
}
else {
registerButton.setEnabled(false);
registerButton.setText(R.string.register_soon);
}
}
public void ScrollToTop (View view) {
......
......@@ -171,6 +171,11 @@ public class EventInfo {
if(!location.isEmpty()) infos.add(new String[]{ r.getString(R.string.location), location});
if(time_register_start != null) infos.add(new String[]{ r.getString(R.string.register_start), dateFormat.format(time_register_start)});
if(time_register_end != null) infos.add(new String[]{ r.getString(R.string.register_end), dateFormat.format(time_register_end)});
/*//DEBUG
if(time_advertising_start != null) infos.add(new String[]{ ("Ad Start"), dateFormat.format(time_advertising_start)});
if(time_advertising_end != null) infos.add(new String[]{ ("Ad End"), dateFormat.format(time_advertising_end)});*/
infos.add(new String[]{ r.getString(R.string.available_places), (spots <= 0 ? "-" : "" + Math.max(0, spots - signup_count))});
if(spots - signup_count < 0)
infos.add(new String[]{ r.getString(R.string.waiting_list_size), "" + (signup_count - spots)});
......
......@@ -7,13 +7,30 @@ import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
/**
* This is the central place for storing information about the events, events + signups.
*/
public final class Events {
public static List<EventInfo> eventInfos = new ArrayList<EventInfo>();
public static List<EventInfo> eventInfos = new ArrayList<EventInfo>(); //This is a list of ALL events as received from the api, we will not use this directly
public static List<List<EventInfo>> sortedEvents = new ArrayList<List<EventInfo>>(4); //A list of lists which has been sorted according to the EventGroup configuration
public static boolean[] invertEventGroupSorting = new boolean[] {false, false, false, true}; //used to invert date sorting for the event groups
//Use this class to use the correct indexes for the event group for the sortedEvents list
public static final class EventGroup {
public static final int HIDDEN_EVENTS = 0;
public static final int ALL_EVENTS = 1;
public static final int CLOSED_EVENTS = 2;
public static final int PAST_EVENTS = 3;
}
//Defines for how many days after the ad start date the new tag is visible for
public static final int DAYS_NEW_TAG_ACTIVE = 3;
/**
* Update the event infos with the data received from the api. This is just for updating information about the event NOT the signup
......@@ -22,6 +39,8 @@ public final class Events {
public static void UpdateEventInfos(JSONArray json)
{
eventInfos.clear();
sortedEvents.clear();
for (int i = 0; i < json.length(); i++)
{
try {
......@@ -30,6 +49,37 @@ public final class Events {
e.printStackTrace();
}
}
if(!eventInfos.isEmpty()){
//sort so first elem has an advertising start date furthest in the future
Comparator<EventInfo> comparator;
comparator = new Comparator<EventInfo>() {
@Override
public int compare(EventInfo a, EventInfo b) {
return b.time_advertising_start.compareTo(a.time_advertising_start);
}
};
Collections.sort(eventInfos, comparator);
Date today = Calendar.getInstance().getTime();
sortedEvents.add(new ArrayList<EventInfo>());
sortedEvents.add(new ArrayList<EventInfo>());
sortedEvents.add(new ArrayList<EventInfo>());
sortedEvents.add(new ArrayList<EventInfo>());
//fill in the sorted list according to the dates of the events
for (int i = 0; i < eventInfos.size(); i++){
if(eventInfos.get(i).time_advertising_start.after(today))
sortedEvents.get(EventGroup.HIDDEN_EVENTS).add(eventInfos.get(i));
else if(eventInfos.get(i).time_register_end.after(today))
sortedEvents.get(EventGroup.ALL_EVENTS).add(eventInfos.get(i));
else if(eventInfos.get(i).time_end.after(today))
sortedEvents.get(EventGroup.CLOSED_EVENTS).add(eventInfos.get(i));
else
sortedEvents.get(EventGroup.PAST_EVENTS).add(eventInfos.get(i));
}
}
}
/**
......
......@@ -10,56 +10,113 @@ import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import ch.amiv.android_app.R;
public class EventsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<String> headerList = new ArrayList<String>();
private List<Pair> dataList = new ArrayList<>();
private Activity activity;
//Whether to show hidden events, where the adverts should not have started yet, should later be set by user access group
private boolean showHidden = true;
private static final class ViewType {
private static final int HEADER = 0;
private static final int SPACE = 1;
private static final int EVENT = 2;
}
private class Pair {
public int type;
public Object value;
private Pair(int type, Object value) {
this.type = type;
this.value = value;
}
}
/**
* Defining our own view holder which maps the layout items to view variables which can then later be accessed, and text value set etc
* For each item type we have to define a viewholder. This will map the layout to the variables
*/
public class EventInfoHolder extends RecyclerView.ViewHolder {
private class EventInfoHolder extends RecyclerView.ViewHolder {
TextView titleField;
TextView catchphraseField;
TextView placesField;
TextView newTag;
ImageView statusImage;
public EventInfoHolder(View view) {
private EventInfoHolder(View view) {
super(view);
titleField = view.findViewById(R.id.titleField);
catchphraseField = view.findViewById(R.id.infoField);
placesField = view.findViewById(R.id.places_left);
newTag = view.findViewById(R.id.newTag);
statusImage = view.findViewById(R.id.signupStatus);
}
}
public class HeaderHolder extends RecyclerView.ViewHolder {
private class HeaderHolder extends RecyclerView.ViewHolder {
TextView nameField;
public HeaderHolder(View view) {
private HeaderHolder(View view) {
super(view);
nameField = view.findViewById(R.id.titleField);
}
}
public class SpaceHolder extends RecyclerView.ViewHolder {
private class SpaceHolder extends RecyclerView.ViewHolder {
View space;
public SpaceHolder(View view) {
private SpaceHolder(View view) {
super(view);
space = view.findViewById(R.id.space);
}
}
public EventsListAdapter(Activity activity_) {
headerList.add(activity_.getResources().getString(R.string.new_events_title));
//headerList.add("Attended Events"); //XXX Add sorting of old events, where checkin or confirmed is true
activity = activity_;
}
public void RefreshData(){
BuildDataset();
notifyDataSetChanged();
}
public void BuildDataset ()
{
dataList.clear();
List<Integer> headers = new ArrayList<>();
if(showHidden)
headers.add(R.string.hidden_events_title);
headers.add(R.string.all_events_title);
headers.add(R.string.closed_events_title);
headers.add(R.string.past_events_title);
//Debug: Start at 0 to show hidden events, headers will be offset though
for (int i = (showHidden ? 0 : 1); i < Events.sortedEvents.size(); i++) {
if(i < headers.size())
dataList.add(new Pair(ViewType.HEADER, activity.getResources().getString(headers.get(i))));
//invert order on the specified groups
if(i >= Events.invertEventGroupSorting.length || !Events.invertEventGroupSorting[i]) {
for (int j = 0; j < Events.sortedEvents.get(i).size(); j++) {
dataList.add(new Pair(ViewType.EVENT, new int[]{i, j}));
}
}
else{
for (int j = Events.sortedEvents.get(i).size() -1; j >= 0; j--) {
dataList.add(new Pair(ViewType.EVENT, new int[]{i, j}));
}
}
}
dataList.add(new Pair(ViewType.SPACE, 128));
}
/**
* This is used when creating a new UI list item. Depending on the type we use a different layout xml
*/
......@@ -70,15 +127,15 @@ public class EventsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
switch (viewType)
{
case 0: //header
case ViewType.HEADER: //header
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.core_list_item_header, parent, false);
holder = new HeaderHolder(view);
break;
case 1: //space
case ViewType.SPACE: //space
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.core_list_item_space, parent, false);
holder = new SpaceHolder(view);
break;
case 2: //event
case ViewType.EVENT: //event
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.core_list_item_event, parent, false);
holder = new EventInfoHolder(view);
break;
......@@ -94,41 +151,58 @@ public class EventsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
*/
@Override
public int getItemViewType(int position) { //Note stat and event info use the same layout, but types are different
if(position == 0 /*|| position == Events.eventInfos.size() + 1*/)
return 0; //header
if(position < Events.eventInfos.size() +1)
return 2; //events
else
return 1; //Space
return dataList.get(position).type;
}
/**
* This is where the data in the ui is set. Note that position is the position on screen whereas getAdapterPos is the position in the whole list
*/
@Override