Commit 1f6c36e4 authored by Roger Barton's avatar Roger Barton
Browse files

Added event detail page, still in progress

parent 102785c9
......@@ -19,3 +19,5 @@ captures
\.idea/caches/build_file_checksums\.ser
\.idea/codeStyles/Project\.xml
\.idea/
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WizardSettings">
<option name="children">
<map>
<entry key="vectorWizard">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="vectorAssetStep">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="clipartAsset">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="url" value="jar:file:/opt/android-studio/plugins/android/lib/android.jar!/images/material_design_icons/action/ic_account_circle_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
<option name="values">
<map>
<entry key="outputName" value="ic_menu_account" />
<entry key="sourceFile" value="$USER_HOME$" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="5">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/amiv-android-app.iml" filepath="$PROJECT_DIR$/amiv-android-app.iml" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
......@@ -33,6 +33,7 @@
android:theme="@style/AppThemeLight">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
......@@ -43,6 +44,12 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="ch.amiv.android_app.MainActivity" />
</activity>
<activity android:name=".EventDetailActivity"
android:theme="@style/AppThemeLight">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="ch.amiv.android_app.MainActivity" />
</activity>
</application>
</manifest>
\ No newline at end of file
package ch.amiv.android_app;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.MaskFilter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageView;
import android.widget.ScrollView;
import android.widget.TextView;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageRequest;
import java.net.URL;
public class EventDetailActivity extends AppCompatActivity {
private int eventIndex = 0;
ImageView posterImage;
View posterMask;
ScrollView scrollView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_detail);
GetIntentData();
InitUI();
}
@Override
protected void onResume() {
super.onResume();
GetIntentData();
}
private void GetIntentData (){
Intent intent = getIntent();
eventIndex = intent.getIntExtra("eventIndex", 0);
}
private void InitUI (){
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
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()) {
//generate URL to image
StringBuilder posterUrl = new StringBuilder();
posterUrl.append(Events.eventInfos.get(eventIndex).posterUrl);
if(posterUrl.charAt(0) == '/')
posterUrl.deleteCharAt(0);
posterUrl.insert(0, Settings.API_URL);
Log.e("request", "image url: " + posterUrl.toString());
ImageRequest posterRequest = new ImageRequest(posterUrl.toString(),
new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap bitmap) {
posterImage.setImageBitmap(bitmap);
//Will adjust the empty space/mask at the top of the scrollview so we can see the whole image
posterImage.post(new Runnable() {
@Override
public void run() {
ViewGroup.LayoutParams layoutParams = posterMask.getLayoutParams();
layoutParams.height = posterImage.getHeight();
posterMask.setLayoutParams(layoutParams);
findViewById(R.id.posterBg).setLayoutParams(layoutParams);
}
});
}
}, 0, 0, null, null,
new Response.ErrorListener() {
public void onErrorResponse(VolleyError error) {
posterImage.setImageResource(R.drawable.ic_error_white);
}
});
Requests.SendRequest(posterRequest, getApplicationContext());
}
}
public void ScrollToTop (View view) {
scrollView.fullScroll(ScrollView.FOCUS_UP);
}
}
package ch.amiv.android_app;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageRequest;
/**
* A simple {@link Fragment} subclass.
* Activities that contain this fragment must implement the
* {@link OnEventDetailFragmentListener} interface
* to handle interaction events.
* Use the {@link EventDetailFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class EventDetailFragment extends Fragment {
private static final String eventIndex_key = "eventIndex";
private int eventIndex;
private OnEventDetailFragmentListener mListener;
public EventDetailFragment() {}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param eventIndex_ The index in the EventInfos array of the event to display
* @return A new instance of fragment EventDetailFragment.
*/
public static EventDetailFragment newInstance(int eventIndex_) {
EventDetailFragment fragment = new EventDetailFragment();
Bundle args = new Bundle();
args.putInt(eventIndex_key, eventIndex_);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null)
eventIndex = getArguments().getInt(eventIndex_key);
else
eventIndex = 0;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
TextView title = getView().findViewById(R.id.eventTitle);
TextView content = getView().findViewById(R.id.eventDetail);
final ImageView poster = getView().findViewById(R.id.eventPoster);
title.setText(Events.eventInfos.get(eventIndex).title);
content.setText(Events.eventInfos.get(eventIndex).description);
String posterUrl = "https://www.amiv.ethz.ch/sites/all/themes/amiv15/logo.png";
ImageRequest posterRequest = new ImageRequest(posterUrl,
new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap bitmap) {
poster.setImageBitmap(bitmap);
}
}, 0, 0, ImageView.ScaleType.CENTER, Bitmap.Config.RGB_565,
new Response.ErrorListener() {
public void onErrorResponse(VolleyError error) {
poster.setImageResource(R.drawable.ic_error_white);
}
});
Requests.SendRequest(posterRequest, getContext());
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_event_detail, container, false);
}
//region Callbacks for Parent Activity
/**
* Call this when we have registered for the event in the fragment to notify the parent activity
*/
public void onRegisterEvent() {
if (mListener != null) {
mListener.onRegisteredForEvent(eventIndex);
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
//Add the parent activity as a listener for our callback
if (context instanceof OnEventDetailFragmentListener)
mListener = (OnEventDetailFragmentListener) context;
else
throw new RuntimeException(context.toString() + " must implement OnEventDetailFragmentListener");
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnEventDetailFragmentListener {
// TODO: Update argument type and name
void onRegisteredForEvent(int eventIndex);
}
//endregion
}
package ch.amiv.android_app;
import android.media.Image;
import org.json.JSONException;
import org.json.JSONObject;
public class EventInfo {
public String _id;
public String title;
public String description;
public String catchphrase;
public String spots;
public String allow_email_signup;
public String show_website;
public String time_register_end;
public String _created;
public String posterUrl;
public EventInfo(JSONObject json)
{
_id = json.optString("_id");
title = json.optString("title_en");
description = json.optString("description_en");
catchphrase = json.optString("catchphrase_en");
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");
try {
posterUrl = json.getJSONObject("img_poster").optString("file");
} catch (JSONException e) {
e.printStackTrace();
}
}
}
package ch.amiv.android_app;
import android.support.annotation.NonNull;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import java.util.ArrayList;
import java.util.List;
public final class Events {
public static List<EventInfo> eventInfos = new ArrayList<EventInfo>();
/**
* Update the event infos with the data received from the api
* @param json json array of the events.
*/
public static void UpdateEventInfos(JSONArray json)
{
eventInfos.clear();
for (int i = 0; i < json.length(); i++)
{
try {
eventInfos.add(new EventInfo(json.getJSONObject(i)));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
package ch.amiv.android_app;
import android.app.Activity;
import android.content.Intent;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class EventsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<String> headerList = new ArrayList<String>();
private Activity activity;
/**
* 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 static class EventInfoHolder extends RecyclerView.ViewHolder {
TextView titleField;
TextView catchphraseField;
TextView placesField;
View.OnClickListener clickListener;
public EventInfoHolder(View view) {
super(view);
titleField = view.findViewById(R.id.titleField);
catchphraseField = view.findViewById(R.id.infoField);
placesField = view.findViewById(R.id.places_left);
}
}
public static class HeaderHolder extends RecyclerView.ViewHolder {
TextView nameField;
public HeaderHolder(View view) {
super(view);
nameField = view.findViewById(R.id.titleField);
}
}
public static class SpaceHolder extends RecyclerView.ViewHolder {
View space;
public SpaceHolder(View view) {
super(view);
space = view.findViewById(R.id.space);
}
}
public EventsListAdapter(Activity activity_) {
headerList.add("All Events");
activity = activity_;
}
/**
* This is used when creating a new UI list item. Depending on the type we use a different layout xml
*/
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = null;
RecyclerView.ViewHolder holder = null;
switch (viewType)
{
case 0: //header
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_header, parent, false);
holder = new HeaderHolder(view);
break;
case 1: //space
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_space, parent, false);
holder = new SpaceHolder(view);
break;
case 2: //event
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_event, parent, false);
holder = new EventInfoHolder(view);
break;
}
if(view == null)
Log.e("recyclerView", "Unhandled viewType found, type: " + viewType);
return holder;
}
/**
* Here we map the position in the whole list to the item type, be careful with indexing and offsets
*/
@Override
public int getItemViewType(int position) { //Note stat and event info use the same layout, but types are different
if(position == 0)
return 0; //header
if(position < Events.eventInfos.size() +1)
return 2; //events
else
return 1; //Space
}
/**
* 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
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (holder.getItemViewType())
{
case 0: //header
HeaderHolder headerHolder = (HeaderHolder)holder;
headerHolder.nameField.setText(headerList.get(GetHeaderIndex(holder.getAdapterPosition())));
break;
case 1: //space
SpaceHolder spaceHolder =