diff --git a/app/build.gradle b/app/build.gradle index 4a4812ea..feef5d71 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -72,6 +72,7 @@ dependencies { implementation 'com.google.android.material:material:1.0.0' implementation 'com.google.firebase:firebase-core:16.0.6' implementation 'com.google.firebase:firebase-messaging:17.3.4' + implementation 'com.google.firebase:firebase-firestore:18.1.0' implementation 'com.crashlytics.sdk.android:crashlytics:2.9.8' implementation 'com.squareup.okhttp3:okhttp:3.12.0' implementation 'com.squareup.picasso:picasso:2.5.2' @@ -89,6 +90,7 @@ dependencies { implementation 'ru.noties:markwon:2.0.0' implementation 'net.gotev:uploadservice:3.4.2' implementation 'net.gotev:uploadservice-okhttp:3.4.2' + implementation 'com.github.curioustechizen.android-ago:library:1.4.0' implementation 'com.itkacher.okhttpprofiler:okhttpprofiler:1.0.4' //Plugin: https://plugins.jetbrains.com/plugin/11249-okhttp-profiler } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/MainActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/MainActivity.java index af1a4cf2..9ec5aa28 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/MainActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/MainActivity.java @@ -31,6 +31,7 @@ import gr.thmmy.mthmmy.activities.settings.SettingsActivity; import gr.thmmy.mthmmy.activities.topic.TopicActivity; import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.model.Board; +import gr.thmmy.mthmmy.model.RecentItem; import gr.thmmy.mthmmy.model.ThmmyPage; import gr.thmmy.mthmmy.model.TopicSummary; import timber.log.Timber; @@ -144,10 +145,12 @@ public class MainActivity extends BaseActivity implements RecentFragment.RecentF } @Override - public void onRecentFragmentInteraction(TopicSummary topicSummary) { + public void onRecentFragmentInteraction(RecentItem recentItem) { Intent i = new Intent(MainActivity.this, TopicActivity.class); - i.putExtra(BUNDLE_TOPIC_URL, topicSummary.getTopicUrl()); - i.putExtra(BUNDLE_TOPIC_TITLE, topicSummary.getSubject()); + //TODO: Improve this shit with a thmmy url class in model probably + i.putExtra(BUNDLE_TOPIC_URL, "https://www.thmmy.gr/smf/index.php?topic=" + + recentItem.getTopicId() + ".msg" + recentItem.getPostId() + ";topicseen#new"); + i.putExtra(BUNDLE_TOPIC_TITLE, recentItem.getTopicTitle()); i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); startActivity(i); } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentAdapter.java index 31871069..3350c740 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentAdapter.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentAdapter.java @@ -6,12 +6,15 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import com.github.curioustechizen.ago.RelativeTimeTextView; + import java.util.List; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.base.BaseFragment; +import gr.thmmy.mthmmy.model.RecentItem; import gr.thmmy.mthmmy.model.TopicSummary; @@ -21,12 +24,13 @@ import gr.thmmy.mthmmy.model.TopicSummary; */ class RecentAdapter extends RecyclerView.Adapter { private final Context context; - private final List recentList; + private final List recentItems; private final RecentFragment.RecentFragmentInteractionListener mListener; - RecentAdapter(Context context, @NonNull List topicSummaryList, BaseFragment.FragmentInteractionListener listener) { + RecentAdapter(Context context, @NonNull List recentItems, BaseFragment.FragmentInteractionListener listener) { this.context = context; - this.recentList = topicSummaryList; + + this.recentItems = recentItems; mListener = (RecentFragment.RecentFragmentInteractionListener) listener; } @@ -41,38 +45,33 @@ class RecentAdapter extends RecyclerView.Adapter { @Override public void onBindViewHolder(final ViewHolder holder, final int position) { - holder.mTitleView.setText(recentList.get(position).getSubject()); - holder.mDateTimeView.setText(recentList.get(position).getDateTimeModified()); - holder.mUserView.setText(recentList.get(position).getLastUser()); - - holder.topic = recentList.get(position); + RecentItem recentItem = recentItems.get(position); + holder.mTitleView.setText(recentItem.getTopicTitle()); + holder.mDateTimeView.setReferenceTime(recentItem.getTimestamp()); + holder.mUserView.setText(recentItem.getPoster()); - holder.mView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { + holder.mView.setOnClickListener(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.onRecentFragmentInteraction(holder.topic); //? - - } + if (null != mListener) { + // Notify the active callbacks interface (the activity, if the + // fragment is attached to one) that an item has been selected. + mListener.onRecentFragmentInteraction(recentItems.get(holder.getAdapterPosition())); //? } + }); } @Override public int getItemCount() { - return recentList.size(); + return recentItems.size(); } class ViewHolder extends RecyclerView.ViewHolder { final View mView; final TextView mTitleView; final TextView mUserView; - final TextView mDateTimeView; - public TopicSummary topic; + final RelativeTimeTextView mDateTimeView; ViewHolder(View view) { super(view); diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java index 6c194c0e..78483887 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java @@ -1,6 +1,5 @@ package gr.thmmy.mthmmy.activities.main.recent; -import android.os.AsyncTask; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -9,27 +8,22 @@ import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.Toast; -import org.jsoup.nodes.Document; -import org.jsoup.select.Elements; +import com.google.firebase.firestore.DocumentReference; +import com.google.firebase.firestore.DocumentSnapshot; +import com.google.firebase.firestore.FirebaseFirestore; +import com.google.firebase.firestore.ListenerRegistration; import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.HashMap; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; 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.model.RecentItem; import gr.thmmy.mthmmy.utils.CustomRecyclerView; -import gr.thmmy.mthmmy.utils.NetworkResultCodes; -import gr.thmmy.mthmmy.utils.parsing.NewParseTask; -import gr.thmmy.mthmmy.utils.parsing.ParseException; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; -import okhttp3.Response; import timber.log.Timber; @@ -46,15 +40,16 @@ public class RecentFragment extends BaseFragment { // Fragment initialization parameters, e.g. ARG_SECTION_NUMBER private MaterialProgressBar progressBar; - private SwipeRefreshLayout swipeRefreshLayout; private RecentAdapter recentAdapter; - private List topicSummaries; + private ArrayList recentItems = new ArrayList<>(); - private RecentTask recentTask; + private ListenerRegistration recentSnapshotListener; // Required empty public constructor - public RecentFragment() {} + public RecentFragment() { + } + /** * Use ONLY this factory method to create a new instance of @@ -74,20 +69,34 @@ public class RecentFragment extends BaseFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - topicSummaries = new ArrayList<>(); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - if (topicSummaries.isEmpty()) { - recentTask = new RecentTask(this::onRecentTaskStarted, this::onRecentTaskFinished); - recentTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, SessionManager.indexUrl.toString()); - } - Timber.d("onActivityCreated"); + DocumentReference docRef = FirebaseFirestore.getInstance() + .collection("recent_posts") + .document("recent"); + docRef.get().addOnCompleteListener(task -> { + progressBar.setVisibility(ProgressBar.INVISIBLE); + + if (task.isSuccessful()) onNewDocumentSnapshot(task.getResult()); + else Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show(); + }); + recentSnapshotListener = docRef.addSnapshotListener((documentSnapshot, e) -> { + Timber.i("New doc"); + onNewDocumentSnapshot(documentSnapshot); + } + ); + progressBar.setVisibility(ProgressBar.VISIBLE); } + @Override + public void onDestroy() { + super.onDestroy(); + recentSnapshotListener.remove(); + } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -98,7 +107,7 @@ public class RecentFragment extends BaseFragment { // Set the adapter if (rootView instanceof RelativeLayout) { progressBar = rootView.findViewById(R.id.progressBar); - recentAdapter = new RecentAdapter(getActivity(), topicSummaries, fragmentInteractionListener); + recentAdapter = new RecentAdapter(getActivity(), recentItems, fragmentInteractionListener); CustomRecyclerView recyclerView = rootView.findViewById(R.id.list); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(recyclerView.getContext()); @@ -107,107 +116,23 @@ public class RecentFragment extends BaseFragment { linearLayoutManager.getOrientation()); recyclerView.addItemDecoration(dividerItemDecoration); recyclerView.setAdapter(recentAdapter); - - swipeRefreshLayout = rootView.findViewById(R.id.swiperefresh); - swipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.primary); - swipeRefreshLayout.setColorSchemeResources(R.color.accent); - swipeRefreshLayout.setOnRefreshListener(() -> { - if (!recentTask.isRunning()) { - recentTask = new RecentTask(this::onRecentTaskStarted, this::onRecentTaskFinished); - recentTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, SessionManager.indexUrl.toString()); - } - } - ); } return rootView; } - @Override - public void onDestroy() { - super.onDestroy(); - if (recentTask.isRunning()) - recentTask.cancel(true); - } - - - public interface RecentFragmentInteractionListener extends FragmentInteractionListener { - void onRecentFragmentInteraction(TopicSummary topicSummary); - } - - private void onRecentTaskStarted() { - progressBar.setVisibility(ProgressBar.VISIBLE); - } - - private void onRecentTaskFinished(int resultCode, ArrayList fetchedRecent) { - if (resultCode == NetworkResultCodes.SUCCESSFUL) { - topicSummaries.clear(); - topicSummaries.addAll(fetchedRecent); - recentAdapter.notifyDataSetChanged(); - } else if (resultCode == NetworkResultCodes.NETWORK_ERROR) { - Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText(getContext(), "Unexpected error," + - " please contact the developers with the details", Toast.LENGTH_LONG).show(); + public void onNewDocumentSnapshot(DocumentSnapshot recentDocument) { + recentItems.clear(); + ArrayList> posts = (ArrayList>) recentDocument.get("posts"); + for (HashMap map : posts) { + RecentItem recentItem = new RecentItem(map); + recentItems.add(recentItem); } - - progressBar.setVisibility(ProgressBar.INVISIBLE); - swipeRefreshLayout.setRefreshing(false); + recentAdapter.notifyDataSetChanged(); } - //---------------------------------------ASYNC TASK----------------------------------- - private class RecentTask extends NewParseTask> { - RecentTask(OnTaskStartedListener onTaskStartedListener, - OnNetworkTaskFinishedListener> onParseTaskFinishedListener) { - super(onTaskStartedListener, onParseTaskFinishedListener); - } - - @Override - protected ArrayList parse(Document document, Response response) throws ParseException { - ArrayList fetchedRecent = new ArrayList<>(); - Elements recent = document.select("#block8 :first-child div"); - if (!recent.isEmpty()) { - for (int i = 0; i < recent.size(); i += 3) { - String link = recent.get(i).child(0).attr("href"); - String title = recent.get(i).child(0).attr("title"); - title = title.trim(); - - String lastUser = recent.get(i + 1).text(); - Pattern pattern = Pattern.compile("\\b (.*)"); - Matcher matcher = pattern.matcher(lastUser); - if (matcher.find()) - lastUser = matcher.group(1); - else - throw new ParseException("Parsing failed (lastUser)"); - - String dateTime = recent.get(i + 2).text(); - pattern = Pattern.compile("\\[(.*)]"); - matcher = pattern.matcher(dateTime); - if (matcher.find()) { - dateTime = matcher.group(1); - if (dateTime.contains(" am") || dateTime.contains(" pm") || - dateTime.contains(" πμ") || dateTime.contains(" μμ")) { - dateTime = dateTime.replaceAll(":[0-5][0-9] ", " "); - } else { - dateTime = dateTime.substring(0, dateTime.lastIndexOf(":")); - } - if (!dateTime.contains(",")) { - dateTime = dateTime.replaceAll(".+? ([0-9])", "$1"); - } - } else - throw new ParseException("Parsing failed (dateTime)"); - - fetchedRecent.add(new TopicSummary(link, title, lastUser, dateTime)); - } - return fetchedRecent; - } - throw new ParseException("Parsing failed"); - } - - @Override - protected int getResultCode(Response response, ArrayList data) { - return NetworkResultCodes.SUCCESSFUL; - } + public interface RecentFragmentInteractionListener extends FragmentInteractionListener { + void onRecentFragmentInteraction(RecentItem topicSummary); } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java b/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java index ce4e4068..55b1a9be 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java +++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java @@ -16,6 +16,7 @@ import com.franmontiel.persistentcookiejar.PersistentCookieJar; import com.franmontiel.persistentcookiejar.cache.SetCookieCache; import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor; import com.google.firebase.analytics.FirebaseAnalytics; +import com.google.firebase.firestore.FirebaseFirestore; import com.itkacher.okhttpprofiler.OkHttpProfilerInterceptor; import com.jakewharton.picasso.OkHttp3Downloader; import com.mikepenz.fontawesome_typeface_library.FontAwesome; diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/RecentItem.java b/app/src/main/java/gr/thmmy/mthmmy/model/RecentItem.java new file mode 100644 index 00000000..667ab826 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/model/RecentItem.java @@ -0,0 +1,53 @@ +package gr.thmmy.mthmmy.model; + +import java.util.Date; +import java.util.Map; + +public class RecentItem { + private int boardId, postId, topicId, posterId; + private String boardTitle, topicTitle, poster; + private long timestamp; + + public RecentItem(Map map) { + this.boardId = ((Long) map.get("boardId")).intValue(); + this.postId = ((Long) map.get("postId")).intValue(); + this.poster = String.valueOf(map.get("poster")); + this.posterId = ((Long) map.get("posterId")).intValue(); + this.topicId = ((Long) map.get("topicId")).intValue(); + this.boardTitle = String.valueOf(map.get("boardTitle")); + this.topicTitle = String.valueOf(map.get("topicTitle")); + this.timestamp = (long)(map.get("timestamp")) * 1000; + } + + public long getTimestamp() { + return timestamp; + } + + public int getBoardId() { + return boardId; + } + + public int getPosterId() { + return posterId; + } + + public int getPostId() { + return postId; + } + + public int getTopicId() { + return topicId; + } + + public String getBoardTitle() { + return boardTitle; + } + + public String getPoster() { + return poster; + } + + public String getTopicTitle() { + return topicTitle; + } +} diff --git a/app/src/main/res/layout/fragment_recent.xml b/app/src/main/res/layout/fragment_recent.xml index 356f96ae..0ed39317 100644 --- a/app/src/main/res/layout/fragment_recent.xml +++ b/app/src/main/res/layout/fragment_recent.xml @@ -1,28 +1,21 @@ - - - - - + android:layout_height="match_parent" + android:background="@color/background" + android:paddingTop="4dp" + android:paddingBottom="4dp" + app:layoutManager="LinearLayoutManager" + tools:context=".activities.main.recent.RecentFragment" + tools:listitem="@layout/fragment_recent_row" /> + app:mpb_progressStyle="horizontal" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_recent_row.xml b/app/src/main/res/layout/fragment_recent_row.xml index 7d136a4e..b816afb6 100644 --- a/app/src/main/res/layout/fragment_recent_row.xml +++ b/app/src/main/res/layout/fragment_recent_row.xml @@ -32,7 +32,7 @@ android:layout_below="@+id/title" android:layout_toEndOf="@+id/dateTime"/> -