Add an option to require authentication before going to account section in navigation drawer.

This commit is contained in:
Alex Ning 2020-09-16 11:23:38 +08:00
parent ef514a85c7
commit eb5cc7d7d4
12 changed files with 177 additions and 38 deletions

View File

@ -95,6 +95,7 @@ dependencies {
implementation 'me.zhanghai.android.fastscroll:library:1.1.2'
implementation "com.thefuntasty.hauler:core:3.1.0"
implementation 'com.github.Piasy:BigImageViewer:1.6.5'
implementation 'androidx.biometric:biometric:1.1.0-alpha02'
def toroVersion = '3.7.0.2010003'
implementation "im.ene.toro3:toro:$toroVersion"

View File

@ -75,6 +75,7 @@ import ml.docilealligator.infinityforreddit.Event.ChangeConfirmToExitEvent;
import ml.docilealligator.infinityforreddit.Event.ChangeDisableSwipingBetweenTabsEvent;
import ml.docilealligator.infinityforreddit.Event.ChangeLockBottomAppBarEvent;
import ml.docilealligator.infinityforreddit.Event.ChangeNSFWEvent;
import ml.docilealligator.infinityforreddit.Event.ChangeRequireAuthToAccountSectionEvent;
import ml.docilealligator.infinityforreddit.Event.RecreateActivityEvent;
import ml.docilealligator.infinityforreddit.Event.SwitchAccountEvent;
import ml.docilealligator.infinityforreddit.FetchMyInfo;
@ -468,9 +469,9 @@ public class MainActivity extends BaseActivity implements SortTypeSelectionCallb
fab.setVisibility(View.VISIBLE);
}
boolean nsfwEnabled = mSharedPreferences.getBoolean(SharedPreferencesUtils.NSFW_KEY, false);
adapter = new NavigationDrawerRecyclerViewAdapter(this, mCustomThemeWrapper, mAccountName,
mProfileImageUrl, mBannerImageUrl, mKarma, nsfwEnabled,
adapter = new NavigationDrawerRecyclerViewAdapter(this, mSharedPreferences,
mCustomThemeWrapper, mAccountName,
mProfileImageUrl, mBannerImageUrl, mKarma,
new NavigationDrawerRecyclerViewAdapter.ItemClickListener() {
@Override
public void onMenuClick(int stringId) {
@ -972,6 +973,11 @@ public class MainActivity extends BaseActivity implements SortTypeSelectionCallb
viewPager2.setUserInputEnabled(!mDisableSwipingBetweenTabs);
}
@Subscribe
public void onChangeRequireAuthToAccountSectionEvent(ChangeRequireAuthToAccountSectionEvent changeRequireAuthToAccountSectionEvent) {
adapter.setRequireAuthToAccountSection(changeRequireAuthToAccountSectionEvent.requireAuthToAccountSection);
}
@Override
public void onLongPress() {
if (sectionsPagerAdapter != null) {

View File

@ -1,6 +1,6 @@
package ml.docilealligator.infinityforreddit.Adapter;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.view.LayoutInflater;
@ -10,6 +10,10 @@ import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.biometric.BiometricManager;
import androidx.biometric.BiometricPrompt;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
@ -18,6 +22,7 @@ import com.bumptech.glide.request.RequestOptions;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import butterknife.BindView;
import butterknife.ButterKnife;
@ -26,8 +31,12 @@ import ml.docilealligator.infinityforreddit.Account.Account;
import ml.docilealligator.infinityforreddit.CustomTheme.CustomThemeWrapper;
import ml.docilealligator.infinityforreddit.R;
import ml.docilealligator.infinityforreddit.SubscribedSubreddit.SubscribedSubredditData;
import ml.docilealligator.infinityforreddit.Utils.SharedPreferencesUtils;
import pl.droidsonroids.gif.GifImageView;
import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG;
import static androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL;
public class NavigationDrawerRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public interface ItemClickListener {
@ -44,7 +53,7 @@ public class NavigationDrawerRecyclerViewAdapter extends RecyclerView.Adapter<Re
private static final int VIEW_TYPE_ACCOUNT = 5;
private static final int CURRENT_MENU_ITEMS = 17;
private Context context;
private AppCompatActivity appCompatActivity;
private Resources resources;
private RequestManager glide;
private String accountName;
@ -52,6 +61,7 @@ public class NavigationDrawerRecyclerViewAdapter extends RecyclerView.Adapter<Re
private String userBannerUrl;
private int karma;
private boolean isNSFWEnabled;
private boolean requireAuthToAccountSection;
private ItemClickListener itemClickListener;
private boolean isLoggedIn;
private boolean isInMainPage = true;
@ -62,18 +72,20 @@ public class NavigationDrawerRecyclerViewAdapter extends RecyclerView.Adapter<Re
private int dividerColor;
private int primaryIconColor;
public NavigationDrawerRecyclerViewAdapter(Context context, CustomThemeWrapper customThemeWrapper,
public NavigationDrawerRecyclerViewAdapter(AppCompatActivity appCompatActivity, SharedPreferences sharedPreferences,
CustomThemeWrapper customThemeWrapper,
String accountName, String userIconUrl,
String userBannerUrl, int karma, boolean isNSFWEnabled,
String userBannerUrl, int karma,
ItemClickListener itemClickListener) {
this.context = context;
resources = context.getResources();
glide = Glide.with(context);
this.appCompatActivity = appCompatActivity;
resources = appCompatActivity.getResources();
glide = Glide.with(appCompatActivity);
this.accountName = accountName;
this.userIconUrl = userIconUrl;
this.userBannerUrl = userBannerUrl;
this.karma = karma;
this.isNSFWEnabled = isNSFWEnabled;
isNSFWEnabled = sharedPreferences.getBoolean(SharedPreferencesUtils.NSFW_KEY, false);
requireAuthToAccountSection = sharedPreferences.getBoolean(SharedPreferencesUtils.REQUIRE_AUTHENTICATION_TO_GO_TO_ACCOUNT_SECTION_IN_NAVIGATION_DRAWER, false);
isLoggedIn = accountName != null;
this.itemClickListener = itemClickListener;
primaryTextColor = customThemeWrapper.getPrimaryTextColor();
@ -149,7 +161,7 @@ public class NavigationDrawerRecyclerViewAdapter extends RecyclerView.Adapter<Re
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (holder instanceof NavHeaderViewHolder) {
if (isLoggedIn) {
((NavHeaderViewHolder) holder).karmaTextView.setText(context.getString(R.string.karma_info, karma));
((NavHeaderViewHolder) holder).karmaTextView.setText(appCompatActivity.getString(R.string.karma_info, karma));
((NavHeaderViewHolder) holder).accountNameTextView.setText(accountName);
if (userIconUrl != null && !userIconUrl.equals("")) {
glide.load(userIconUrl)
@ -182,18 +194,43 @@ public class NavigationDrawerRecyclerViewAdapter extends RecyclerView.Adapter<Re
((NavHeaderViewHolder) holder).itemView.setOnClickListener(view -> {
if (isInMainPage) {
((NavHeaderViewHolder) holder).dropIconImageView.setImageDrawable(resources.getDrawable(R.drawable.ic_baseline_arrow_drop_up_24px));
notifyItemRangeRemoved(1, getItemCount() - 1);
if (accounts != null) {
notifyItemRangeInserted(1, accounts.size() + 3);
} else {
if (isLoggedIn) {
notifyItemRangeInserted(1, 3);
} else {
notifyItemInserted(1);
if (requireAuthToAccountSection) {
BiometricManager biometricManager = BiometricManager.from(appCompatActivity);
if (biometricManager.canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS) {
Executor executor = ContextCompat.getMainExecutor(appCompatActivity);
BiometricPrompt biometricPrompt = new BiometricPrompt(appCompatActivity,
executor, new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationError(int errorCode,
@NonNull CharSequence errString) {
super.onAuthenticationError(errorCode, errString);
}
@Override
public void onAuthenticationSucceeded(
@NonNull BiometricPrompt.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
openAccountSection(((NavHeaderViewHolder) holder).dropIconImageView);
}
@Override
public void onAuthenticationFailed() {
super.onAuthenticationFailed();
}
});
BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
.setTitle(appCompatActivity.getString(R.string.unlock_account_section))
.setAllowedAuthenticators(BIOMETRIC_STRONG | DEVICE_CREDENTIAL)
.build();
biometricPrompt.authenticate(promptInfo);
} else {
openAccountSection(((NavHeaderViewHolder) holder).dropIconImageView);
}
} else {
openAccountSection(((NavHeaderViewHolder) holder).dropIconImageView);
}
isInMainPage = false;
} else {
((NavHeaderViewHolder) holder).dropIconImageView.setImageDrawable(resources.getDrawable(R.drawable.ic_baseline_arrow_drop_down_24px));
notifyItemRangeRemoved(1, getItemCount() - 1);
@ -284,12 +321,12 @@ public class NavigationDrawerRecyclerViewAdapter extends RecyclerView.Adapter<Re
if (isNSFWEnabled) {
isNSFWEnabled = false;
((MenuItemViewHolder) holder).menuTextView.setText(R.string.enable_nsfw);
((MenuItemViewHolder) holder).imageView.setImageDrawable(context.getDrawable(R.drawable.ic_nsfw_on_24dp));
((MenuItemViewHolder) holder).imageView.setImageDrawable(appCompatActivity.getDrawable(R.drawable.ic_nsfw_on_24dp));
itemClickListener.onMenuClick(R.string.disable_nsfw);
} else {
isNSFWEnabled = true;
((MenuItemViewHolder) holder).menuTextView.setText(R.string.disable_nsfw);
((MenuItemViewHolder) holder).imageView.setImageDrawable(context.getDrawable(R.drawable.ic_nsfw_off_24dp));
((MenuItemViewHolder) holder).imageView.setImageDrawable(appCompatActivity.getDrawable(R.drawable.ic_nsfw_off_24dp));
itemClickListener.onMenuClick(R.string.enable_nsfw);
}
});
@ -323,12 +360,12 @@ public class NavigationDrawerRecyclerViewAdapter extends RecyclerView.Adapter<Re
if (isNSFWEnabled) {
isNSFWEnabled = false;
((MenuItemViewHolder) holder).menuTextView.setText(R.string.enable_nsfw);
((MenuItemViewHolder) holder).imageView.setImageDrawable(context.getDrawable(R.drawable.ic_nsfw_on_24dp));
((MenuItemViewHolder) holder).imageView.setImageDrawable(appCompatActivity.getDrawable(R.drawable.ic_nsfw_on_24dp));
itemClickListener.onMenuClick(R.string.disable_nsfw);
} else {
isNSFWEnabled = true;
((MenuItemViewHolder) holder).menuTextView.setText(R.string.disable_nsfw);
((MenuItemViewHolder) holder).imageView.setImageDrawable(context.getDrawable(R.drawable.ic_nsfw_off_24dp));
((MenuItemViewHolder) holder).imageView.setImageDrawable(appCompatActivity.getDrawable(R.drawable.ic_nsfw_off_24dp));
itemClickListener.onMenuClick(R.string.enable_nsfw);
}
});
@ -359,7 +396,7 @@ public class NavigationDrawerRecyclerViewAdapter extends RecyclerView.Adapter<Re
if (stringId != 0) {
((MenuItemViewHolder) holder).menuTextView.setText(stringId);
((MenuItemViewHolder) holder).imageView.setImageDrawable(context.getDrawable(drawableId));
((MenuItemViewHolder) holder).imageView.setImageDrawable(appCompatActivity.getDrawable(drawableId));
if (setOnClickListener) {
int finalStringId = stringId;
((MenuItemViewHolder) holder).itemView.setOnClickListener(view -> itemClickListener.onMenuClick(finalStringId));
@ -396,6 +433,21 @@ public class NavigationDrawerRecyclerViewAdapter extends RecyclerView.Adapter<Re
}
}
private void openAccountSection(ImageView dropIconImageView) {
dropIconImageView.setImageDrawable(resources.getDrawable(R.drawable.ic_baseline_arrow_drop_up_24px));
notifyItemRangeRemoved(1, getItemCount() - 1);
if (accounts != null) {
notifyItemRangeInserted(1, accounts.size() + 3);
} else {
if (isLoggedIn) {
notifyItemRangeInserted(1, 3);
} else {
notifyItemInserted(1);
}
}
isInMainPage = false;
}
@Override
public int getItemCount() {
if (isInMainPage) {
@ -457,6 +509,10 @@ public class NavigationDrawerRecyclerViewAdapter extends RecyclerView.Adapter<Re
}
}
public void setRequireAuthToAccountSection(boolean requireAuthToAccountSection) {
this.requireAuthToAccountSection = requireAuthToAccountSection;
}
class NavHeaderViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.name_text_view_nav_header_main)
TextView accountNameTextView;

View File

@ -0,0 +1,9 @@
package ml.docilealligator.infinityforreddit.Event;
public class ChangeRequireAuthToAccountSectionEvent {
public boolean requireAuthToAccountSection;
public ChangeRequireAuthToAccountSectionEvent(boolean requireAuthToAccountSection) {
this.requireAuthToAccountSection = requireAuthToAccountSection;
}
}

View File

@ -0,0 +1,29 @@
package ml.docilealligator.infinityforreddit.Settings;
import android.os.Bundle;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.SwitchPreference;
import org.greenrobot.eventbus.EventBus;
import ml.docilealligator.infinityforreddit.Event.ChangeRequireAuthToAccountSectionEvent;
import ml.docilealligator.infinityforreddit.R;
import ml.docilealligator.infinityforreddit.Utils.SharedPreferencesUtils;
public class SecurityPreferenceFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.security_preferences, rootKey);
SwitchPreference requireAuthToAccountSectionSwitch = findPreference(SharedPreferencesUtils.REQUIRE_AUTHENTICATION_TO_GO_TO_ACCOUNT_SECTION_IN_NAVIGATION_DRAWER);
if (requireAuthToAccountSectionSwitch != null) {
requireAuthToAccountSectionSwitch.setOnPreferenceChangeListener((preference, newValue) -> {
EventBus.getDefault().post(new ChangeRequireAuthToAccountSectionEvent((Boolean) newValue));
return true;
});
}
}
}

View File

@ -123,6 +123,7 @@ public class SharedPreferencesUtils {
public static final String DISABLE_SWIPING_BETWEEN_TABS = "disable_swiping_between_tabs";
public static final String ENABLE_SWIPE_ACTION = "enable_swipe_action";
public static final String PULL_TO_REFRESH = "pull_to_refresh";
public static final String REQUIRE_AUTHENTICATION_TO_GO_TO_ACCOUNT_SECTION_IN_NAVIGATION_DRAWER = "require_auth_to_account_section";
public static final String MAIN_PAGE_TABS_SHARED_PREFERENCES_FILE = "ml.docilealligator.infinityforreddit.main_page_tabs";
public static final String MAIN_PAGE_TAB_1_TITLE = "_main_page_tab_1_title";

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,1L3,5v6c0,5.55 3.84,10.74 9,12 5.16,-1.26 9,-6.45 9,-12L21,5l-9,-4zM12,11.99h7c-0.53,4.12 -3.28,7.79 -7,8.94L12,12L5,12L5,6.3l7,-3.11v8.8z"
android:fillColor="#FFFFFF"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,1L3,5v6c0,5.55 3.84,10.74 9,12 5.16,-1.26 9,-6.45 9,-12L21,5l-9,-4zM12,11.99h7c-0.53,4.12 -3.28,7.79 -7,8.94L12,12L5,12L5,6.3l7,-3.11v8.8z"
android:fillColor="#000000"/>
</vector>

View File

@ -233,14 +233,4 @@
<item>1</item>
<item>2</item>
</string-array>
<!-- Reply Preference -->
<string-array name="reply_entries">
<item>Reply</item>
<item>Reply to all</item>
</string-array>
<string-array name="reply_values">
<item>reply</item>
<item>reply_all</item>
</string-array>
</resources>

View File

@ -483,6 +483,8 @@
<string name="settings_disable_swiping_between_tabs_title">Disable Swiping Between Tabs</string>
<string name="settings_enable_swipe_action_title">Enable Swipe Action</string>
<string name="settings_pull_to_refresh_title">Pull to Refresh</string>
<string name="settings_security_title">Security</string>
<string name="settings_require_authentication_to_go_to_account_section_in_navigation_drawer">Require Authentication to Go to Account Section in Navigation Drawer</string>
<string name="no_link_available">Cannot get the link</string>
@ -882,7 +884,21 @@
<string name="block_user_failed">Failed to block user</string>
<string name="view_full_comment_markdown">View Full Markdown</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="unlock_account_section">Unlock Account Section</string>
<!-- Preference Titles -->
<string name="messages_header">Messages</string>
<string name="sync_header">Sync</string>
<!-- Messages Preferences -->
<string name="signature_title">Your signature</string>
<string name="reply_title">Default reply action</string>
<!-- Sync Preferences -->
<string name="sync_title">Sync email periodically</string>
<string name="attachment_title">Download incoming attachments</string>
<string name="attachment_summary_on">Automatically download attachments for incoming emails
</string>
<string name="attachment_summary_off">Only download attachments when manually requested</string>
</resources>

View File

@ -41,6 +41,11 @@
android:icon="@drawable/ic_download_24dp"
app:fragment="ml.docilealligator.infinityforreddit.Settings.DownloadLocationPreferenceFragment" />
<Preference
app:title="@string/settings_security_title"
android:icon="@drawable/ic_security_24dp"
app:fragment="ml.docilealligator.infinityforreddit.Settings.SecurityPreferenceFragment" />
<SwitchPreference
app:defaultValue="false"
app:key="save_front_page_scrolled_position"

View File

@ -0,0 +1,8 @@
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
<SwitchPreference
app:defaultValue="false"
app:key="require_auth_to_account_section"
app:title="@string/settings_require_authentication_to_go_to_account_section_in_navigation_drawer" />
</PreferenceScreen>