Allow adding/removing menu items, Fix TOTP/HOTP code input
This commit is contained in:
parent
eb57876fe3
commit
002475d1b3
@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetDropDown">
|
||||
<runningDeviceTargetSelectedWithDropDown>
|
||||
<targetSelectedWithDropDown>
|
||||
<Target>
|
||||
<type value="RUNNING_DEVICE_TARGET" />
|
||||
<type value="QUICK_BOOT_TARGET" />
|
||||
<deviceKey>
|
||||
<Key>
|
||||
<type value="SERIAL_NUMBER" />
|
||||
<value value="R38N50464FV" />
|
||||
<type value="VIRTUAL_DEVICE_PATH" />
|
||||
<value value="$USER_HOME$/.android/avd/Pixel_2_API_33.avd" />
|
||||
</Key>
|
||||
</deviceKey>
|
||||
</Target>
|
||||
</runningDeviceTargetSelectedWithDropDown>
|
||||
<timeTargetWasSelectedWithDropDown value="2023-06-25T19:29:01.472337342Z" />
|
||||
</targetSelectedWithDropDown>
|
||||
<timeTargetWasSelectedWithDropDown value="2023-06-26T17:29:55.573199337Z" />
|
||||
</component>
|
||||
</project>
|
@ -35,15 +35,15 @@ dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'com.google.android.material:material:1.9.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata:2.4.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.4.1'
|
||||
implementation 'androidx.navigation:navigation-fragment:2.5.3'
|
||||
implementation 'androidx.navigation:navigation-ui:2.5.3'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
implementation "androidx.biometric:biometric:1.1.0"
|
||||
implementation 'com.cringe_studios:CringeAuthenticatorLibrary:1.0'
|
||||
implementation 'com.cringe_studios:CringeAuthenticatorLibrary:1.2'
|
||||
implementation 'com.google.mlkit:barcode-scanning:17.1.0'
|
||||
implementation 'com.google.code.gson:gson:2.8.9'
|
||||
|
||||
|
@ -6,6 +6,7 @@ import android.content.SharedPreferences;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
@ -24,6 +25,8 @@ public class IntroActivity extends AppCompatActivity {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Log.i("AMOGUS", "CREATE");
|
||||
|
||||
if (!SettingsUtil.isIntroVideoEnabled(this)) {
|
||||
openMainActivity();
|
||||
return;
|
||||
@ -61,11 +64,16 @@ public class IntroActivity extends AppCompatActivity {
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
// When the Activity is destroyed, release our MediaPlayer and set it to null.
|
||||
if(mMediaPlayer != null) mMediaPlayer.release();
|
||||
mMediaPlayer = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
binding.videoView.start();
|
||||
}
|
||||
|
||||
private void setDimension() {
|
||||
float videoProportion = (float) mMediaPlayer.getVideoHeight() / mMediaPlayer.getVideoWidth();
|
||||
int screenWidth = getResources().getDisplayMetrics().widthPixels;
|
||||
|
@ -23,6 +23,7 @@ import androidx.navigation.ui.NavigationUI;
|
||||
|
||||
import com.cringe_studios.cringe_authenticator.databinding.ActivityMainBinding;
|
||||
import com.cringe_studios.cringe_authenticator.databinding.DialogInputCodeChoiceBinding;
|
||||
import com.cringe_studios.cringe_authenticator.databinding.DialogInputCodeHotpBinding;
|
||||
import com.cringe_studios.cringe_authenticator.databinding.DialogInputCodeTotpBinding;
|
||||
import com.cringe_studios.cringe_authenticator.fragment.DynamicFragment;
|
||||
import com.cringe_studios.cringe_authenticator.fragment.HomeFragment;
|
||||
@ -185,7 +186,6 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private void showTOTPDialog() {
|
||||
// TODO: checksum option
|
||||
DialogInputCodeTotpBinding binding = DialogInputCodeTotpBinding.inflate(getLayoutInflater());
|
||||
binding.inputAlgorithm.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, OTPAlgorithm.values()));
|
||||
binding.inputDigits.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, new Integer[]{6, 7, 8, 9, 10, 11, 12}));
|
||||
@ -199,9 +199,9 @@ public class MainActivity extends AppCompatActivity {
|
||||
OTPAlgorithm algorithm = (OTPAlgorithm) binding.inputAlgorithm.getSelectedItem();
|
||||
int digits = (int) binding.inputDigits.getSelectedItem();
|
||||
int period = Integer.parseInt(binding.inputPeriod.getText().toString());
|
||||
boolean checksum = binding.inputChecksum.isChecked();
|
||||
|
||||
// TODO: checksum
|
||||
OTPData data = new OTPData(name, OTPType.TOTP, secret, algorithm, digits, period, 0, false);
|
||||
OTPData data = new OTPData(name, OTPType.TOTP, secret, algorithm, digits, period, 0, checksum);
|
||||
|
||||
String errorMessage = data.validate();
|
||||
if(errorMessage != null) {
|
||||
@ -219,8 +219,36 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private void showHOTPDialog() {
|
||||
DialogInputCodeTotpBinding binding = DialogInputCodeTotpBinding.inflate(getLayoutInflater());
|
||||
showCodeDialog(binding.getRoot(), () -> true);
|
||||
DialogInputCodeHotpBinding binding = DialogInputCodeHotpBinding.inflate(getLayoutInflater());
|
||||
binding.inputAlgorithm.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, OTPAlgorithm.values()));
|
||||
binding.inputDigits.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, new Integer[]{6, 7, 8, 9, 10, 11, 12}));
|
||||
showCodeDialog(binding.getRoot(), () -> {
|
||||
Fragment fragment = NavigationUtil.getCurrentFragment(this);
|
||||
if(!(fragment instanceof DynamicFragment)) return true;
|
||||
|
||||
try {
|
||||
String name = binding.inputName.getText().toString();
|
||||
String secret = binding.inputSecret.getText().toString();
|
||||
OTPAlgorithm algorithm = (OTPAlgorithm) binding.inputAlgorithm.getSelectedItem();
|
||||
int digits = (int) binding.inputDigits.getSelectedItem();
|
||||
int counter = Integer.parseInt(binding.inputCounter.getText().toString());
|
||||
boolean checksum = binding.inputChecksum.isChecked();
|
||||
|
||||
OTPData data = new OTPData(name, OTPType.TOTP, secret, algorithm, digits, 0, counter, checksum);
|
||||
|
||||
String errorMessage = data.validate();
|
||||
if(errorMessage != null) {
|
||||
showErrorDialog(errorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
((DynamicFragment) fragment).addOTP(data);
|
||||
return true;
|
||||
}catch(NumberFormatException e) {
|
||||
showErrorDialog("Invalid number entered");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showCodeDialog(View view, DialogCallback ok) {
|
||||
@ -254,7 +282,17 @@ public class MainActivity extends AppCompatActivity {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("New Group")
|
||||
.setView(t)
|
||||
.setPositiveButton("Add", (view, which) -> {})
|
||||
.setPositiveButton("Add", (view, which) -> {
|
||||
if(t.getText().length() == 0) {
|
||||
showErrorDialog("You need to input a name");
|
||||
return;
|
||||
}
|
||||
|
||||
Fragment frag = NavigationUtil.getCurrentFragment(this);
|
||||
if(frag instanceof MenuFragment) {
|
||||
((MenuFragment) frag).addGroup(t.getText().toString());
|
||||
}
|
||||
})
|
||||
.setNegativeButton("Cancel", (view, which) -> {})
|
||||
.show();
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DynamicFragment extends Fragment {
|
||||
public class DynamicFragment extends NamedFragment {
|
||||
|
||||
public static final String BUNDLE_GROUP = "group";
|
||||
|
||||
@ -36,6 +36,11 @@ public class DynamicFragment extends Fragment {
|
||||
|
||||
private OTPListAdapter otpListAdapter;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return groupName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@ -72,8 +77,7 @@ public class DynamicFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void loadOTPs() {
|
||||
SharedPreferences prefs = requireActivity().getSharedPreferences(SettingsUtil.GROUPS_PREFS_NAME, Context.MODE_PRIVATE);
|
||||
List<OTPData> data = SettingsUtil.getOTPs(prefs, groupName);
|
||||
List<OTPData> data = SettingsUtil.getOTPs(requireContext(), groupName);
|
||||
|
||||
for(OTPData otp : data) {
|
||||
otpListAdapter.add(otp);
|
||||
@ -81,8 +85,7 @@ public class DynamicFragment extends Fragment {
|
||||
}
|
||||
|
||||
public void addOTP(OTPData data) {
|
||||
SharedPreferences prefs = requireActivity().getSharedPreferences(SettingsUtil.GROUPS_PREFS_NAME, Context.MODE_PRIVATE);
|
||||
SettingsUtil.addOTP(prefs, groupName, data);
|
||||
SettingsUtil.addOTP(requireContext(), groupName, data);
|
||||
otpListAdapter.add(data);
|
||||
}
|
||||
|
||||
|
@ -11,10 +11,15 @@ import androidx.fragment.app.Fragment;
|
||||
import com.cringe_studios.cringe_authenticator.databinding.FragmentHomeBinding;
|
||||
import com.cringe_studios.cringe_authenticator.util.FabUtil;
|
||||
|
||||
public class HomeFragment extends Fragment {
|
||||
public class HomeFragment extends NamedFragment {
|
||||
|
||||
private FragmentHomeBinding binding;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Home";
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
binding = FragmentHomeBinding.inflate(inflater, container, false);
|
||||
|
@ -3,6 +3,7 @@ package com.cringe_studios.cringe_authenticator.fragment;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -12,55 +13,71 @@ import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.cringe_studios.cringe_authenticator.OTPData;
|
||||
import com.cringe_studios.cringe_authenticator.databinding.FragmentMenuBinding;
|
||||
import com.cringe_studios.cringe_authenticator.databinding.MenuItemBinding;
|
||||
import com.cringe_studios.cringe_authenticator.grouplist.GroupListAdapter;
|
||||
import com.cringe_studios.cringe_authenticator.grouplist.GroupListItem;
|
||||
import com.cringe_studios.cringe_authenticator.util.FabUtil;
|
||||
import com.cringe_studios.cringe_authenticator.util.NavigationUtil;
|
||||
import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
|
||||
|
||||
public class MenuFragment extends Fragment {
|
||||
import java.util.List;
|
||||
|
||||
public class MenuFragment extends NamedFragment {
|
||||
|
||||
private FragmentMenuBinding binding;
|
||||
|
||||
private GroupListAdapter groupListAdapter;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Menu";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
binding = FragmentMenuBinding.inflate(inflater);
|
||||
|
||||
SharedPreferences pr = requireContext().getSharedPreferences("menu", Context.MODE_PRIVATE);
|
||||
|
||||
String[] items = {"a", "b"};
|
||||
|
||||
for(String item : items) {
|
||||
MenuItemBinding itemBinding = MenuItemBinding.inflate(inflater, binding.menuItems, false);
|
||||
itemBinding.button.setText(item);
|
||||
itemBinding.button.setOnClickListener(view -> {
|
||||
groupListAdapter = new GroupListAdapter(requireContext(), group -> {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(DynamicFragment.BUNDLE_GROUP, item);
|
||||
bundle.putString(DynamicFragment.BUNDLE_GROUP, group);
|
||||
NavigationUtil.navigate(this, DynamicFragment.class, bundle);
|
||||
});
|
||||
itemBinding.button.setOnLongClickListener(view -> {
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle("Delete?")
|
||||
.setMessage("Delete this?")
|
||||
.setPositiveButton("Yes", (dialog, which) -> itemBinding.button.setVisibility(View.GONE))
|
||||
.setNegativeButton("No", (dialog, which) -> {})
|
||||
.show();
|
||||
// TODO: better method?
|
||||
// TODO: actually delete
|
||||
return true;
|
||||
});
|
||||
binding.menuItems.addView(itemBinding.getRoot());
|
||||
}
|
||||
}, this::removeGroup);
|
||||
|
||||
binding.editSwitch.setOnCheckedChangeListener((view, checked) -> {
|
||||
binding.menuItems.setAdapter(groupListAdapter);
|
||||
|
||||
loadGroups();
|
||||
|
||||
/*binding.editSwitch.setOnCheckedChangeListener((view, checked) -> {
|
||||
// TODO: edit mode
|
||||
});
|
||||
});*/
|
||||
|
||||
FabUtil.hideFabs(requireActivity());
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void loadGroups() {
|
||||
List<String> items = SettingsUtil.getGroups(requireContext());
|
||||
Log.i("AMOGUS", "items: " + items);
|
||||
|
||||
for(String item : items) {
|
||||
groupListAdapter.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void addGroup(String group) {
|
||||
SettingsUtil.addGroup(requireContext(), group);
|
||||
groupListAdapter.add(group);
|
||||
}
|
||||
|
||||
public void removeGroup(String group) {
|
||||
SettingsUtil.removeGroup(requireContext(), group);
|
||||
groupListAdapter.remove(group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
@ -0,0 +1,9 @@
|
||||
package com.cringe_studios.cringe_authenticator.fragment;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
public abstract class NamedFragment extends Fragment {
|
||||
|
||||
public abstract String getName();
|
||||
|
||||
}
|
@ -27,10 +27,15 @@ import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class SettingsFragment extends Fragment {
|
||||
public class SettingsFragment extends NamedFragment {
|
||||
|
||||
private FragmentSettingsBinding binding;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Settings";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
|
@ -0,0 +1,93 @@
|
||||
package com.cringe_studios.cringe_authenticator.grouplist;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.util.Consumer;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.cringe_studios.cringe_authenticator.OTPData;
|
||||
import com.cringe_studios.cringe_authenticator.databinding.MenuItemBinding;
|
||||
import com.cringe_studios.cringe_authenticator.fragment.DynamicFragment;
|
||||
import com.cringe_studios.cringe_authenticator.util.NavigationUtil;
|
||||
import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GroupListAdapter extends RecyclerView.Adapter<GroupListItem> {
|
||||
|
||||
private Context context;
|
||||
|
||||
private LayoutInflater inflater;
|
||||
|
||||
private List<String> items;
|
||||
|
||||
private Handler handler;
|
||||
|
||||
private Consumer<String> navigateToGroup;
|
||||
|
||||
private Consumer<String> removeGroup;
|
||||
|
||||
public GroupListAdapter(Context context, Consumer<String> navigateToGroup, Consumer<String> removeGroup) {
|
||||
this.context = context;
|
||||
this.navigateToGroup = navigateToGroup;
|
||||
this.removeGroup = removeGroup;
|
||||
this.inflater = LayoutInflater.from(context);
|
||||
this.items = new ArrayList<>();
|
||||
this.handler = new Handler(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public GroupListItem onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
MenuItemBinding binding = MenuItemBinding.inflate(inflater, parent, false);
|
||||
return new GroupListItem(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull GroupListItem holder, int position) {
|
||||
String group = items.get(position);
|
||||
|
||||
holder.getBinding().button.setText(group);
|
||||
|
||||
holder.getBinding().button.setOnClickListener(view -> navigateToGroup.accept(group));
|
||||
holder.getBinding().button.setOnLongClickListener(view -> {
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle("Delete?")
|
||||
.setMessage("Delete this?")
|
||||
.setPositiveButton("Yes", (dialog, which) -> removeGroup.accept(group))
|
||||
.setNegativeButton("No", (dialog, which) -> {})
|
||||
.show();
|
||||
// TODO: better method?
|
||||
// TODO: actually delete
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
public void add(String group) {
|
||||
items.add(group);
|
||||
notifyItemInserted(items.size() - 1);
|
||||
}
|
||||
|
||||
public void remove(String group) {
|
||||
int index = items.indexOf(group);
|
||||
if(index == -1) return;
|
||||
items.remove(group);
|
||||
notifyItemRemoved(index);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.cringe_studios.cringe_authenticator.grouplist;
|
||||
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.cringe_studios.cringe_authenticator.databinding.MenuItemBinding;
|
||||
|
||||
public class GroupListItem extends RecyclerView.ViewHolder {
|
||||
|
||||
private MenuItemBinding binding;
|
||||
|
||||
public GroupListItem(@NonNull MenuItemBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
public MenuItemBinding getBinding() {
|
||||
return binding;
|
||||
}
|
||||
}
|
@ -68,6 +68,7 @@ public class OTPListAdapter extends RecyclerView.Adapter<OTPListItem> {
|
||||
|
||||
public void remove(OTPData data) {
|
||||
int index = items.indexOf(data);
|
||||
if(index == -1) return;
|
||||
items.remove(data);
|
||||
notifyItemRemoved(index);
|
||||
}
|
||||
|
@ -3,28 +3,44 @@ package com.cringe_studios.cringe_authenticator.util;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
import com.cringe_studios.cringe_authenticator.R;
|
||||
import com.cringe_studios.cringe_authenticator.fragment.NamedFragment;
|
||||
|
||||
import kotlin.Suppress;
|
||||
|
||||
public class NavigationUtil {
|
||||
|
||||
public static void navigate(AppCompatActivity activity, Class<? extends Fragment> fragmentClass, Bundle args) {
|
||||
activity.getSupportActionBar().setTitle(fragmentClass.getSimpleName());
|
||||
navigate(activity.getSupportFragmentManager().getPrimaryNavigationFragment().getChildFragmentManager(), fragmentClass, args);
|
||||
public static void navigate(AppCompatActivity activity, Class<? extends NamedFragment> fragmentClass, Bundle args) {
|
||||
FragmentManager manager = activity.getSupportFragmentManager().getPrimaryNavigationFragment().getChildFragmentManager();
|
||||
NamedFragment fragment = instantiateFragment(manager, fragmentClass, args);
|
||||
|
||||
ActionBar bar = activity.getSupportActionBar();
|
||||
navigate(manager, fragment, () -> {
|
||||
if(bar != null) bar.setTitle(fragment.getName());
|
||||
});
|
||||
}
|
||||
|
||||
public static void navigate(Fragment currentFragment, Class<? extends Fragment> fragmentClass, Bundle args) {
|
||||
((AppCompatActivity) currentFragment.getActivity()).getSupportActionBar().setTitle(fragmentClass.getSimpleName());
|
||||
navigate(currentFragment.getParentFragment().getChildFragmentManager(), fragmentClass, args);
|
||||
public static void navigate(Fragment currentFragment, Class<? extends NamedFragment> fragmentClass, Bundle args) {
|
||||
navigate((AppCompatActivity) currentFragment.requireActivity(), fragmentClass, args);
|
||||
}
|
||||
|
||||
private static void navigate(FragmentManager manager, Class<? extends Fragment> fragmentClass, Bundle args) {
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends Fragment> T instantiateFragment(FragmentManager manager, Class<? extends T> fragmentClass, Bundle args) {
|
||||
T fragment = (T) manager.getFragmentFactory().instantiate(ClassLoader.getSystemClassLoader(), fragmentClass.getName());
|
||||
if(args != null) fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
private static void navigate(FragmentManager manager, Fragment fragment, Runnable onCommit) {
|
||||
manager.beginTransaction()
|
||||
.setReorderingAllowed(true)
|
||||
.replace(R.id.nav_host_fragment_content_main, fragmentClass, args)
|
||||
.replace(R.id.nav_host_fragment_content_main, fragment)
|
||||
.runOnCommit(onCommit)
|
||||
.commit();
|
||||
}
|
||||
|
||||
|
@ -41,21 +41,49 @@ public class SettingsUtil {
|
||||
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
// TODO: refactor
|
||||
public static List<OTPData> getOTPs(SharedPreferences prefs, String group) {
|
||||
String currentOTPs = prefs.getString("group." + group, "[]");
|
||||
public static List<String> getGroups(Context ctx) {
|
||||
SharedPreferences prefs = ctx.getSharedPreferences(GROUPS_PREFS_NAME, Context.MODE_PRIVATE);
|
||||
return Arrays.asList(GSON.fromJson(prefs.getString("groups", "[]"), String[].class));
|
||||
}
|
||||
|
||||
public static void addGroup(Context ctx, String group) {
|
||||
List<String> groups = new ArrayList<>(getGroups(ctx));
|
||||
groups.add(group);
|
||||
|
||||
SharedPreferences prefs = ctx.getSharedPreferences(GROUPS_PREFS_NAME, Context.MODE_PRIVATE);
|
||||
prefs.edit().putString("groups", GSON.toJson(groups)).apply();
|
||||
}
|
||||
|
||||
public static void removeGroup(Context ctx, String group) {
|
||||
List<String> groups = new ArrayList<>(getGroups(ctx));
|
||||
groups.remove(group);
|
||||
|
||||
SharedPreferences prefs = ctx.getSharedPreferences(GROUPS_PREFS_NAME, Context.MODE_PRIVATE);
|
||||
prefs.edit().putString("groups", GSON.toJson(groups)).apply();
|
||||
|
||||
deleteOTPs(ctx, group);
|
||||
}
|
||||
|
||||
public static List<OTPData> getOTPs(Context ctx, String group) {
|
||||
String currentOTPs = ctx.getSharedPreferences(GROUPS_PREFS_NAME, Context.MODE_PRIVATE).getString("group." + group, "[]");
|
||||
return Arrays.asList(GSON.fromJson(currentOTPs, OTPData[].class));
|
||||
}
|
||||
|
||||
public static void addOTP(SharedPreferences prefs, String group, @NonNull OTPData data) {
|
||||
List<OTPData> otps = new ArrayList<>(getOTPs(prefs, group));
|
||||
public static void addOTP(Context ctx, String group, @NonNull OTPData data) {
|
||||
List<OTPData> otps = new ArrayList<>(getOTPs(ctx, group));
|
||||
otps.add(data);
|
||||
|
||||
prefs.edit()
|
||||
ctx.getSharedPreferences(GROUPS_PREFS_NAME, Context.MODE_PRIVATE).edit()
|
||||
.putString("group." + group, GSON.toJson(otps.toArray(new OTPData[0])))
|
||||
.apply();
|
||||
}
|
||||
|
||||
private static void deleteOTPs(Context ctx, String group) {
|
||||
ctx.getSharedPreferences(GROUPS_PREFS_NAME, Context.MODE_PRIVATE).edit()
|
||||
.remove("group." + group)
|
||||
.apply();
|
||||
}
|
||||
|
||||
public static void setEnableIntroVideo(Context ctx, boolean enableIntroVideo) {
|
||||
SharedPreferences prefs = ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE);
|
||||
prefs.edit().putBoolean("enableIntroVideo", enableIntroVideo).apply();
|
||||
|
55
app/src/main/res/layout/dialog_input_code_hotp.xml
Normal file
55
app/src/main/res/layout/dialog_input_code_hotp.xml
Normal file
@ -0,0 +1,55 @@
|
||||
<?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:padding="10dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/input_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="text"
|
||||
android:hint="Name"
|
||||
android:autofillHints="" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/input_secret"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="text"
|
||||
android:hint="Secret"
|
||||
android:autofillHints="" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/input_algorithm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/input_digits"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/input_counter"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="number"
|
||||
android:hint="Counter"
|
||||
android:autofillHints="" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/input_checksum"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Add Checksum" />
|
||||
|
||||
</LinearLayout>
|
@ -11,7 +11,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="text"
|
||||
android:hint="Name" />
|
||||
android:hint="Name"
|
||||
android:autofillHints="" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/input_secret"
|
||||
@ -19,7 +20,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="text"
|
||||
android:hint="Secret" />
|
||||
android:hint="Secret"
|
||||
android:autofillHints="" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/input_algorithm"
|
||||
@ -44,4 +46,10 @@
|
||||
android:hint="Period"
|
||||
android:autofillHints="" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/input_checksum"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Add Checksum" />
|
||||
|
||||
</LinearLayout>
|
@ -11,7 +11,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
<!--<LinearLayout
|
||||
android:id="@+id/menuItems"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
@ -25,7 +25,16 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Edit" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>-->
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/menu_items"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:orientation="vertical"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
@ -11,6 +11,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Button"
|
||||
android:background="@drawable/button_themed" />
|
||||
android:background="@drawable/button_themed"
|
||||
android:textAllCaps="false" />
|
||||
|
||||
</LinearLayout>
|
Loading…
Reference in New Issue
Block a user