Lazy Mode: Automatically scroll down the recyclerview in PostFragment to view posts without using hands.

This commit is contained in:
Alex Ning 2019-06-08 08:01:31 +08:00
parent d7a90b5397
commit 7cfc4296a0
6 changed files with 161 additions and 24 deletions

View File

@ -2,4 +2,6 @@ package ml.docilealligator.infinityforreddit;
interface FragmentCommunicator { interface FragmentCommunicator {
void refresh(); void refresh();
default void startLazyMode() {};
default void stopLazyMode() {};
} }

View File

@ -12,10 +12,23 @@ import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestManager; import com.bumptech.glide.RequestManager;
import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.RequestOptions;
import com.ferfalk.simplesearchview.SimpleSearchView; import com.ferfalk.simplesearchview.SimpleSearchView;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import java.util.ArrayList; import java.util.ArrayList;
@ -30,15 +43,6 @@ import SubscribedSubredditDatabase.SubscribedSubredditViewModel;
import SubscribedUserDatabase.SubscribedUserData; import SubscribedUserDatabase.SubscribedUserData;
import SubscribedUserDatabase.SubscribedUserRoomDatabase; import SubscribedUserDatabase.SubscribedUserRoomDatabase;
import SubscribedUserDatabase.SubscribedUserViewModel; import SubscribedUserDatabase.SubscribedUserViewModel;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import jp.wasabeef.glide.transformations.RoundedCornersTransformation; import jp.wasabeef.glide.transformations.RoundedCornersTransformation;
@ -50,10 +54,12 @@ public class MainActivity extends AppCompatActivity {
private static final String FRAGMENT_OUT_STATE = "FOS"; private static final String FRAGMENT_OUT_STATE = "FOS";
private static final String FETCH_USER_INFO_STATE = "FUIS"; private static final String FETCH_USER_INFO_STATE = "FUIS";
private static final String INSERT_SUBSCRIBED_SUBREDDIT_STATE = "ISSS"; private static final String INSERT_SUBSCRIBED_SUBREDDIT_STATE = "ISSS";
private static final String IS_IN_LAZY_MODE_STATE = "IILMS";
private static final int LOGIN_ACTIVITY_REQUEST_CODE = 0; private static final int LOGIN_ACTIVITY_REQUEST_CODE = 0;
@BindView(R.id.drawer_layout) DrawerLayout drawer; @BindView(R.id.drawer_layout) DrawerLayout drawer;
@BindView(R.id.collapsing_toolbar_layout_main_activity) CollapsingToolbarLayout collapsingToolbarLayout;
@BindView(R.id.search_view_main_activity) SimpleSearchView simpleSearchView; @BindView(R.id.search_view_main_activity) SimpleSearchView simpleSearchView;
@BindView(R.id.transparent_overlay_main_activity) View transparentOverlay; @BindView(R.id.transparent_overlay_main_activity) View transparentOverlay;
@BindView(R.id.subscribed_subreddit_recycler_view_main_activity) RecyclerView subscribedSubredditRecyclerView; @BindView(R.id.subscribed_subreddit_recycler_view_main_activity) RecyclerView subscribedSubredditRecyclerView;
@ -69,6 +75,7 @@ public class MainActivity extends AppCompatActivity {
private Fragment mFragment; private Fragment mFragment;
private RequestManager glide; private RequestManager glide;
private AppBarLayout.LayoutParams params;
private String mName; private String mName;
private String mProfileImageUrl; private String mProfileImageUrl;
@ -77,9 +84,13 @@ public class MainActivity extends AppCompatActivity {
private boolean mFetchUserInfoSuccess = false; private boolean mFetchUserInfoSuccess = false;
private boolean mInsertSuccess = false; private boolean mInsertSuccess = false;
private Menu mMenu;
private SubscribedSubredditViewModel mSubscribedSubredditViewModel; private SubscribedSubredditViewModel mSubscribedSubredditViewModel;
private SubscribedUserViewModel mSubscribedUserViewModel; private SubscribedUserViewModel mSubscribedUserViewModel;
private boolean isInLazyMode = false;
@Inject @Inject
@Named("user_info") @Named("user_info")
SharedPreferences mUserInfoSharedPreferences; SharedPreferences mUserInfoSharedPreferences;
@ -109,6 +120,8 @@ public class MainActivity extends AppCompatActivity {
drawer.addDrawerListener(toggle); drawer.addDrawerListener(toggle);
toggle.syncState(); toggle.syncState();
params = (AppBarLayout.LayoutParams) collapsingToolbarLayout.getLayoutParams();
transparentOverlay.setOnClickListener(view -> simpleSearchView.onBackPressed()); transparentOverlay.setOnClickListener(view -> simpleSearchView.onBackPressed());
simpleSearchView.setOnQueryTextListener(new SimpleSearchView.OnQueryTextListener() { simpleSearchView.setOnQueryTextListener(new SimpleSearchView.OnQueryTextListener() {
@ -170,6 +183,7 @@ public class MainActivity extends AppCompatActivity {
mFetchUserInfoSuccess = savedInstanceState.getBoolean(FETCH_USER_INFO_STATE); mFetchUserInfoSuccess = savedInstanceState.getBoolean(FETCH_USER_INFO_STATE);
mInsertSuccess = savedInstanceState.getBoolean(INSERT_SUBSCRIBED_SUBREDDIT_STATE); mInsertSuccess = savedInstanceState.getBoolean(INSERT_SUBSCRIBED_SUBREDDIT_STATE);
isInLazyMode = savedInstanceState.getBoolean(IS_IN_LAZY_MODE_STATE);
} }
glide = Glide.with(this); glide = Glide.with(this);
@ -347,24 +361,50 @@ public class MainActivity extends AppCompatActivity {
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_activity, menu); getMenuInflater().inflate(R.menu.main_activity, menu);
mMenu = menu;
simpleSearchView.setMenuItem(menu.findItem(R.id.action_search_main_activity)); simpleSearchView.setMenuItem(mMenu.findItem(R.id.action_search_main_activity));
MenuItem lazyModeItem = mMenu.findItem(R.id.action_lazy_mode_main_activity);
if(isInLazyMode) {
lazyModeItem.setTitle(R.string.action_stop_lazy_mode);
params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL);
collapsingToolbarLayout.setLayoutParams(params);
} else {
lazyModeItem.setTitle(R.string.action_start_lazy_mode);
params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS);
collapsingToolbarLayout.setLayoutParams(params);
}
return true; return true;
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (mFragment instanceof FragmentCommunicator) {
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.action_refresh_main_activity: case R.id.action_refresh_main_activity:
if (mFragment instanceof FragmentCommunicator) {
((FragmentCommunicator) mFragment).refresh(); ((FragmentCommunicator) mFragment).refresh();
mFetchUserInfoSuccess = false; mFetchUserInfoSuccess = false;
mInsertSuccess = false; mInsertSuccess = false;
loadUserData(); loadUserData();
return true;
case R.id.action_lazy_mode_main_activity:
MenuItem lazyModeItem = mMenu.findItem(R.id.action_lazy_mode_main_activity);
if(isInLazyMode) {
isInLazyMode = false;
((FragmentCommunicator) mFragment).stopLazyMode();
lazyModeItem.setTitle(R.string.action_start_lazy_mode);
params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS);
collapsingToolbarLayout.setLayoutParams(params);
} else {
isInLazyMode = true;
((FragmentCommunicator) mFragment).startLazyMode();
lazyModeItem.setTitle(R.string.action_stop_lazy_mode);
params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL);
collapsingToolbarLayout.setLayoutParams(params);
} }
return true; return true;
} }
}
return false; return false;
} }
@ -383,7 +423,7 @@ public class MainActivity extends AppCompatActivity {
} }
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
if (mFragment != null) { if (mFragment != null) {
getSupportFragmentManager().putFragment(outState, FRAGMENT_OUT_STATE, mFragment); getSupportFragmentManager().putFragment(outState, FRAGMENT_OUT_STATE, mFragment);
@ -391,5 +431,6 @@ public class MainActivity extends AppCompatActivity {
outState.putBoolean(FETCH_USER_INFO_STATE, mFetchUserInfoSuccess); outState.putBoolean(FETCH_USER_INFO_STATE, mFetchUserInfoSuccess);
outState.putBoolean(INSERT_SUBSCRIBED_SUBREDDIT_STATE, mInsertSuccess); outState.putBoolean(INSERT_SUBSCRIBED_SUBREDDIT_STATE, mInsertSuccess);
outState.putBoolean(IS_IN_LAZY_MODE_STATE, isInLazyMode);
} }
} }

View File

@ -4,18 +4,23 @@ package ml.docilealligator.infinityforreddit;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders; import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
@ -39,6 +44,7 @@ public class PostFragment extends Fragment implements FragmentCommunicator {
static final String NAME_KEY = "NK"; static final String NAME_KEY = "NK";
static final String POST_TYPE_KEY = "PTK"; static final String POST_TYPE_KEY = "PTK";
private static final String IS_IN_LAZY_MODE_STATE = "IILMS";
@BindView(R.id.coordinator_layout_post_fragment) CoordinatorLayout mCoordinatorLayout; @BindView(R.id.coordinator_layout_post_fragment) CoordinatorLayout mCoordinatorLayout;
@BindView(R.id.recycler_view_post_fragment) RecyclerView mPostRecyclerView; @BindView(R.id.recycler_view_post_fragment) RecyclerView mPostRecyclerView;
@ -51,11 +57,17 @@ public class PostFragment extends Fragment implements FragmentCommunicator {
private String mName; private String mName;
private int mPostType; private int mPostType;
private boolean isInLazyMode = false;
private PostRecyclerViewAdapter mAdapter; private PostRecyclerViewAdapter mAdapter;
private RecyclerView.SmoothScroller smoothScroller;
PostViewModel mPostViewModel; PostViewModel mPostViewModel;
private Window window;
private Handler lazyModeHandler;
private Runnable lazyModeRunnable;
@Inject @Named("no_oauth") @Inject @Named("no_oauth")
Retrofit mRetrofit; Retrofit mRetrofit;
@ -89,6 +101,24 @@ public class PostFragment extends Fragment implements FragmentCommunicator {
EventBus.getDefault().register(this); EventBus.getDefault().register(this);
lazyModeHandler = new Handler();
smoothScroller = new LinearSmoothScroller(getActivity()) {
@Override
protected int getVerticalSnapPreference() {
return LinearSmoothScroller.SNAP_TO_START;
}
};
window = getActivity().getWindow();
if(savedInstanceState != null) {
isInLazyMode = savedInstanceState.getBoolean(IS_IN_LAZY_MODE_STATE);
if(isInLazyMode) {
startLazyMode();
}
}
mLinearLayoutManager = new LinearLayoutManager(getActivity()); mLinearLayoutManager = new LinearLayoutManager(getActivity());
mPostRecyclerView.setLayoutManager(mLinearLayoutManager); mPostRecyclerView.setLayoutManager(mLinearLayoutManager);
@ -165,6 +195,12 @@ public class PostFragment extends Fragment implements FragmentCommunicator {
return rootView; return rootView;
} }
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(IS_IN_LAZY_MODE_STATE, isInLazyMode);
}
@Override @Override
public void refresh() { public void refresh() {
mPostViewModel.refresh(); mPostViewModel.refresh();
@ -179,6 +215,36 @@ public class PostFragment extends Fragment implements FragmentCommunicator {
} }
} }
@Override
public void startLazyMode() {
Toast.makeText(getActivity(), getString(R.string.lazy_mode_start, 2.5), Toast.LENGTH_SHORT).show();
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
isInLazyMode = true;
lazyModeRunnable = new Runnable() {
@Override
public void run() {
int nPosts = mAdapter.getItemCount();
int firstVisiblePosition = mLinearLayoutManager.findFirstVisibleItemPosition();
if(firstVisiblePosition != RecyclerView.NO_POSITION && nPosts > firstVisiblePosition) {
smoothScroller.setTargetPosition(firstVisiblePosition + 1);
mLinearLayoutManager.startSmoothScroll(smoothScroller);
}
if(isInLazyMode) {
lazyModeHandler.postDelayed(this, 2500);
}
}
};
lazyModeHandler.postDelayed(lazyModeRunnable, 2500);
}
@Override
public void stopLazyMode() {
lazyModeHandler.removeCallbacks(lazyModeRunnable);
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
isInLazyMode = false;
Toast.makeText(getActivity(), getString(R.string.lazy_mode_stop), Toast.LENGTH_SHORT).show();
}
@Subscribe @Subscribe
public void onPostUpdateEvent(PostUpdateEventToPostList event) { public void onPostUpdateEvent(PostUpdateEventToPostList event) {
Post post = mAdapter.getCurrentList().get(event.positionInList); Post post = mAdapter.getCurrentList().get(event.positionInList);
@ -190,6 +256,23 @@ public class PostFragment extends Fragment implements FragmentCommunicator {
} }
} }
@Override
public void onStart() {
super.onStart();
if(isInLazyMode) {
startLazyMode();
}
}
@Override
public void onStop() {
super.onStop();
if(isInLazyMode) {
stopLazyMode();
}
isInLazyMode = true;
}
@Override @Override
public void onDestroy() { public void onDestroy() {
EventBus.getDefault().unregister(this); EventBus.getDefault().unregister(this);

View File

@ -12,6 +12,7 @@
android:theme="@style/AppTheme.AppBarOverlay"> android:theme="@style/AppTheme.AppBarOverlay">
<com.google.android.material.appbar.CollapsingToolbarLayout <com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar_layout_main_activity"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:titleEnabled="false" app:titleEnabled="false"

View File

@ -3,17 +3,22 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:context="ml.docilealligator.infinityforreddit.MainActivity"> tools:context="ml.docilealligator.infinityforreddit.MainActivity">
<item
android:id="@+id/action_refresh_main_activity"
android:orderInCategory="2"
android:title="@string/action_refresh"
android:icon="@drawable/ic_refresh_white_24dp"
app:showAsAction="never" />
<item <item
android:id="@+id/action_search_main_activity" android:id="@+id/action_search_main_activity"
android:orderInCategory="1" android:orderInCategory="1"
android:title="@string/action_search" android:title="@string/action_search"
android:icon="@drawable/ic_search_white_24dp" android:icon="@drawable/ic_search_white_24dp"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
<item
android:id="@+id/action_refresh_main_activity"
android:orderInCategory="2"
android:title="@string/action_refresh"
app:showAsAction="never" />
<item
android:id="@+id/action_lazy_mode_main_activity"
android:orderInCategory="3"
android:title="@string/action_start_lazy_mode"
app:showAsAction="never" />
</menu> </menu>

View File

@ -10,6 +10,8 @@
<string name="action_download">Download</string> <string name="action_download">Download</string>
<string name="action_refresh">Refresh</string> <string name="action_refresh">Refresh</string>
<string name="action_search">Search</string> <string name="action_search">Search</string>
<string name="action_start_lazy_mode">Start Lazy Mode</string>
<string name="action_stop_lazy_mode">Stop Lazy Mode</string>
<string name="tap_to_retry">Error loading image. Tap to retry.</string> <string name="tap_to_retry">Error loading image. Tap to retry.</string>
<string name="load_posts_error">Error loading posts.\nTap to retry.</string> <string name="load_posts_error">Error loading posts.\nTap to retry.</string>
@ -63,4 +65,7 @@
<string name="app_label">Infinity</string> <string name="app_label">Infinity</string>
<string name="search_hint">Search anything</string> <string name="search_hint">Search anything</string>
<string name="lazy_mode_start">Lazy Mode starts in %1$fs</string>
<string name="lazy_mode_stop">Lazy Mode stopped</string>
</resources> </resources>