mirror of https://github.com/ThmmyNoLife/mTHMMY
Ezerous
8 years ago
64 changed files with 1495 additions and 903 deletions
@ -1 +1 @@ |
|||||
1.2.1 |
1.3.0 |
||||
|
After Width: | Height: | Size: 13 KiB |
@ -0,0 +1,147 @@ |
|||||
|
package gr.thmmy.mthmmy.activities.main.unread; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.support.annotation.NonNull; |
||||
|
import android.support.v7.widget.RecyclerView; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
import android.widget.TextView; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
import gr.thmmy.mthmmy.R; |
||||
|
import gr.thmmy.mthmmy.base.BaseFragment; |
||||
|
import gr.thmmy.mthmmy.model.TopicSummary; |
||||
|
|
||||
|
class UnreadAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { |
||||
|
private final Context context; |
||||
|
private final List<TopicSummary> unreadList; |
||||
|
private final UnreadFragment.UnreadFragmentInteractionListener mListener; |
||||
|
private final MarkReadInteractionListener markReadListener; |
||||
|
|
||||
|
private final int VIEW_TYPE_ITEM = 0; |
||||
|
private final int VIEW_TYPE_NADA = 1; |
||||
|
private final int VIEW_TYPE_MARK_READ = 2; |
||||
|
|
||||
|
UnreadAdapter(Context context, @NonNull List<TopicSummary> topicSummaryList, |
||||
|
BaseFragment.FragmentInteractionListener listener, |
||||
|
MarkReadInteractionListener markReadInteractionListener) { |
||||
|
this.context = context; |
||||
|
this.unreadList = topicSummaryList; |
||||
|
mListener = (UnreadFragment.UnreadFragmentInteractionListener) listener; |
||||
|
markReadListener = markReadInteractionListener; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public int getItemViewType(int position) { |
||||
|
if (unreadList.get(position).getDateTimeModified() == null) return VIEW_TYPE_MARK_READ; |
||||
|
return unreadList.get(position).getTopicUrl() == null ? VIEW_TYPE_NADA : VIEW_TYPE_ITEM; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { |
||||
|
if (viewType == VIEW_TYPE_ITEM) { |
||||
|
View view = LayoutInflater.from(parent.getContext()) |
||||
|
.inflate(R.layout.fragment_unread_row, parent, false); |
||||
|
return new ViewHolder(view); |
||||
|
} else if (viewType == VIEW_TYPE_NADA) { |
||||
|
View view = LayoutInflater.from(parent.getContext()) |
||||
|
.inflate(R.layout.fragment_unread_empty_row, parent, false); |
||||
|
return new EmptyViewHolder(view); |
||||
|
} else if (viewType == VIEW_TYPE_MARK_READ) { |
||||
|
View view = LayoutInflater.from(parent.getContext()) |
||||
|
.inflate(R.layout.fragment_unread_mark_read_row, parent, false); |
||||
|
return new MarkReadViewHolder(view); |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { |
||||
|
if (holder instanceof UnreadAdapter.EmptyViewHolder) { |
||||
|
final UnreadAdapter.EmptyViewHolder emptyViewHolder = (UnreadAdapter.EmptyViewHolder) holder; |
||||
|
emptyViewHolder.text.setText(unreadList.get(position).getDateTimeModified()); |
||||
|
} else if (holder instanceof UnreadAdapter.ViewHolder) { |
||||
|
final UnreadAdapter.ViewHolder viewHolder = (UnreadAdapter.ViewHolder) holder; |
||||
|
|
||||
|
viewHolder.mTitleView.setText(unreadList.get(position).getSubject()); |
||||
|
viewHolder.mDateTimeView.setText(unreadList.get(position).getDateTimeModified()); |
||||
|
viewHolder.mUserView.setText(context.getString(R.string.byUser, unreadList.get(position).getLastUser())); |
||||
|
|
||||
|
viewHolder.topic = unreadList.get(position); |
||||
|
|
||||
|
viewHolder.mView.setOnClickListener(new View.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(View v) { |
||||
|
if (null != mListener) { |
||||
|
// Notify the active callbacks interface (the activity, if the
|
||||
|
// fragment is attached to one) that an item has been selected.
|
||||
|
mListener.onUnreadFragmentInteraction(viewHolder.topic); //?
|
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} else if (holder instanceof UnreadAdapter.MarkReadViewHolder) { |
||||
|
final UnreadAdapter.MarkReadViewHolder markReadViewHolder = (UnreadAdapter.MarkReadViewHolder) holder; |
||||
|
markReadViewHolder.text.setText(unreadList.get(position).getSubject()); |
||||
|
markReadViewHolder.topic = unreadList.get(position); |
||||
|
|
||||
|
markReadViewHolder.mView.setOnClickListener(new View.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(View v) { |
||||
|
if (null != mListener) { |
||||
|
// Notify the active callbacks interface (the activity, if the
|
||||
|
// fragment is attached to one) that an item has been selected.
|
||||
|
markReadListener.onMarkReadInteraction(unreadList.get(position).getTopicUrl()); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public int getItemCount() { |
||||
|
return unreadList.size(); |
||||
|
} |
||||
|
|
||||
|
private static class ViewHolder extends RecyclerView.ViewHolder { |
||||
|
final View mView; |
||||
|
final TextView mTitleView; |
||||
|
final TextView mUserView; |
||||
|
final TextView mDateTimeView; |
||||
|
public TopicSummary topic; |
||||
|
|
||||
|
ViewHolder(View view) { |
||||
|
super(view); |
||||
|
mView = view; |
||||
|
mTitleView = (TextView) view.findViewById(R.id.title); |
||||
|
mUserView = (TextView) view.findViewById(R.id.lastUser); |
||||
|
mDateTimeView = (TextView) view.findViewById(R.id.dateTime); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static class EmptyViewHolder extends RecyclerView.ViewHolder { |
||||
|
final TextView text; |
||||
|
|
||||
|
EmptyViewHolder(View view) { |
||||
|
super(view); |
||||
|
text = (TextView) view.findViewById(R.id.text); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static class MarkReadViewHolder extends RecyclerView.ViewHolder { |
||||
|
final View mView; |
||||
|
final TextView text; |
||||
|
public TopicSummary topic; |
||||
|
|
||||
|
MarkReadViewHolder(View view) { |
||||
|
super(view); |
||||
|
mView = view; |
||||
|
text = (TextView) view.findViewById(R.id.mark_read); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public interface MarkReadInteractionListener { |
||||
|
void onMarkReadInteraction(String markReadLinkUrl); |
||||
|
} |
||||
|
} |
@ -0,0 +1,251 @@ |
|||||
|
package gr.thmmy.mthmmy.activities.main.unread; |
||||
|
|
||||
|
import android.os.AsyncTask; |
||||
|
import android.os.Bundle; |
||||
|
import android.support.v4.widget.SwipeRefreshLayout; |
||||
|
import android.support.v7.widget.DividerItemDecoration; |
||||
|
import android.support.v7.widget.LinearLayoutManager; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
import android.widget.ProgressBar; |
||||
|
import android.widget.RelativeLayout; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import org.jsoup.nodes.Document; |
||||
|
import org.jsoup.nodes.Element; |
||||
|
import org.jsoup.select.Elements; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
import gr.thmmy.mthmmy.R; |
||||
|
import gr.thmmy.mthmmy.base.BaseFragment; |
||||
|
import gr.thmmy.mthmmy.model.TopicSummary; |
||||
|
import gr.thmmy.mthmmy.session.SessionManager; |
||||
|
import gr.thmmy.mthmmy.utils.CustomRecyclerView; |
||||
|
import gr.thmmy.mthmmy.utils.ParseTask; |
||||
|
import gr.thmmy.mthmmy.utils.exceptions.ParseException; |
||||
|
import me.zhanghai.android.materialprogressbar.MaterialProgressBar; |
||||
|
import okhttp3.Request; |
||||
|
import timber.log.Timber; |
||||
|
|
||||
|
/** |
||||
|
* A {@link BaseFragment} subclass. |
||||
|
* Activities that contain this fragment must implement the |
||||
|
* {@link UnreadFragment.UnreadFragmentInteractionListener} interface |
||||
|
* to handle interaction events. |
||||
|
* Use the {@link UnreadFragment#newInstance} factory method to |
||||
|
* create an instance of this fragment. |
||||
|
*/ |
||||
|
|
||||
|
public class UnreadFragment extends BaseFragment { |
||||
|
private static final String TAG = "UnreadFragment"; |
||||
|
// Fragment initialization parameters, e.g. ARG_SECTION_NUMBER
|
||||
|
|
||||
|
private MaterialProgressBar progressBar; |
||||
|
private SwipeRefreshLayout swipeRefreshLayout; |
||||
|
private UnreadAdapter unreadAdapter; |
||||
|
|
||||
|
private List<TopicSummary> topicSummaries; |
||||
|
|
||||
|
private UnreadTask unreadTask; |
||||
|
private MarkReadTask markReadTask; |
||||
|
|
||||
|
// Required empty public constructor
|
||||
|
public UnreadFragment() { |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Use ONLY this factory method to create a new instance of |
||||
|
* this fragment using the provided parameters. |
||||
|
* |
||||
|
* @return A new instance of fragment Unread. |
||||
|
*/ |
||||
|
public static UnreadFragment newInstance(int sectionNumber) { |
||||
|
UnreadFragment fragment = new UnreadFragment(); |
||||
|
Bundle args = new Bundle(); |
||||
|
args.putString(ARG_TAG, TAG); |
||||
|
args.putInt(ARG_SECTION_NUMBER, sectionNumber); |
||||
|
fragment.setArguments(args); |
||||
|
return fragment; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onCreate(Bundle savedInstanceState) { |
||||
|
super.onCreate(savedInstanceState); |
||||
|
topicSummaries = new ArrayList<>(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onActivityCreated(Bundle savedInstanceState) { |
||||
|
super.onActivityCreated(savedInstanceState); |
||||
|
if (topicSummaries.isEmpty()) { |
||||
|
unreadTask = new UnreadTask(); |
||||
|
unreadTask.execute(SessionManager.unreadUrl.toString()); |
||||
|
} |
||||
|
markReadTask = new MarkReadTask(); |
||||
|
Timber.d("onActivityCreated"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, |
||||
|
Bundle savedInstanceState) { |
||||
|
// Inflate the layout for this fragment
|
||||
|
final View rootView = inflater.inflate(R.layout.fragment_unread, container, false); |
||||
|
|
||||
|
// Set the adapter
|
||||
|
if (rootView instanceof RelativeLayout) { |
||||
|
progressBar = (MaterialProgressBar) rootView.findViewById(R.id.progressBar); |
||||
|
unreadAdapter = new UnreadAdapter(getActivity(), topicSummaries, |
||||
|
fragmentInteractionListener, new UnreadAdapter.MarkReadInteractionListener() { |
||||
|
@Override |
||||
|
public void onMarkReadInteraction(String markReadLinkUrl) { |
||||
|
if (markReadTask != null && markReadTask.getStatus() != AsyncTask.Status.RUNNING) { |
||||
|
markReadTask = new MarkReadTask(); |
||||
|
markReadTask.execute(markReadLinkUrl); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
CustomRecyclerView recyclerView = (CustomRecyclerView) rootView.findViewById(R.id.list); |
||||
|
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(rootView.findViewById(R.id.list).getContext()); |
||||
|
recyclerView.setLayoutManager(linearLayoutManager); |
||||
|
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), |
||||
|
linearLayoutManager.getOrientation()); |
||||
|
recyclerView.addItemDecoration(dividerItemDecoration); |
||||
|
recyclerView.setAdapter(unreadAdapter); |
||||
|
|
||||
|
swipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swiperefresh); |
||||
|
swipeRefreshLayout.setOnRefreshListener( |
||||
|
new SwipeRefreshLayout.OnRefreshListener() { |
||||
|
@Override |
||||
|
public void onRefresh() { |
||||
|
if (unreadTask != null && unreadTask.getStatus() != AsyncTask.Status.RUNNING) { |
||||
|
unreadTask = new UnreadTask(); |
||||
|
unreadTask.execute(SessionManager.unreadUrl.toString()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return rootView; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onDestroy() { |
||||
|
super.onDestroy(); |
||||
|
if (unreadTask != null && unreadTask.getStatus() != AsyncTask.Status.RUNNING) |
||||
|
unreadTask.cancel(true); |
||||
|
if (markReadTask != null && markReadTask.getStatus() != AsyncTask.Status.RUNNING) |
||||
|
markReadTask.cancel(true); |
||||
|
} |
||||
|
|
||||
|
public interface UnreadFragmentInteractionListener extends FragmentInteractionListener { |
||||
|
void onUnreadFragmentInteraction(TopicSummary topicSummary); |
||||
|
} |
||||
|
|
||||
|
//---------------------------------------ASYNC TASK-----------------------------------
|
||||
|
private class UnreadTask extends ParseTask { |
||||
|
protected void onPreExecute() { |
||||
|
progressBar.setVisibility(ProgressBar.VISIBLE); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void parse(Document document) throws ParseException { |
||||
|
Elements unread = document.select("table.bordercolor[cellspacing=1] tr:not(.titlebg)"); |
||||
|
if (!unread.isEmpty()) { |
||||
|
topicSummaries.clear(); |
||||
|
for (Element row : unread) { |
||||
|
Elements information = row.select("td"); |
||||
|
String link = information.last().select("a").first().attr("href"); |
||||
|
String title = information.get(2).select("a").first().text(); |
||||
|
|
||||
|
Element lastUserAndDate = information.get(6); |
||||
|
String lastUser = lastUserAndDate.select("a").text(); |
||||
|
String dateTime = lastUserAndDate.select("span").html(); |
||||
|
//dateTime = dateTime.replace("<br>", "");
|
||||
|
dateTime = dateTime.substring(0, dateTime.indexOf("<br>")); |
||||
|
dateTime = dateTime.replace("<b>", ""); |
||||
|
dateTime = dateTime.replace("</b>", ""); |
||||
|
|
||||
|
topicSummaries.add(new TopicSummary(link, title, lastUser, dateTime)); |
||||
|
} |
||||
|
Element markRead = document.select("table:not(.bordercolor):not([width])").select("a") |
||||
|
.first(); |
||||
|
if (markRead != null) |
||||
|
topicSummaries.add(new TopicSummary(markRead.attr("href"), markRead.text(), null, |
||||
|
null)); |
||||
|
} else { |
||||
|
topicSummaries.clear(); |
||||
|
String message = document.select("table.bordercolor[cellspacing=1]").first().text(); |
||||
|
if (message.contains("No messages")) { //It's english
|
||||
|
message = "No unread posts!"; |
||||
|
} else { //It's greek
|
||||
|
message = "Δεν υπάρχουν μη διαβασμένα μηνύματα!"; |
||||
|
} |
||||
|
topicSummaries.add(new TopicSummary(null, null, null, message)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void postParsing(ParseTask.ResultCode result) { |
||||
|
if (result == ResultCode.SUCCESS) |
||||
|
unreadAdapter.notifyDataSetChanged(); |
||||
|
|
||||
|
progressBar.setVisibility(ProgressBar.INVISIBLE); |
||||
|
swipeRefreshLayout.setRefreshing(false); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private class MarkReadTask extends AsyncTask<String, Void, Integer> { |
||||
|
private static final int SUCCESS = 0; |
||||
|
private static final int NETWORK_ERROR = 1; |
||||
|
private static final int OTHER_ERROR = 2; |
||||
|
|
||||
|
@Override |
||||
|
protected void onPreExecute() { |
||||
|
progressBar.setVisibility(ProgressBar.VISIBLE); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected Integer doInBackground(String... strings) { |
||||
|
Request request = new Request.Builder() |
||||
|
.url(strings[0]) |
||||
|
.build(); |
||||
|
try { |
||||
|
client.newCall(request).execute(); |
||||
|
return SUCCESS; |
||||
|
} catch (IOException e) { |
||||
|
Timber.i(e, "IO Exception"); |
||||
|
return NETWORK_ERROR; |
||||
|
} catch (Exception e) { |
||||
|
Timber.e(e, "Exception"); |
||||
|
return OTHER_ERROR; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPostExecute(Integer result) { |
||||
|
progressBar.setVisibility(ProgressBar.GONE); |
||||
|
|
||||
|
if (result == NETWORK_ERROR) { |
||||
|
Toast.makeText(getContext() |
||||
|
, "Task was unsuccessful!\n Please check your internet conneciton.", |
||||
|
Toast.LENGTH_LONG).show(); |
||||
|
} else if (result == OTHER_ERROR) { |
||||
|
Toast.makeText(getContext() |
||||
|
, "Fatal error!\n Task aborted...", Toast.LENGTH_LONG).show(); |
||||
|
} else { |
||||
|
if (unreadTask != null && unreadTask.getStatus() != AsyncTask.Status.RUNNING) { |
||||
|
unreadTask = new UnreadTask(); |
||||
|
unreadTask.execute(SessionManager.unreadUrl.toString()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,38 @@ |
|||||
|
package gr.thmmy.mthmmy.utils; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.support.v7.widget.LinearLayoutManager; |
||||
|
import android.support.v7.widget.RecyclerView; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import timber.log.Timber; |
||||
|
|
||||
|
public class CustomLinearLayoutManager extends LinearLayoutManager { |
||||
|
private String pageUrl; |
||||
|
|
||||
|
public CustomLinearLayoutManager(Context context, String pageUrl) { |
||||
|
super(context); |
||||
|
this.pageUrl = pageUrl; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { |
||||
|
try { |
||||
|
super.onLayoutChildren(recycler, state); |
||||
|
} catch (IndexOutOfBoundsException e) { |
||||
|
Timber.wtf(e, "Inconsistency detected: topic_requested = \"" + pageUrl + "\""); |
||||
|
Log.d("CustomLinearLayoutMan", "Inconsistency detected: topic_requested = \"" |
||||
|
+ pageUrl + "\"", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Disable predictive animations. There is a bug in RecyclerView which causes views that |
||||
|
* are being reloaded to pull invalid ViewHolders from the internal recycler stack if the |
||||
|
* adapter size has decreased since the ViewHolder was recycled. |
||||
|
*/ |
||||
|
@Override |
||||
|
public boolean supportsPredictiveItemAnimations() { |
||||
|
return false; |
||||
|
} |
||||
|
} |
@ -0,0 +1,67 @@ |
|||||
|
package gr.thmmy.mthmmy.utils; |
||||
|
|
||||
|
import android.os.AsyncTask; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import org.jsoup.Jsoup; |
||||
|
import org.jsoup.nodes.Document; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
|
||||
|
import gr.thmmy.mthmmy.base.BaseApplication; |
||||
|
import gr.thmmy.mthmmy.utils.exceptions.ParseException; |
||||
|
import okhttp3.Request; |
||||
|
import okhttp3.Response; |
||||
|
import timber.log.Timber; |
||||
|
|
||||
|
/** |
||||
|
* An {@link AsyncTask} class to be inherited for asynchronous parsing. |
||||
|
* Do NOT override doInBackground() and onPostExecute directly. |
||||
|
* Default usage while executing is ParseTask.execute(urlToParse), however feel free to override |
||||
|
* and modify prepareRequest() as needed. |
||||
|
*/ |
||||
|
public abstract class ParseTask extends AsyncTask<String, Void, ParseTask.ResultCode> { |
||||
|
protected enum ResultCode { |
||||
|
SUCCESS, PARSING_ERROR, NETWORK_ERROR, OTHER_ERROR |
||||
|
} |
||||
|
|
||||
|
protected abstract void parse (Document document) throws ParseException; |
||||
|
protected abstract void postParsing (ParseTask.ResultCode result); //ResultCode.NETWORK_ERROR is handled automatically
|
||||
|
|
||||
|
protected Request prepareRequest(String... params) { |
||||
|
return new Request.Builder() |
||||
|
.url(params[0]) |
||||
|
.build(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected ResultCode doInBackground(String... params) { |
||||
|
Request request = prepareRequest(params); |
||||
|
try { |
||||
|
Response response = BaseApplication.getInstance().getClient().newCall(request).execute(); |
||||
|
Document document = Jsoup.parse(response.body().string()); |
||||
|
parse(document); |
||||
|
return ResultCode.SUCCESS; |
||||
|
} catch (ParseException e) { |
||||
|
Timber.tag(this.getClass().getSimpleName()); |
||||
|
Timber.e(e, "Parsing Error"); |
||||
|
return ResultCode.PARSING_ERROR; |
||||
|
} catch (IOException e) { |
||||
|
Timber.tag(this.getClass().getSimpleName()); |
||||
|
Timber.i(e, "Network Error"); |
||||
|
return ResultCode.NETWORK_ERROR; |
||||
|
} catch (Exception e) { |
||||
|
Timber.tag(this.getClass().getSimpleName()); |
||||
|
Timber.e(e, "Other Error"); |
||||
|
return ResultCode.OTHER_ERROR; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPostExecute(ParseTask.ResultCode result) { |
||||
|
if (result == ResultCode.NETWORK_ERROR) |
||||
|
Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Network error", Toast.LENGTH_SHORT).show(); |
||||
|
postParsing(result); |
||||
|
} |
||||
|
} |
||||
|
|
@ -1,14 +0,0 @@ |
|||||
package gr.thmmy.mthmmy.utils.exceptions; |
|
||||
|
|
||||
/** |
|
||||
* UnknownException is thrown upon an error (see Report.java in release), when no other specific |
|
||||
* exception is set, to report to FireBase. |
|
||||
*/ |
|
||||
public class UnknownException extends Exception { |
|
||||
public UnknownException() { |
|
||||
} |
|
||||
|
|
||||
public UnknownException(String message) { |
|
||||
super(message); |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,9 @@ |
|||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:width="24dp" |
||||
|
android:height="24dp" |
||||
|
android:viewportWidth="24.0" |
||||
|
android:viewportHeight="24.0"> |
||||
|
<path |
||||
|
android:fillColor="#ffffff" |
||||
|
android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/> |
||||
|
</vector> |
@ -0,0 +1,9 @@ |
|||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:width="24dp" |
||||
|
android:height="24dp" |
||||
|
android:viewportWidth="24.0" |
||||
|
android:viewportHeight="24.0"> |
||||
|
<path |
||||
|
android:fillColor="#FFFFFFFF" |
||||
|
android:pathData="M10,9V5l-7,7 7,7v-4.1c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z"/> |
||||
|
</vector> |
@ -0,0 +1,34 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<android.support.design.widget.CoordinatorLayout |
||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||
|
android:id="@+id/main_content" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
android:fitsSystemWindows="true" |
||||
|
tools:context=".activities.main.MainActivity"> |
||||
|
|
||||
|
<android.support.design.widget.AppBarLayout |
||||
|
android:id="@+id/appbar" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:theme="@style/ToolbarTheme"> |
||||
|
|
||||
|
<android.support.design.widget.TabLayout |
||||
|
android:id="@+id/tabs" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
app:tabMaxWidth="0dp" |
||||
|
app:tabGravity="fill" |
||||
|
app:tabMode="fixed" |
||||
|
app:tabSelectedTextColor="@color/accent" |
||||
|
app:tabTextColor="@color/white"/> |
||||
|
</android.support.design.widget.AppBarLayout> |
||||
|
|
||||
|
<android.support.v4.view.ViewPager |
||||
|
android:id="@+id/container" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"/> |
||||
|
</android.support.design.widget.CoordinatorLayout> |
@ -0,0 +1,38 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<RelativeLayout |
||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent"> |
||||
|
|
||||
|
<android.support.v4.widget.SwipeRefreshLayout |
||||
|
android:id="@+id/swiperefresh" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent"> |
||||
|
|
||||
|
<gr.thmmy.mthmmy.utils.CustomRecyclerView |
||||
|
android:id="@+id/list" |
||||
|
android:name="gr.thmmy.mthmmy.sections.unread.UnreadFragment" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
android:background="@color/background" |
||||
|
android:paddingBottom="4dp" |
||||
|
android:paddingTop="4dp" |
||||
|
app:layoutManager="LinearLayoutManager" |
||||
|
tools:context=".activities.main.unread.UnreadFragment" |
||||
|
tools:listitem="@layout/fragment_unread_row"/> |
||||
|
</android.support.v4.widget.SwipeRefreshLayout> |
||||
|
|
||||
|
<me.zhanghai.android.materialprogressbar.MaterialProgressBar |
||||
|
android:id="@+id/progressBar" |
||||
|
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal.NoPadding" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="@dimen/progress_bar_height" |
||||
|
android:layout_alignParentTop="true" |
||||
|
android:indeterminate="true" |
||||
|
android:visibility="invisible" |
||||
|
app:mpb_indeterminateTint="@color/accent" |
||||
|
app:mpb_progressStyle="horizontal"/> |
||||
|
|
||||
|
</RelativeLayout> |
@ -0,0 +1,19 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
android:orientation="vertical" |
||||
|
android:paddingBottom="6dp" |
||||
|
android:paddingLeft="10dp" |
||||
|
android:paddingRight="10dp" |
||||
|
android:paddingTop="20dp"> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/text" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
android:textAlignment="center" |
||||
|
android:textColor="@color/accent" |
||||
|
android:textSize="@dimen/big_text" /> |
||||
|
|
||||
|
</LinearLayout> |
@ -0,0 +1,15 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:orientation="vertical" |
||||
|
android:paddingEnd="10dp" |
||||
|
android:paddingTop="7dp"> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/mark_read" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:textAlignment="textEnd" |
||||
|
android:textColor="@color/accent" /> |
||||
|
</LinearLayout> |
@ -0,0 +1,54 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<LinearLayout |
||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:orientation="vertical"> |
||||
|
|
||||
|
<RelativeLayout |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:background="@color/primary_light" |
||||
|
android:foreground="?android:attr/selectableItemBackground" |
||||
|
android:paddingBottom="6dp" |
||||
|
android:paddingLeft="10dp" |
||||
|
android:paddingRight="10dp" |
||||
|
android:paddingTop="6dp"> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/title" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_alignParentStart="true" |
||||
|
android:layout_alignParentTop="true" |
||||
|
android:textAppearance="?attr/textAppearanceListItem" |
||||
|
android:textColor="@color/primary_text"/> |
||||
|
|
||||
|
<Space |
||||
|
android:id="@+id/spacer" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="20dp" |
||||
|
android:layout_alignParentBottom="@+id/title" |
||||
|
android:layout_below="@+id/title" |
||||
|
android:layout_toEndOf="@+id/dateTime"/> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/dateTime" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_alignParentStart="true" |
||||
|
android:layout_below="@+id/spacer" |
||||
|
android:textColor="@color/secondary_text"/> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/lastUser" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_alignBaseline="@+id/dateTime" |
||||
|
android:layout_alignBottom="@+id/dateTime" |
||||
|
android:layout_alignParentEnd="true" |
||||
|
android:textColor="@color/secondary_text" |
||||
|
android:textStyle="italic"/> |
||||
|
</RelativeLayout> |
||||
|
|
||||
|
</LinearLayout> |
@ -1,35 +0,0 @@ |
|||||
[center][size=25pt][b]Introduction[/b][/size][/center] |
|
||||
|
|
||||
Από τη συζήτηση [url=https://www.thmmy.gr/smf/index.php?topic=67629.0]εδώ[/url], ξεκίνησε ένα project με στόχο τη δημιουργία εφαρμογής για Android κινητά που θα συγκεντρώνει και θα κάνει πιο εύκολη τη πρόσβαση σε μερικές από τις βασικές σελίδες και υπηρεσίες που αφορούν τη σχολή και χρησιμοποιούμε καθημερινά. |
|
||||
|
|
||||
Μετά από 2+ μήνες δουλειάς και πάνω από 9000 γραμμές κώδικα (.java, .xml και άλλα), σήμερα (16/1/2017) ανεβάσαμε για πρώτη φορά την εφαρμογή (σε closed alpha phase) στο Google Play Store! |
|
||||
|
|
||||
Προς το παρόν η εφαρμογή υποστηρίζει κάποιες από τις βασικές λειτουργίες του forum. Σταδιακά θα ενσωματώνονται όλο και περισσότερες λειτουργίες, για παράδειγμα ενός συστήματος ειδοποιήσεων για νέες ανακοινώσεις του ethmmy και της σελίδας της γραμματείας, instant chat κ.ά., ανάλογα πάντα με τις ιδέες, τη διάθεση και την ενέργεια όσων θα συμμετέχουν. |
|
||||
|
|
||||
Αυτή τη στιγμή με το project ασχολούμαστε εγώ και ο L, ενώ έχουν βοηθήσει ο iason1907 και ο [url=https://www.thmmy.gr/smf/index.php?topic=67565.msg1163192#msg1163192]nohponex[/url]. |
|
||||
|
|
||||
[hr] |
|
||||
[center][size=25pt][b]Κάλεσμα για contributors[/b][/size] |
|
||||
|
|
||||
[img height=200]https://tctechcrunch2011.files.wordpress.com/2015/04/uncle-sam-we-want-you1-kopie_1.png[/img][/center] |
|
||||
|
|
||||
Αν ενδιαφέρεσαι κι [b]ΕΣΥ[/b] να ασχοληθείς με το project μπορείς να το κάνεις με πολλούς τρόπους: |
|
||||
[list] |
|
||||
|
|
||||
[li] |
|
||||
Να αναφέρεις bugs, να προτείνεις βελτιώσεις και να συμμετέχεις σε συζητήσεις στον [url=https://discord.gg/PVRVjth]Discord server[/url] μας και στον Issue Tracker στο [url=trello.com]Trello[/url] (το link του είναι pinned στο #feedback στο Discord). |
|
||||
[/li] |
|
||||
[li] |
|
||||
Να κατεβάσεις και να δοκιμάσεις την alpha έκδοση της εφαρμογής από [url=https://play.google.com/apps/testing/gr.thmmy.mthmmy]εδώ[/url], [b]αφού [/b]πρώτα μας στείλεις το Gmail που έχεις στο Google Play για να αποκτήσεις πρόσβαση. |
|
||||
[/li] |
|
||||
[li] |
|
||||
Να έρθεις σε άμεση επικοινωνία με την ομάδα μέσω του [url=https://discord.gg/PVRVjth]Discord[/url] ή στέλνοντας email στο thmmynolife@gmail.com. |
|
||||
[/li] |
|
||||
[li]Αν ξέρεις προγραμματισμό μπορείς αρχικά να ζητήσεις πρόσβαση στο repository και να συνεισφέρεις κώδικα με fork και merge requests στους ρυθμούς σου. Χρειάζονται [i]άμεσα[/i] νέοι developers για υλοποιήση καινούργιων χαρακτηριστικών, διόρθωση εντόμων, σύνταξη των javadocs και του documentation γενικότερα, white-box testing, υλοποίηση του backend στο server που στήθηκε πρόσφατα και πολλών άλλων. |
|
||||
[/li][/list] |
|
||||
[hr] |
|
||||
[center][size=25pt][b]Η εφαρμογή[/b][/size][/center] |
|
||||
|
|
||||
[center][url=https://s6.postimg.org/v9mseb7n5/image.png][img width=200]https://s6.postimg.org/v9mseb7n5/image.png[/img][/url] [url=https://s6.postimg.org/3nk0tmoa9/image2.png][img width=200]https://s6.postimg.org/3nk0tmoa9/image2.png[/img][/url] [url=https://s6.postimg.org/i813ogj8x/image3.png][img width=200]https://s6.postimg.org/i813ogj8x/image3.png[/img][/url] [url=https://s6.postimg.org/4to0sfckx/image4.png][img width=200]https://s6.postimg.org/4to0sfckx/image4.png[/img][/url] [url=https://s6.postimg.org/69zjakfht/image5.png][img width=200]https://s6.postimg.org/69zjakfht/image5.png[/img][/url] [url=https://s6.postimg.org/rkx3etxm9/image6.png][img width=200]https://s6.postimg.org/rkx3etxm9/image6.png[/img][/url][/center] |
|
||||
|
|
||||
Αυτή τη στιγμή στην εφαρμογή μπορείς να κάνεις login και logout, να δεις τα "Πρόσφατα", να περιηγηθείς στα boards, topics και user profiles, να κατεβάσεις αρχεία από τα downloads και από συνημμένα σε post. |
|
Loading…
Reference in new issue