Commit 2d2b5a52 authored by Roger Barton's avatar Roger Barton
Browse files

Improved event detail activity

parent 552e5a27
......@@ -2,24 +2,42 @@ package ch.amiv.android_app;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.MaskFilter;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.util.DisplayMetrics;
import android.util.Base64;
import android.util.Log;
import android.util.Xml;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageRequest;
import java.net.URL;
import com.android.volley.toolbox.StringRequest;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimerTask;
public class EventDetailActivity extends AppCompatActivity {
......@@ -51,20 +69,36 @@ public class EventDetailActivity extends AppCompatActivity {
private void InitUI (){
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
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.");
return;
}
TextView title = findViewById(R.id.eventTitle);
TextView content = findViewById(R.id.eventDetail);
scrollView = findViewById(R.id.scrollView_event);
posterImage = findViewById(R.id.eventPoster);
posterMask = findViewById(R.id.posterMask);
title.setText(Events.eventInfos.get(eventIndex).title);
content.setText(Events.eventInfos.get(eventIndex).description);
if(!Events.eventInfos.get(eventIndex).posterUrl.isEmpty()) {
AddRegisterDetails();
if(Events.eventInfos.get(eventIndex).posterUrl.isEmpty())
{
posterImage.setVisibility(View.GONE);
posterMask.setVisibility(View.GONE);
findViewById(R.id.posterBg).setVisibility(View.GONE);
}
else
{
posterImage.setVisibility(View.VISIBLE);
posterMask.setVisibility(View.VISIBLE);
findViewById(R.id.posterBg).setVisibility(View.VISIBLE);
//generate URL to image
StringBuilder posterUrl = new StringBuilder();
posterUrl.append(Events.eventInfos.get(eventIndex).posterUrl);
......@@ -74,6 +108,8 @@ public class EventDetailActivity extends AppCompatActivity {
Log.e("request", "image url: " + posterUrl.toString());
//posterImage.setImageUrl(posterUrl.toString(), Requests.GetImageLoader(getApplicationContext()));
ImageRequest posterRequest = new ImageRequest(posterUrl.toString(),
new Response.Listener<Bitmap>() {
@Override
......@@ -86,11 +122,11 @@ public class EventDetailActivity extends AppCompatActivity {
ViewGroup.LayoutParams layoutParams = posterMask.getLayoutParams();
layoutParams.height = posterImage.getHeight();
posterMask.setLayoutParams(layoutParams);
findViewById(R.id.posterBg).setLayoutParams(layoutParams);
//findViewById(R.id.posterBg).setLayoutParams(layoutParams);
}
});
}
}, 0, 0, null, null,
}, 0, 0, ImageView.ScaleType.CENTER_INSIDE, Bitmap.Config.ARGB_8888,
new Response.ErrorListener() {
public void onErrorResponse(VolleyError error) {
posterImage.setImageResource(R.drawable.ic_error_white);
......@@ -100,6 +136,106 @@ public class EventDetailActivity extends AppCompatActivity {
}
}
private void AddRegisterDetails ()
{
ArrayList<String[]> infos = Events.eventInfos.get(eventIndex).GetInfos();
LayoutInflater inflater = LayoutInflater.from(getApplicationContext());
for (int i = 0; i < infos.size(); i++) {
LinearLayout layout = (LinearLayout) inflater.inflate(R.layout.list_item_keyval, null, false);
((TextView) layout.findViewById(R.id.keyField )).setText(infos.get(i)[0]);
((TextView) layout.findViewById(R.id.valueField)).setText(infos.get(i)[1]);
LinearLayout linear = findViewById(R.id.register_details_list);
linear.addView(layout);
}
}
public void RegisterForEvent(View view)
{
if(!Requests.CheckConnection(getApplicationContext())) {
Snackbar.make(view, "Requires Internet", Snackbar.LENGTH_LONG).show();
return;
}
if(!Settings.IsLoggedIn(getApplicationContext())){
Intent intent = new Intent(this, LoginActivity.class);
intent.putExtra("cause", "register_event");
startActivity(intent);
return;
}
final int registerEventIndex = eventIndex;
//Do request Token->User
String url = Settings.API_URL + "eventsignups";
StringRequest request = new StringRequest(Request.Method.POST, url,null, null)
{
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) { //Note: the parseNetworkResponse is only called if the response was successful (codes 2xx), else parseNetworkError is called.
if(response != null) {
Log.e("request", "status Code: " + response.statusCode);
try {
JSONObject json = new JSONObject(new String(response.data)).getJSONObject("user");
if(json.has("_status") && json.getString("_status").equals("OK"))
Events.eventInfos.get(registerEventIndex).AddSignup(json);
//Log.e("request", json.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
else
Log.e("request", "Request returned null response.");
return super.parseNetworkResponse(response);
}
@Override
protected VolleyError parseNetworkError(final VolleyError volleyError) { //see comments at parseNetworkResponse()
if(volleyError != null && volleyError.networkResponse != null)
Log.e("request", "status code: " + volleyError.networkResponse.statusCode + "\n" + volleyError.networkResponse.data.toString());
else
Log.e("request", "Request returned null response.");
return super.parseNetworkError(volleyError);
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String,String> headers = new HashMap<String, String>();
// Add basic auth with token
String credentials = Settings.GetToken(getApplicationContext()) + ":";
String auth = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
headers.put("Authorization", auth);
return headers;
}
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("event", Events.eventInfos.get(eventIndex)._id);
params.put("user", UserInfo.current._id);//XXX check user exists
return params;
}
/*
@Override
public byte[] getBody() throws AuthFailureError {
String body = "event:" + Events.eventInfos.get(eventIndex)._id + "\nuser:" + UserInfo.current._id;
byte[] bytes = new byte[0];
try {
bytes = body.getBytes("UTF-8");
Log.e("request", "body: " + new String(bytes, "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return bytes;
}*/
};
Requests.SendRequest(request, getApplicationContext());
}
public void ScrollToTop (View view) {
scrollView.fullScroll(ScrollView.FOCUS_UP);
}
......
package ch.amiv.android_app;
import android.media.Image;
import android.net.ParseException;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class EventInfo {
public String _id;
public String title;
......@@ -13,10 +25,34 @@ public class EventInfo {
public String spots;
public String allow_email_signup;
public String show_website;
public String time_register_end;
public String _created;
public String posterUrl;
//Dates
public Date time_register_start;
public Date time_register_end;
public Date time_created;
public Date time_updated;
private ArrayList<String[]> infos = new ArrayList<>();
//Signup related
public boolean accepted;
public boolean confirmed;
public String signup_id = "";
/**
* Choose the key value pairs to be displayed in the event info section when viewing the event in detail
* @return
*/
public ArrayList<String[]> GetInfos(){
DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy HH:mm");
infos.add(new String[]{"Register Start", dateFormat.format(time_register_start)});
infos.add(new String[]{"Register End", dateFormat.format(time_register_end)});
infos.add(new String[]{"Available Places", spots});
return infos;
}
public EventInfo(JSONObject json)
{
_id = json.optString("_id");
......@@ -26,12 +62,34 @@ public class EventInfo {
spots = json.optString("spots");
allow_email_signup = json.optString("allow_email_signup");
show_website = json.optString("show_website");
time_register_end = json.optString("time_register_end");
_created = json.optString("_created");
String register_start = json.optString("time_register_start");
String register_end = json.optString("time_register_end");
String _created = json.optString("_created");
String _updated = json.optString("_updated");
try {
posterUrl = json.getJSONObject("img_poster").optString("file");
if(json.has("img_poster"))
posterUrl = json.getJSONObject("img_poster").optString("file");
else
posterUrl = "";
} catch (JSONException e) {
e.printStackTrace();
}
//convert dates
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
try {
time_register_start = format.parse(register_start);
time_register_end = format.parse(register_end);
time_created = format.parse(_created);
time_updated = format.parse(_updated);
} catch (java.text.ParseException e) {
e.printStackTrace();
}
}
public void AddSignup(JSONObject json) {
accepted = json.optBoolean("accepted", false);
confirmed = json.optBoolean("confirmed", false);
signup_id = json.optString("_id", "");
}
}
package ch.amiv.android_app;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.support.design.widget.Snackbar;
import android.content.res.Resources;
import android.graphics.ColorFilter;
import android.os.AsyncTask;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
......@@ -25,6 +29,7 @@ public class EventsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
TextView titleField;
TextView catchphraseField;
TextView placesField;
ImageView statusImage;
View.OnClickListener clickListener;
public EventInfoHolder(View view) {
......@@ -32,6 +37,7 @@ public class EventsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
titleField = view.findViewById(R.id.titleField);
catchphraseField = view.findViewById(R.id.infoField);
placesField = view.findViewById(R.id.places_left);
statusImage = view.findViewById(R.id.signupStatus);
}
}
......@@ -54,7 +60,8 @@ public class EventsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
}
public EventsListAdapter(Activity activity_) {
headerList.add("All Events");
headerList.add("New Events");
headerList.add("Attended Events");
activity = activity_;
}
......@@ -92,7 +99,7 @@ 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)
if(position == 0 || position == Events.eventInfos.size() + 1)
return 0; //header
if(position < Events.eventInfos.size() +1)
return 2; //events
......@@ -120,14 +127,36 @@ public class EventsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
final EventInfo e = Events.eventInfos.get(eventIndex);
eventInfoHolder.titleField.setText(e.title);
eventInfoHolder.catchphraseField.setText(e.catchphrase);
eventInfoHolder.placesField.setText(e.spots);
eventInfoHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
StartEventDetailActivity(eventIndex);
}
});
if(e.accepted && e.confirmed){ //change status of event depending on signup state
eventInfoHolder.statusImage.setVisibility(View.VISIBLE);
eventInfoHolder.placesField.setVisibility(View.GONE);
eventInfoHolder.statusImage.setImageDrawable(Resources.getSystem().getDrawable(R.drawable.ic_check, activity.getTheme()));
eventInfoHolder.statusImage.setColorFilter(Resources.getSystem().getColor(R.color.colorYellow, activity.getTheme()));
}
else if (e.accepted) {
eventInfoHolder.statusImage.setVisibility(View.VISIBLE);
eventInfoHolder.placesField.setVisibility(View.GONE);
eventInfoHolder.statusImage.setImageDrawable(Resources.getSystem().getDrawable(R.drawable.ic_pending, activity.getTheme()));
eventInfoHolder.statusImage.setColorFilter(Resources.getSystem().getColor(R.color.colorGreen, activity.getTheme()));
}
else {
eventInfoHolder.statusImage.setVisibility(View.GONE);
eventInfoHolder.placesField.setVisibility(View.VISIBLE);
eventInfoHolder.placesField.setText(e.spots);
}
break;
/*XXXXX Change color of status image, Test status showing correctly for event, whether we are signed up or not, pending. Hide register button once registered, show registered.
Test registering to event
Fix event image loading from api
Sort events upcoming and attended events, hide unattended old eventsa
return to main activity once registered??*/
}
}
......@@ -146,6 +175,8 @@ public class EventsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
{
if(position == 0)
return 0;
if(position == Events.eventInfos.size() +1)
return 1;
Log.e("recyclerView", "Could not determine header position within list, at position: " + position);
return 0;
......
......@@ -102,6 +102,14 @@ public class ListFragment extends Fragment {
return v;
}
@Override
public void onResume() {
super.onResume();
if(recylcerAdaper != null)
recylcerAdaper.notifyDataSetChanged();
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
......
......@@ -26,6 +26,11 @@ public class LoginActivity extends AppCompatActivity {
webView = findViewById(R.id.WebView);
Intent intent = getIntent();
String loginCause = intent.getStringExtra("cause");
if(loginCause != null && loginCause.equals("register_event"))
Snackbar.make(webView, "Need to be logged in to register", Snackbar.LENGTH_LONG).show();
webView.loadUrl(GenerateOAuthUrl());
webView.setWebViewClient(new WebViewClient() {
@Override
......
......@@ -2,6 +2,8 @@ package ch.amiv.android_app;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.BottomNavigationView;
......@@ -94,7 +96,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
InitialisePageView();
new Settings(getApplicationContext());
if(Settings.IsLoggedIn() && UserInfo.current == null)
if(Settings.IsLoggedIn(getApplicationContext()) && UserInfo.current == null)
FetchUserData();
else
SetLoginUIDirty();
......@@ -142,7 +144,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
// If we are returning from the login activity and have had a successfuly login, refresh the user info and login UI
Intent intent = getIntent();
boolean refreshLogin = intent.getBooleanExtra("login_sucess", false);
if(refreshLogin && Settings.IsLoggedIn()){
if(refreshLogin && Settings.IsLoggedIn(getApplicationContext())){
FetchUserData();
}
}
......@@ -154,7 +156,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
*/
private void FetchUserData()
{
if(!Settings.IsLoggedIn())
if(!Settings.IsLoggedIn(getApplicationContext()))
return;
//Do request Token->User
......@@ -229,7 +231,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
*/
public void SetLoginUIDirty ()
{
if(Settings.IsLoggedIn()) {
if(Settings.IsLoggedIn(getApplicationContext())) {
if(UserInfo.current == null)
FetchUserData();
else {
......@@ -263,16 +265,19 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
Log.e("request", "status Code: " + response.statusCode);
try {
JSONArray eventArrayJson = new JSONObject(new String(response.data)).getJSONArray("_items");
Events.UpdateEventInfos(eventArrayJson);
final JSONArray eventArrayJson = new JSONObject(new String(response.data)).getJSONArray("_items");
//Update UI in nav drawer
drawerNavigation.post(new Runnable() { //Run updating UI on UI thread
//Update events on main thread
Handler mainHandler = new Handler(getApplicationContext().getMainLooper());
Runnable myRunnable = new Runnable() {
@Override
public void run() {
Events.UpdateEventInfos(eventArrayJson);
SetEventUIDirty();
pagerAdapter.currentFragment.RefreshList();
}});
}
};
mainHandler.post(myRunnable);
Log.e("request", eventArrayJson.toString());
} catch (JSONException e) {
......@@ -296,14 +301,14 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
if(Settings.IsLoggedIn()) {
Map<String,String> headers = new HashMap<String, String>();
if(Settings.IsLoggedIn(getApplicationContext())) {
Map<String,String> headers = new HashMap<String, String>();
String credentials = Settings.GetToken(getApplicationContext()) + ":";
String auth = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
headers.put("Authorization", auth);
return headers;
return headers;
}
return super.getHeaders();
......@@ -379,7 +384,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
int id = item.getItemId();
if (id == R.id.nav_login) {
if(Settings.IsLoggedIn())
if(Settings.IsLoggedIn(getApplicationContext()))
LogoutUser();
else
StartLoginActivity();
......
......@@ -151,7 +151,7 @@ public final class Requests {
//==Adding the content==
@Override
protected Map<String, String> getParams() {
if(Settings.IsLoggedIn())
if(Settings.IsLoggedIn(context))
params.put("token", Settings.GetToken(context));
return params;
}
......
package ch.amiv.android_app;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.media.session.MediaSession;
......@@ -82,7 +83,8 @@ public class Settings {
* Note: will only check if a token exists. This token may have expired but not have been refreshed/deleted.
* @return True if the user is logged into the api and has an access token.
*/
public static boolean IsLoggedIn(){
public static boolean IsLoggedIn(Context context){
CheckInitSharedPrefs(context);
String t = sharedPrefs.getString(apiTokenKey, "");
return !t.isEmpty();
}
......
......@@ -191,7 +191,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
// updated to reflect the new value, per the Android Design
// guidelines.
bindPreferenceSummaryToValue(findPreference("example_text"));
bindPreferenceSummaryToValue(findPreference("example_list"));
bindPreferenceSummaryToValue(findPreference("lang_list"));
}
@Override
......
......@@ -6,6 +6,7 @@ import org.json.JSONObject;
public class UserInfo {
public static UserInfo current;
public String _id = "";
public String firstname = "";
public String lastname = "";
public String nethz = "";
......@@ -16,6 +17,8 @@ public class UserInfo {
public UserInfo (JSONObject json)
{
try {
if (json.has("_id"))
_id = json.getString("_id");
if (json.has("firstname"))
firstname = json.getString("firstname");
if (json.has(</