Commit e4e617c4 authored by Lennart Bader's avatar Lennart Bader
Browse files

Added News to StartFragment

parent e60003fc
package de.mytfg.apps.vplan.adapters;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.view.View;
import android.widget.TextView;
import de.mytfg.apps.vplan.R;
import de.mytfg.apps.vplan.objects.TfgNewsEntry;
import de.mytfg.apps.vplan.objects.VplanEntry;
import static android.view.View.GONE;
public class NewsEntryHolder extends RecyclerView.ViewHolder {
private TextView title;
private TextView date;
private TextView summary;
private Context context;
private CardView cardView;
public NewsEntryHolder(View view) {
super(view);
context = view.getContext();
cardView = (CardView) view;
title = (TextView) view.findViewById(R.id.news_title);
date = (TextView) view.findViewById(R.id.news_date);
summary = (TextView) view.findViewById(R.id.news_summary);
}
public void update(TfgNewsEntry entry) {
//titleView.setText(baseObject.getName());
title.setText(entry.getTitle());
date.setText(entry.getDateString());
summary.setText(Html.fromHtml(entry.getSummary()));
final String link = entry.getLink();
if (!link.isEmpty()) {
setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(link));
context.startActivity(i);
}
});
} else {
setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
}
}
public void setOnClickListener(CardView.OnClickListener listener) {
cardView.setOnClickListener(listener);
}
public CardView getCardView() {
return cardView;
}
}
package de.mytfg.apps.vplan.adapters;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.UUID;
import de.mytfg.apps.vplan.R;
import de.mytfg.apps.vplan.objects.TfgNewsEntry;
import de.mytfg.apps.vplan.objects.VplanEntry;
public class RecylcerNewsAdapter extends RecyclerView.Adapter<NewsEntryHolder> {
private Context context;
private ArrayList<TfgNewsEntry> elements = new ArrayList<>();
private String unique;
public RecylcerNewsAdapter(Context c) {
this.context = c;
unique = UUID.randomUUID().toString();
}
public void addItem(TfgNewsEntry item) {
elements.add(item);
}
public int getCount() {
return elements.size();
}
@Override
public NewsEntryHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).
inflate(R.layout.cardview_newsentry, null);
NewsEntryHolder newsEntryHolder = new NewsEntryHolder(view);
return newsEntryHolder;
}
@Override
public void onBindViewHolder(final NewsEntryHolder holder, final int position) {
holder.update(elements.get(position));
}
@Override
public int getItemCount() {
return elements.size();
}
public void clear() {
elements.clear();
}
}
......@@ -34,6 +34,7 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
......@@ -348,4 +349,16 @@ public class MyTFGApi {
}
}
}
public static String tsToString(long timestamp) {
timestamp = timestamp * 1000;
try{
DateFormat sdf = new SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN);
Date netDate = (new Date(timestamp));
return sdf.format(netDate);
}
catch(Exception ex){
return "--";
}
}
}
......@@ -4,8 +4,11 @@ import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.widget.TextViewCompat;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.Menu;
......@@ -13,19 +16,25 @@ import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import de.mytfg.apps.vplan.R;
import de.mytfg.apps.vplan.activities.MainActivity;
import de.mytfg.apps.vplan.adapters.RecylcerNewsAdapter;
import de.mytfg.apps.vplan.api.MyTFGApi;
import de.mytfg.apps.vplan.api.SuccessCallback;
import de.mytfg.apps.vplan.objects.TfgNews;
import de.mytfg.apps.vplan.objects.TfgNewsEntry;
import de.mytfg.apps.vplan.objects.User;
import de.mytfg.apps.vplan.objects.Vplan;
import de.mytfg.apps.vplan.toolbar.ToolbarManager;
import de.mytfg.apps.vplan.tools.ItemOffsetDecoration;
public class StartFragment extends Fragment {
private View view;
private RecylcerNewsAdapter adapter;
private RecyclerView recyclerView;
public StartFragment() {
}
......@@ -57,6 +66,8 @@ public class StartFragment extends Fragment {
infotext.setText(Html.fromHtml(text));
}
this.displayNews();
return view;
}
......@@ -76,4 +87,33 @@ public class StartFragment extends Fragment {
i.setData(Uri.parse(url));
startActivity(i);
}
public void displayNews() {
final ProgressBar pb = (ProgressBar) view.findViewById(R.id.news_loading_bar);
recyclerView = (RecyclerView) view.findViewById(R.id.recyclerview_news);
pb.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), 1);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(gridLayoutManager);
recyclerView.addItemDecoration(new ItemOffsetDecoration(getContext(), R.dimen.cardview_spacing));
final TfgNews news = new TfgNews(getContext());
news.load(new SuccessCallback() {
@Override
public void callback(boolean success) {
pb.setVisibility(View.GONE);
if (success) {
adapter = new RecylcerNewsAdapter(getContext());
recyclerView.setAdapter(adapter);
for (TfgNewsEntry entry : news.getEntries()) {
adapter.addItem(entry);
}
adapter.notifyDataSetChanged();
recyclerView.setVisibility(View.VISIBLE);
} else {
((MainActivity)getActivity()).getNavi().snackbar(getString(R.string.api_news_error));
}
}
}, true);
}
}
......@@ -4,6 +4,8 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
......@@ -128,4 +130,13 @@ public class Navigation {
public void showKeyboard() {
((MainActivity) context).getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
}
public void snackbar(String text) {
CoordinatorLayout coordinatorLayout = (CoordinatorLayout) ((MainActivity)context).findViewById(R.id.coordinator_layout);
Snackbar snackbar = Snackbar
.make(coordinatorLayout,
text,
Snackbar.LENGTH_LONG);
snackbar.show();
}
}
package de.mytfg.apps.vplan.objects;
import android.content.Context;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.LinkedList;
import java.util.List;
import de.mytfg.apps.vplan.api.ApiCallback;
import de.mytfg.apps.vplan.api.MyTFGApi;
import de.mytfg.apps.vplan.api.SuccessCallback;
import de.mytfg.apps.vplan.tools.JsonFileManager;
/**
* Represents a set of News from the TFG Webpage
*/
public class TfgNews extends MytfgObject {
private Context context;
private List<TfgNewsEntry> entries = new LinkedList<>();
private boolean loaded = false;
private long timestamp;
// 3 Minutes
private final long timeout = 60 * 60 * 1000; // 60 minutes
public TfgNews(Context context) {
this.context = context;
}
@Override
public void load(SuccessCallback callback) {
this.load(callback, false);
}
public void load(final SuccessCallback callback, boolean clearCache) {
// Try to load from cache
if (!clearCache && loadFromCache()) {
if (upToDate()) {
callback.callback(true);
return;
}
}
loaded = false;
// Otherwise request new data
MyTFGApi api = new MyTFGApi(context);
api.call("api_tfg_news", new ApiCallback() {
@Override
public void callback(JSONObject result, int responseCode) {
if (responseCode == 200) {
loaded = true;
Log.d("NEWS", result.toString());
if (parse(result)) {
saveToCache(result);
callback.callback(true);
} else {
callback.callback(false);
}
} else {
if (responseCode == -1 && loadFromCache()) {
// No internet connection -> use cache even when cache timed out
callback.callback(true);
} else {
callback.callback(false);
}
}
}
});
}
private boolean parse(JSONObject result) {
this.entries = new LinkedList<>();
try {
JSONArray entries = result.getJSONArray("news");
for (int i = 0; i < entries.length(); ++i) {
TfgNewsEntry entry = new TfgNewsEntry();
entry.load(entries.getJSONObject(i));
this.entries.add(entry);
}
timestamp = result.optLong("api_time", System.currentTimeMillis());
loaded = true;
} catch (JSONException ex) {
ex.printStackTrace();
return false;
}
return true;
}
private void saveToCache(JSONObject json) {
try {
json.put("api_time", System.currentTimeMillis());
} catch (JSONException ex) {
ex.printStackTrace();
}
JsonFileManager.write(json, "tfg_news", context);
}
private boolean loadFromCache() {
JSONObject json = JsonFileManager.read("tfg_news", context);
return parse(json);
}
public List<TfgNewsEntry> getEntries() {
return entries;
}
public boolean upToDate() {
return (timestamp + timeout) >= System.currentTimeMillis();
}
}
package de.mytfg.apps.vplan.objects;
import org.json.JSONException;
import org.json.JSONObject;
import de.mytfg.apps.vplan.api.MyTFGApi;
import de.mytfg.apps.vplan.api.SuccessCallback;
/**
* Represents a single News Entry from the RSS Feed of TFG.
*/
public class TfgNewsEntry extends MytfgObject {
private long timestamp;
private String link;
private String title;
private String text;
private String summary;
/**
* Do not use this method. Use the load method and pass a JSON Object.
* @param callback Always called with false
*/
@Override
public void load(SuccessCallback callback) {
callback.callback(false);
}
public boolean load(JSONObject data) {
if (data == null) {
return false;
}
try {
timestamp = data.getLong("ts");
link = data.getString("link");
title = data.getString("title");
text = data.getString("text");
summary = data.getString("summary");
} catch (JSONException ex) {
return false;
}
return true;
}
public String getTitle() {
return title;
}
public String getSummary() {
return summary;
}
public String getDateString() {
return MyTFGApi.tsToString(timestamp);
}
public String getLink() {
return link;
}
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="@color/colorSecondaryLight"
android:layout_height="wrap_content"
android:padding="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorSecondaryLight"
android:padding="10dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_gravity="start"
android:textSize="@dimen/textSmallTitle"
android:id="@+id/news_title" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_gravity="start"
android:textSize="14sp"
android:layout_marginBottom="@dimen/defaultPadding"
android:id="@+id/news_date" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_gravity="start"
android:textSize="@dimen/textDefault"
android:id="@+id/news_summary" />
</LinearLayout>
</android.support.v7.widget.CardView>
\ No newline at end of file
......@@ -57,12 +57,21 @@
android:textSize="@dimen/textTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="NEWS" />
<TextView
android:textSize="@dimen/textDefault"
android:text="@string/home_news" />
<ProgressBar
style="?android:attr/progressBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Display news or other junk here? ;)" />
android:id="@+id/news_loading_bar" />
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/recyclerview_news"
android:visibility="gone">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
</android.support.v7.widget.CardView>
......
......@@ -20,6 +20,7 @@
<string name="api_noauth">Keine Berechtigung</string>
<string name="api_serverfault">Interner Server Fehler</string>
<string name="api_response">Ungültige Server Antwort</string>
<string name="api_news_error">News konnten nicht geladen werden</string>
<!-- LOGIN -->
<string name="login_text">Um diese App und all ihre Funktionen nutzen zu können,
......@@ -36,6 +37,7 @@
<!-- HOME -->
<string name="home_status_title">MyTFG VPlan</string>
<string name="home_news">Aktuelles</string>
<string name="home_status_not_loggedin">
Sie sind derzeit nicht eingeloggt.\nKlicken Sie hier, um Ihre Anmeldung zu verwalten.
</string>
......
......@@ -17,7 +17,8 @@
<!-- HOME -->
<string name="home_status_title">MyTFG VPlan</string>
<string name="home_status_not_loggedin">You are currently not logged in.\nTap here to manager your login.</string>
<string name="home_news">NEWS</string>
<string name="home_status_not_loggedin">You are currently not logged in.\nTo use all features of this App please log in.</string>
<string name="home_status_loggedin">
You are currently logged in as %1$s.\nYour login will expire at %2$s.\nTap here to manage your login.
</string>
......@@ -53,6 +54,7 @@
<string name="api_noauth">No permissions to request the content</string>
<string name="api_serverfault">Internal Server error</string>
<string name="api_response">Invalid Server response</string>
<string name="api_news_error">News could not be loaded</string>
<!-- LOGIN -->
<string name="login_text">To use this App and all its features you have to login first.</string>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment