Read posts history is available.

This commit is contained in:
Docile-Alligator 2022-07-13 22:05:19 +08:00
parent 14bf98d08a
commit d3f38f318e
19 changed files with 6109 additions and 18 deletions

View File

@ -46,11 +46,11 @@ android {
dependencies {
/** AndroidX **/
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'androidx.biometric:biometric:1.2.0-alpha04'
implementation 'androidx.browser:browser:1.4.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
def lifecycleVersion = "2.3.1"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion"
@ -61,11 +61,12 @@ dependencies {
def pagingVersion = '3.1.0'
implementation "androidx.paging:paging-runtime:$pagingVersion"
implementation "androidx.paging:paging-guava:$pagingVersion"
implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.preference:preference:1.2.0'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
def roomVersion = "2.4.2"
implementation "androidx.room:room-runtime:$roomVersion"
annotationProcessor "androidx.room:room-compiler:$roomVersion"
implementation "androidx.room:room-guava:$roomVersion"
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
implementation 'androidx.work:work-runtime:2.7.1'
implementation 'com.google.android.material:material:1.5.0-alpha05'

View File

@ -34,6 +34,12 @@
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true"
tools:replace="android:label">
<activity
android:name=".activities.HistoryActivity"
android:exported="false"
android:label="@string/history_activity_label"
android:parentActivityName=".activities.MainActivity"
android:theme="@style/AppTheme.Slidable" />
<activity
android:name=".activities.PostPollActivity"
android:label="@string/post_poll_activity_label"
@ -73,10 +79,10 @@
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".activities.WebViewActivity"
android:configChanges="orientation|screenSize"
android:label=""
android:parentActivityName=".activities.MainActivity"
android:theme="@style/AppTheme.NoActionBar"
android:configChanges="orientation|screenSize" />
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".activities.SuicidePreventionActivity"
android:parentActivityName=".activities.MainActivity"
@ -215,24 +221,21 @@
<activity
android:name=".activities.ShareDataResolverActivity"
android:exported="true">
<intent-filter
android:label="@string/submit_post">
<intent-filter android:label="@string/submit_post">
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter
android:label="@string/submit_post">
<intent-filter android:label="@string/submit_post">
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="video/*" />
</intent-filter>
<intent-filter
android:label="@string/submit_post">
<intent-filter android:label="@string/submit_post">
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
@ -278,8 +281,7 @@
<activity
android:name=".activities.LinkResolverActivity"
android:exported="true">
<intent-filter
android:label="@string/handle_link">
<intent-filter android:label="@string/handle_link">
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />

View File

@ -19,6 +19,7 @@ import ml.docilealligator.infinityforreddit.activities.FetchRandomSubredditOrPos
import ml.docilealligator.infinityforreddit.activities.FilteredPostsActivity;
import ml.docilealligator.infinityforreddit.activities.FullMarkdownActivity;
import ml.docilealligator.infinityforreddit.activities.GiveAwardActivity;
import ml.docilealligator.infinityforreddit.activities.HistoryActivity;
import ml.docilealligator.infinityforreddit.activities.InboxActivity;
import ml.docilealligator.infinityforreddit.activities.LinkResolverActivity;
import ml.docilealligator.infinityforreddit.activities.LockScreenActivity;
@ -65,6 +66,7 @@ import ml.docilealligator.infinityforreddit.bottomsheetfragments.AccountChooserB
import ml.docilealligator.infinityforreddit.bottomsheetfragments.FlairBottomSheetFragment;
import ml.docilealligator.infinityforreddit.fragments.CommentsListingFragment;
import ml.docilealligator.infinityforreddit.fragments.FollowedUsersListingFragment;
import ml.docilealligator.infinityforreddit.fragments.HistoryPostFragment;
import ml.docilealligator.infinityforreddit.fragments.InboxFragment;
import ml.docilealligator.infinityforreddit.fragments.MultiRedditListingFragment;
import ml.docilealligator.infinityforreddit.fragments.PostFragment;
@ -300,4 +302,8 @@ public interface AppComponent {
void inject(AccountChooserBottomSheetFragment accountChooserBottomSheetFragment);
void inject(MaterialYouWorker materialYouWorker);
void inject(HistoryPostFragment historyPostFragment);
void inject(HistoryActivity historyActivity);
}

View File

@ -1,6 +1,7 @@
package ml.docilealligator.infinityforreddit;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Color;
import androidx.annotation.NonNull;
@ -37,7 +38,7 @@ import ml.docilealligator.infinityforreddit.user.UserData;
@Database(entities = {Account.class, SubredditData.class, SubscribedSubredditData.class, UserData.class,
SubscribedUserData.class, MultiReddit.class, CustomTheme.class, RecentSearchQuery.class,
ReadPost.class, PostFilter.class, PostFilterUsage.class, AnonymousMultiredditSubreddit.class}, version = 22)
ReadPost.class, PostFilter.class, PostFilterUsage.class, AnonymousMultiredditSubreddit.class}, version = 23)
public abstract class RedditDataRoomDatabase extends RoomDatabase {
private static RedditDataRoomDatabase INSTANCE;
@ -52,7 +53,7 @@ public abstract class RedditDataRoomDatabase extends RoomDatabase {
MIGRATION_9_10, MIGRATION_10_11, MIGRATION_11_12, MIGRATION_12_13,
MIGRATION_13_14, MIGRATION_14_15, MIGRATION_15_16, MIGRATION_16_17,
MIGRATION_17_18, MIGRATION_18_19, MIGRATION_19_20, MIGRATION_20_21,
MIGRATION_21_22)
MIGRATION_21_22, MIGRATION_22_23)
.build();
}
}
@ -365,4 +366,29 @@ public abstract class RedditDataRoomDatabase extends RoomDatabase {
database.execSQL("ALTER TABLE users ADD COLUMN title TEXT");
}
};
private static final Migration MIGRATION_22_23 = new Migration(22, 23) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE read_posts ADD COLUMN time INTEGER DEFAULT 0 NOT NULL");
Cursor cursor = database.query("SELECT * FROM read_posts");
int row = 0;
database.beginTransaction();
try {
while (cursor.moveToNext()) {
int index;
index = cursor.getColumnIndexOrThrow("username");
String username = cursor.getString(index);
index = cursor.getColumnIndexOrThrow("id");
String id = cursor.getString(index);
database.execSQL("UPDATE read_posts SET time = " + row++ + " WHERE username = '" + username + "' AND id = '" + id + "'");
}
database.setTransactionSuccessful();
} finally {
database.endTransaction();
}
}
};
}

View File

@ -0,0 +1,334 @@
package ml.docilealligator.infinityforreddit.activities;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.material.tabs.TabLayout;
import com.r0adkll.slidr.Slidr;
import com.r0adkll.slidr.model.SlidrInterface;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
import ml.docilealligator.infinityforreddit.ActivityToolbarInterface;
import ml.docilealligator.infinityforreddit.Infinity;
import ml.docilealligator.infinityforreddit.R;
import ml.docilealligator.infinityforreddit.bottomsheetfragments.PostLayoutBottomSheetFragment;
import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
import ml.docilealligator.infinityforreddit.events.ChangeNSFWEvent;
import ml.docilealligator.infinityforreddit.events.SwitchAccountEvent;
import ml.docilealligator.infinityforreddit.fragments.CommentsListingFragment;
import ml.docilealligator.infinityforreddit.fragments.HistoryPostFragment;
import ml.docilealligator.infinityforreddit.fragments.PostFragment;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
public class HistoryActivity extends BaseActivity implements ActivityToolbarInterface,
PostLayoutBottomSheetFragment.PostLayoutSelectionCallback {
@BindView(R.id.coordinator_layout_history_activity)
CoordinatorLayout coordinatorLayout;
@BindView(R.id.appbar_layout_history_activity)
AppBarLayout appBarLayout;
@BindView(R.id.collapsing_toolbar_layout_history_activity)
CollapsingToolbarLayout collapsingToolbarLayout;
@BindView(R.id.toolbar_history_activity)
Toolbar toolbar;
@BindView(R.id.tab_layout_tab_layout_history_activity_activity)
TabLayout tabLayout;
@BindView(R.id.view_pager_history_activity)
ViewPager2 viewPager2;
@Inject
@Named("default")
SharedPreferences mSharedPreferences;
@Inject
@Named("post_layout")
SharedPreferences mPostLayoutSharedPreferences;
@Inject
@Named("current_account")
SharedPreferences mCurrentAccountSharedPreferences;
@Inject
CustomThemeWrapper mCustomThemeWrapper;
private FragmentManager fragmentManager;
private SectionsPagerAdapter sectionsPagerAdapter;
private SlidrInterface mSlidrInterface;
private String mAccessToken;
private String mAccountName;
@Override
protected void onCreate(Bundle savedInstanceState) {
((Infinity) getApplication()).getAppComponent().inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_history);
ButterKnife.bind(this);
EventBus.getDefault().register(this);
applyCustomTheme();
if (mSharedPreferences.getBoolean(SharedPreferencesUtils.SWIPE_RIGHT_TO_GO_BACK, true)) {
mSlidrInterface = Slidr.attach(this);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Window window = getWindow();
if (isChangeStatusBarIconColor()) {
addOnOffsetChangedListener(appBarLayout);
}
if (isImmersiveInterface()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.setDecorFitsSystemWindows(false);
} else {
window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
}
adjustToolbar(toolbar);
}
}
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setToolbarGoToTop(toolbar);
fragmentManager = getSupportFragmentManager();
mAccessToken = mCurrentAccountSharedPreferences.getString(SharedPreferencesUtils.ACCESS_TOKEN, null);
mAccountName = mCurrentAccountSharedPreferences.getString(SharedPreferencesUtils.ACCOUNT_NAME, null);
initializeViewPager();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (sectionsPagerAdapter != null) {
return sectionsPagerAdapter.handleKeyDown(keyCode) || super.onKeyDown(keyCode, event);
}
return super.onKeyDown(keyCode, event);
}
@Override
public SharedPreferences getDefaultSharedPreferences() {
return mSharedPreferences;
}
@Override
protected CustomThemeWrapper getCustomThemeWrapper() {
return mCustomThemeWrapper;
}
@Override
protected void applyCustomTheme() {
coordinatorLayout.setBackgroundColor(mCustomThemeWrapper.getBackgroundColor());
applyAppBarLayoutAndCollapsingToolbarLayoutAndToolbarTheme(appBarLayout, collapsingToolbarLayout, toolbar);
applyTabLayoutTheme(tabLayout);
}
private void initializeViewPager() {
sectionsPagerAdapter = new SectionsPagerAdapter(this);
tabLayout.setVisibility(View.GONE);
viewPager2.setAdapter(sectionsPagerAdapter);
viewPager2.setOffscreenPageLimit(2);
//viewPager2.setUserInputEnabled(!mSharedPreferences.getBoolean(SharedPreferencesUtils.DISABLE_SWIPING_BETWEEN_TABS, false));
viewPager2.setUserInputEnabled(false);
/*new TabLayoutMediator(tabLayout, viewPager2, (tab, position) -> {
switch (position) {
case 0:
Utils.setTitleWithCustomFontToTab(typeface, tab, getString(R.string.posts));
break;
}
}).attach();*/
viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
if (position == 0) {
unlockSwipeRightToGoBack();
} else {
lockSwipeRightToGoBack();
}
}
});
fixViewPager2Sensitivity(viewPager2);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.history_activity, menu);
applyMenuItemTheme(menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
int itemId = item.getItemId();
if (itemId == android.R.id.home) {
finish();
return true;
} else if (itemId == R.id.action_refresh_history_activity) {
sectionsPagerAdapter.refresh();
return true;
} else if (itemId == R.id.action_change_post_layout_history_activity) {
PostLayoutBottomSheetFragment postLayoutBottomSheetFragment = new PostLayoutBottomSheetFragment();
postLayoutBottomSheetFragment.show(getSupportFragmentManager(), postLayoutBottomSheetFragment.getTag());
return true;
}
return false;
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
@Subscribe
public void onAccountSwitchEvent(SwitchAccountEvent event) {
finish();
}
@Subscribe
public void onChangeNSFWEvent(ChangeNSFWEvent changeNSFWEvent) {
sectionsPagerAdapter.changeNSFW(changeNSFWEvent.nsfw);
}
@Override
public void onLongPress() {
if (sectionsPagerAdapter != null) {
sectionsPagerAdapter.goBackToTop();
}
}
public void lockSwipeRightToGoBack() {
if (mSlidrInterface != null) {
mSlidrInterface.lock();
}
}
public void unlockSwipeRightToGoBack() {
if (mSlidrInterface != null) {
mSlidrInterface.unlock();
}
}
@Override
public void postLayoutSelected(int postLayout) {
if (sectionsPagerAdapter != null) {
mPostLayoutSharedPreferences.edit().putInt(SharedPreferencesUtils.POST_LAYOUT_USER_POST_BASE + mAccountName, postLayout).apply();
sectionsPagerAdapter.changePostLayout(postLayout);
}
}
private class SectionsPagerAdapter extends FragmentStateAdapter {
SectionsPagerAdapter(FragmentActivity fa) {
super(fa);
}
@NonNull
@Override
public Fragment createFragment(int position) {
if (position == 0) {
HistoryPostFragment fragment = new HistoryPostFragment();
Bundle bundle = new Bundle();
bundle.putInt(HistoryPostFragment.EXTRA_HISTORY_TYPE, HistoryPostFragment.HISTORY_TYPE_READ_POSTS);
bundle.putString(HistoryPostFragment.EXTRA_ACCESS_TOKEN, mAccessToken);
bundle.putString(HistoryPostFragment.EXTRA_ACCOUNT_NAME, mAccountName);
fragment.setArguments(bundle);
return fragment;
} else {
HistoryPostFragment fragment = new HistoryPostFragment();
Bundle bundle = new Bundle();
bundle.putInt(HistoryPostFragment.EXTRA_HISTORY_TYPE, HistoryPostFragment.HISTORY_TYPE_READ_POSTS);
bundle.putString(HistoryPostFragment.EXTRA_ACCESS_TOKEN, mAccessToken);
bundle.putString(HistoryPostFragment.EXTRA_ACCOUNT_NAME, mAccountName);
fragment.setArguments(bundle);
return fragment;
}
}
@Nullable
private Fragment getCurrentFragment() {
if (viewPager2 == null || fragmentManager == null) {
return null;
}
return fragmentManager.findFragmentByTag("f" + viewPager2.getCurrentItem());
}
public boolean handleKeyDown(int keyCode) {
if (viewPager2.getCurrentItem() == 0) {
Fragment fragment = getCurrentFragment();
if (fragment instanceof PostFragment) {
return ((PostFragment) fragment).handleKeyDown(keyCode);
}
}
return false;
}
public void refresh() {
Fragment fragment = getCurrentFragment();
if (fragment instanceof PostFragment) {
((PostFragment) fragment).refresh();
} else if (fragment instanceof CommentsListingFragment) {
((CommentsListingFragment) fragment).refresh();
}
}
public void changeNSFW(boolean nsfw) {
Fragment fragment = getCurrentFragment();
if (fragment instanceof PostFragment) {
((PostFragment) fragment).changeNSFW(nsfw);
}
}
public void changePostLayout(int postLayout) {
Fragment fragment = getCurrentFragment();
if (fragment instanceof PostFragment) {
((PostFragment) fragment).changePostLayout(postLayout);
}
}
public void goBackToTop() {
Fragment fragment = getCurrentFragment();
if (fragment instanceof PostFragment) {
((PostFragment) fragment).goBackToTop();
} else if (fragment instanceof CommentsListingFragment) {
((CommentsListingFragment) fragment).goBackToTop();
}
}
@Override
public int getItemCount() {
return 1;
}
}
}

View File

@ -733,11 +733,13 @@ public class MainActivity extends BaseActivity implements SortTypeSelectionCallb
}
});
navigationWrapper.floatingActionButton.setOnLongClickListener(view -> {
FABMoreOptionsBottomSheetFragment fabMoreOptionsBottomSheetFragment= new FABMoreOptionsBottomSheetFragment();
/*FABMoreOptionsBottomSheetFragment fabMoreOptionsBottomSheetFragment= new FABMoreOptionsBottomSheetFragment();
Bundle bundle = new Bundle();
bundle.putBoolean(FABMoreOptionsBottomSheetFragment.EXTRA_ANONYMOUS_MODE, mAccessToken == null);
fabMoreOptionsBottomSheetFragment.setArguments(bundle);
fabMoreOptionsBottomSheetFragment.show(getSupportFragmentManager(), fabMoreOptionsBottomSheetFragment.getTag());
fabMoreOptionsBottomSheetFragment.show(getSupportFragmentManager(), fabMoreOptionsBottomSheetFragment.getTag());*/
Intent intent = new Intent(this, HistoryActivity.class);
startActivity(intent);
return true;
});
navigationWrapper.floatingActionButton.setVisibility(View.VISIBLE);

View File

@ -337,7 +337,7 @@ public class PostFragment extends Fragment implements FragmentCommunicator {
Resources resources = getResources();
if ((activity instanceof BaseActivity && ((BaseActivity) activity).isImmersiveInterface())) {
if ((activity != null && activity.isImmersiveInterface())) {
mPostRecyclerView.setPadding(0, 0, 0, ((BaseActivity) activity).getNavBarHeight());
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
&& mSharedPreferences.getBoolean(SharedPreferencesUtils.IMMERSIVE_INTERFACE_KEY, true)) {

View File

@ -0,0 +1,126 @@
package ml.docilealligator.infinityforreddit.post;
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
import androidx.paging.ListenableFuturePagingSource;
import androidx.paging.PagingState;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.concurrent.Executor;
import ml.docilealligator.infinityforreddit.RedditDataRoomDatabase;
import ml.docilealligator.infinityforreddit.apis.RedditAPI;
import ml.docilealligator.infinityforreddit.postfilter.PostFilter;
import ml.docilealligator.infinityforreddit.readpost.ReadPost;
import ml.docilealligator.infinityforreddit.utils.APIUtils;
import retrofit2.Call;
import retrofit2.HttpException;
import retrofit2.Response;
import retrofit2.Retrofit;
public class HistoryPostPagingSource extends ListenableFuturePagingSource<String, Post> {
public static final int TYPE_READ_POSTS = 0;
private Retrofit retrofit;
private Executor executor;
private RedditDataRoomDatabase redditDataRoomDatabase;
private String accessToken;
private String accountName;
private SharedPreferences sharedPreferences;
private String username;
private int postType;
private PostFilter postFilter;
public HistoryPostPagingSource(Retrofit retrofit, Executor executor, RedditDataRoomDatabase redditDataRoomDatabase,
String accessToken, String accountName, SharedPreferences sharedPreferences,
String username, int postType, PostFilter postFilter) {
this.retrofit = retrofit;
this.executor = executor;
this.redditDataRoomDatabase = redditDataRoomDatabase;
this.accessToken = accessToken;
this.accountName = accountName;
this.sharedPreferences = sharedPreferences;
this.username = username;
this.postType = postType;
this.postFilter = postFilter;
}
@Nullable
@Override
public String getRefreshKey(@NonNull PagingState<String, Post> pagingState) {
return null;
}
@NonNull
@Override
public ListenableFuture<LoadResult<String, Post>> loadFuture(@NonNull LoadParams<String> loadParams) {
if (postType == TYPE_READ_POSTS) {
return loadHomePosts(loadParams, redditDataRoomDatabase);
} else {
return loadHomePosts(loadParams, redditDataRoomDatabase);
}
}
public LoadResult<String, Post> transformData(List<ReadPost> readPosts) {
StringBuilder ids = new StringBuilder();
long lastItem = 0;
for (ReadPost readPost : readPosts) {
ids.append("t3_").append(readPost.getId()).append(",");
lastItem = readPost.getTime();
}
if (ids.length() > 0) {
ids.deleteCharAt(ids.length() - 1);
}
Call<String> historyPosts;
if (accessToken != null && !accessToken.isEmpty()) {
historyPosts = retrofit.create(RedditAPI.class).getInfoOauth(ids.toString(), APIUtils.getOAuthHeader(accessToken));
} else {
historyPosts = retrofit.create(RedditAPI.class).getInfo(ids.toString());
}
try {
Response<String> response = historyPosts.execute();
if (response.isSuccessful()) {
String responseString = response.body();
LinkedHashSet<Post> newPosts = ParsePost.parsePostsSync(responseString, -1, postFilter, null);
if (newPosts == null) {
return new LoadResult.Error<>(new Exception("Error parsing posts"));
} else {
if (newPosts.size() < 25) {
return new LoadResult.Page<>(new ArrayList<>(newPosts), null, null);
}
return new LoadResult.Page<>(new ArrayList<>(newPosts), null, Long.toString(lastItem));
}
} else {
return new LoadResult.Error<>(new Exception("Response failed"));
}
} catch (IOException e) {
e.printStackTrace();
return new LoadResult.Error<>(new Exception("Response failed"));
}
}
private ListenableFuture<LoadResult<String, Post>> loadHomePosts(@NonNull LoadParams<String> loadParams, RedditDataRoomDatabase redditDataRoomDatabase) {
String before = loadParams.getKey();
ListenableFuture<List<ReadPost>> readPosts = redditDataRoomDatabase.readPostDao().getAllReadPostsListenableFuture(username, Long.parseLong(before == null ? Long.toString(System.currentTimeMillis()) : before));
ListenableFuture<LoadResult<String, Post>> pageFuture = Futures.transform(readPosts, this::transformData, executor);
ListenableFuture<LoadResult<String, Post>> partialLoadResultFuture =
Futures.catching(pageFuture, HttpException.class,
LoadResult.Error::new, executor);
return Futures.catching(partialLoadResultFuture,
IOException.class, LoadResult.Error::new, executor);
}
}

View File

@ -0,0 +1,115 @@
package ml.docilealligator.infinityforreddit.post;
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Transformations;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelKt;
import androidx.lifecycle.ViewModelProvider;
import androidx.paging.Pager;
import androidx.paging.PagingConfig;
import androidx.paging.PagingData;
import androidx.paging.PagingLiveData;
import java.util.concurrent.Executor;
import ml.docilealligator.infinityforreddit.RedditDataRoomDatabase;
import ml.docilealligator.infinityforreddit.postfilter.PostFilter;
import retrofit2.Retrofit;
public class HistoryPostViewModel extends ViewModel {
private Executor executor;
private Retrofit retrofit;
private RedditDataRoomDatabase redditDataRoomDatabase;
private String accessToken;
private String accountName;
private SharedPreferences sharedPreferences;
private int postType;
private PostFilter postFilter;
private LiveData<PagingData<Post>> posts;
private MutableLiveData<PostFilter> postFilterLiveData;
public HistoryPostViewModel(Executor executor, Retrofit retrofit, RedditDataRoomDatabase redditDataRoomDatabase,
String accessToken, String accountName, SharedPreferences sharedPreferences,
int postType, PostFilter postFilter) {
this.executor = executor;
this.retrofit = retrofit;
this.redditDataRoomDatabase = redditDataRoomDatabase;
this.accessToken = accessToken;
this.accountName = accountName;
this.sharedPreferences = sharedPreferences;
this.postType = postType;
this.postFilter = postFilter;
postFilterLiveData = new MutableLiveData<>();
postFilterLiveData.postValue(postFilter);
Pager<String, Post> pager = new Pager<>(new PagingConfig(25, 25, false), this::returnPagingSoruce);
posts = Transformations.switchMap(postFilterLiveData, postFilterValue -> PagingLiveData.cachedIn(PagingLiveData.getLiveData(pager), ViewModelKt.getViewModelScope(this)));
}
public LiveData<PagingData<Post>> getPosts() {
return posts;
}
public HistoryPostPagingSource returnPagingSoruce() {
HistoryPostPagingSource paging3PagingSource;
switch (postType) {
case HistoryPostPagingSource.TYPE_READ_POSTS:
paging3PagingSource = new HistoryPostPagingSource(retrofit, executor, redditDataRoomDatabase, accessToken, accountName,
sharedPreferences, accountName, postType, postFilter);
break;
default:
paging3PagingSource = new HistoryPostPagingSource(retrofit, executor, redditDataRoomDatabase, accessToken, accountName,
sharedPreferences, accountName, postType, postFilter);
break;
}
return paging3PagingSource;
}
public void changePostFilter(PostFilter postFilter) {
postFilterLiveData.postValue(postFilter);
}
public static class Factory extends ViewModelProvider.NewInstanceFactory {
private Executor executor;
private Retrofit retrofit;
private RedditDataRoomDatabase redditDataRoomDatabase;
private String accessToken;
private String accountName;
private SharedPreferences sharedPreferences;
private int postType;
private PostFilter postFilter;
public Factory(Executor executor, Retrofit retrofit, RedditDataRoomDatabase redditDataRoomDatabase,
String accessToken, String accountName, SharedPreferences sharedPreferences, int postType,
PostFilter postFilter) {
this.executor = executor;
this.retrofit = retrofit;
this.redditDataRoomDatabase = redditDataRoomDatabase;
this.accessToken = accessToken;
this.accountName = accountName;
this.sharedPreferences = sharedPreferences;
this.postType = postType;
this.postFilter = postFilter;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (postType == HistoryPostPagingSource.TYPE_READ_POSTS) {
return (T) new HistoryPostViewModel(executor, retrofit, redditDataRoomDatabase, accessToken, accountName, sharedPreferences,
postType, postFilter);
} else {
return (T) new HistoryPostViewModel(executor, retrofit, redditDataRoomDatabase, accessToken, accountName, sharedPreferences,
postType, postFilter);
}
}
}
}

View File

@ -17,6 +17,8 @@ public class PostFilterUsage implements Parcelable {
public static final int USER_TYPE = 3;
public static final int MULTIREDDIT_TYPE = 4;
public static final int SEARCH_TYPE = 5;
public static final int HISTORY_TYPE = 6;
public static final String HISTORY_TYPE_USAGE_READ_POSTS = "-read-posts";
public static final String NO_USAGE = "--";
@NonNull

View File

@ -21,15 +21,19 @@ public class ReadPost implements Parcelable {
@NonNull
@ColumnInfo(name = "id")
private String id;
@ColumnInfo(name = "time")
private long time;
public ReadPost(@NonNull String username, @NonNull String id) {
this.username = username;
this.id = id;
this.time = System.currentTimeMillis();
}
protected ReadPost(Parcel in) {
username = in.readString();
id = in.readString();
time = in.readLong();
}
public static final Creator<ReadPost> CREATOR = new Creator<ReadPost>() {
@ -62,6 +66,14 @@ public class ReadPost implements Parcelable {
this.id = id;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
@Override
public int describeContents() {
return 0;
@ -71,6 +83,7 @@ public class ReadPost implements Parcelable {
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(username);
parcel.writeString(id);
parcel.writeLong(time);
}
@Override

View File

@ -5,6 +5,8 @@ import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.List;
@Dao
@ -12,6 +14,9 @@ public interface ReadPostDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(ReadPost readPost);
@Query("SELECT * FROM read_posts WHERE username = :username AND time < :before ORDER BY :before LIMIT 25")
ListenableFuture<List<ReadPost>> getAllReadPostsListenableFuture(String username, long before);
@Query("SELECT * FROM read_posts WHERE username = :username")
List<ReadPost> getAllReadPosts(String username);

View File

@ -68,6 +68,7 @@ public class SharedPreferencesUtils {
public static final String POST_LAYOUT_MULTI_REDDIT_POST_BASE = "post_layout_multi_reddit_post_";
public static final String POST_LAYOUT_USER_POST_BASE = "post_layout_user_post_";
public static final String POST_LAYOUT_SEARCH_POST = "post_layout_search_post";
public static final String HISTORY_POST_LAYOUT_FRONT_PAGE_POST = "history_post_layout_read_post";
public static final int POST_LAYOUT_CARD = 0;
public static final int POST_LAYOUT_COMPACT = 1;
public static final int POST_LAYOUT_GALLERY = 2;

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.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:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/coordinator_layout_history_activity"
tools:context=".activities.HistoryActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_layout_history_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar_layout_history_activity"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|enterAlways"
app:titleEnabled="false"
app:toolbarId="@+id/toolbar_history_activity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar_history_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:navigationIcon="?attr/homeAsUpIndicator" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout_tab_layout_history_activity_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:layout_scrollFlags="scroll|enterAlways"
app:tabGravity="fill"
app:tabIndicatorHeight="3dp"
app:tabMode="fixed"
app:tabRippleColor="?attr/colorControlHighlight"
app:tabUnboundedRipple="false" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager_history_activity"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,46 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:application="ml.docilealligator.infinityforreddit.fragments.HistoryPostFragment">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh_layout_history_post_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ml.docilealligator.infinityforreddit.customviews.CustomToroContainer
android:id="@+id/recycler_view_history_post_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<LinearLayout
android:id="@+id/fetch_post_info_linear_layout_history_post_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="48dp"
android:layout_marginBottom="48dp"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:id="@+id/fetch_post_info_image_view_history_post_fragment"
android:layout_width="150dp"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/fetch_post_info_text_view_history_post_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:textSize="?attr/font_default"
android:fontFamily="?attr/font_family" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_refresh_history_activity"
android:orderInCategory="1"
android:title="@string/action_refresh"
app:showAsAction="never" />
<item
android:id="@+id/action_change_post_layout_history_activity"
android:orderInCategory="2"
android:title="@string/action_change_post_layout"
app:showAsAction="never" />
</menu>

View File

@ -45,6 +45,7 @@
<string name="wiki_activity_label">Wiki</string>
<string name="edit_profile_activity_label">Edit Profile</string>
<string name="post_poll_activity_label">Poll Post</string>
<string name="history_activity_label">History</string>
<string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string>