Browse Source
Add Backup and restore. Update DirectoryChooser UI.
Add Backup and restore. Update DirectoryChooser UI.
The updated backup and restore is backward compatible with old backup files. Just have updated the default file name and extension for newer backups.renovate/org.robolectric-robolectric-4.x
Ammar Githam
4 years ago
35 changed files with 1387 additions and 898 deletions
-
75app/src/main/java/awais/instagrabber/adapters/DirectoryFilesAdapter.java
-
75app/src/main/java/awais/instagrabber/adapters/SimpleAdapter.java
-
9app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageInboxItemViewHolder.java
-
169app/src/main/java/awais/instagrabber/dialogs/CreateBackupDialogFragment.java
-
180app/src/main/java/awais/instagrabber/dialogs/RestoreBackupDialogFragment.java
-
4app/src/main/java/awais/instagrabber/fragments/FavoritesFragment.java
-
2app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
-
2app/src/main/java/awais/instagrabber/fragments/LocationFragment.java
-
2app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java
-
107app/src/main/java/awais/instagrabber/fragments/settings/BackupPreferencesFragment.java
-
2app/src/main/java/awais/instagrabber/fragments/settings/BasePreferencesFragment.java
-
9app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java
-
6app/src/main/java/awais/instagrabber/fragments/settings/SettingsPreferencesFragment.java
-
1app/src/main/java/awais/instagrabber/utils/Constants.java
-
80app/src/main/java/awais/instagrabber/utils/DataBox.java
-
181app/src/main/java/awais/instagrabber/utils/DirectoryChooser.java
-
4app/src/main/java/awais/instagrabber/utils/DownloadUtils.java
-
454app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java
-
47app/src/main/java/awais/instagrabber/utils/PasswordUtils.java
-
2app/src/main/java/awais/instagrabber/utils/SettingsHelper.java
-
133app/src/main/java/awais/instagrabber/utils/Utils.java
-
18app/src/main/java/awais/instagrabber/viewmodels/FileListViewModel.java
-
10app/src/main/res/drawable/ic_file_24.xml
-
10app/src/main/res/drawable/ic_folder_24.xml
-
10app/src/main/res/drawable/ic_settings_backup_restore_24.xml
-
73app/src/main/res/layout/dialog_create_backup.xml
-
230app/src/main/res/layout/dialog_import_export.xml
-
143app/src/main/res/layout/dialog_restore_backup.xml
-
38app/src/main/res/layout/item_dir_list.xml
-
153app/src/main/res/layout/layout_directory_chooser.xml
-
3app/src/main/res/layout/pref_custom_folder.xml
-
7app/src/main/res/navigation/more_nav_graph.xml
-
22app/src/main/res/values/strings.xml
-
23app/src/main/res/values/styles.xml
-
1app/src/main/res/values/themes.xml
@ -0,0 +1,75 @@ |
|||
package awais.instagrabber.adapters; |
|||
|
|||
import android.view.LayoutInflater; |
|||
import android.view.ViewGroup; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.recyclerview.widget.DiffUtil; |
|||
import androidx.recyclerview.widget.ListAdapter; |
|||
import androidx.recyclerview.widget.RecyclerView; |
|||
|
|||
import java.io.File; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.databinding.ItemDirListBinding; |
|||
|
|||
public final class DirectoryFilesAdapter extends ListAdapter<File, DirectoryFilesAdapter.ViewHolder> { |
|||
private final OnFileClickListener onFileClickListener; |
|||
|
|||
private static final DiffUtil.ItemCallback<File> DIFF_CALLBACK = new DiffUtil.ItemCallback<File>() { |
|||
@Override |
|||
public boolean areItemsTheSame(@NonNull final File oldItem, @NonNull final File newItem) { |
|||
return oldItem.getAbsolutePath().equals(newItem.getAbsolutePath()); |
|||
} |
|||
|
|||
@Override |
|||
public boolean areContentsTheSame(@NonNull final File oldItem, @NonNull final File newItem) { |
|||
return oldItem.getAbsolutePath().equals(newItem.getAbsolutePath()); |
|||
} |
|||
}; |
|||
|
|||
public DirectoryFilesAdapter(final OnFileClickListener onFileClickListener) { |
|||
super(DIFF_CALLBACK); |
|||
this.onFileClickListener = onFileClickListener; |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) { |
|||
final LayoutInflater inflater = LayoutInflater.from(parent.getContext()); |
|||
final ItemDirListBinding binding = ItemDirListBinding.inflate(inflater, parent, false); |
|||
return new ViewHolder(binding); |
|||
} |
|||
|
|||
@Override |
|||
public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) { |
|||
final File file = getItem(position); |
|||
holder.bind(file, onFileClickListener); |
|||
} |
|||
|
|||
public interface OnFileClickListener { |
|||
void onFileClick(File file); |
|||
} |
|||
|
|||
static final class ViewHolder extends RecyclerView.ViewHolder { |
|||
private final ItemDirListBinding binding; |
|||
|
|||
private ViewHolder(final ItemDirListBinding binding) { |
|||
super(binding.getRoot()); |
|||
this.binding = binding; |
|||
} |
|||
|
|||
public void bind(final File file, final OnFileClickListener onFileClickListener) { |
|||
if (file == null) return; |
|||
if (onFileClickListener != null) { |
|||
itemView.setOnClickListener(v -> onFileClickListener.onFileClick(file)); |
|||
} |
|||
binding.text.setText(file.getName()); |
|||
if (file.isDirectory()) { |
|||
binding.icon.setImageResource(R.drawable.ic_folder_24); |
|||
return; |
|||
} |
|||
binding.icon.setImageResource(R.drawable.ic_file_24); |
|||
} |
|||
} |
|||
} |
@ -1,75 +0,0 @@ |
|||
package awais.instagrabber.adapters; |
|||
|
|||
import android.content.Context; |
|||
import android.view.LayoutInflater; |
|||
import android.view.View; |
|||
import android.view.ViewGroup; |
|||
import android.widget.TextView; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.recyclerview.widget.RecyclerView; |
|||
|
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.utils.DataBox; |
|||
|
|||
public final class SimpleAdapter<T> extends RecyclerView.Adapter<SimpleAdapter.SimpleViewHolder> { |
|||
private List<T> items; |
|||
private final LayoutInflater layoutInflater; |
|||
private final View.OnClickListener onClickListener; |
|||
private final View.OnLongClickListener longClickListener; |
|||
|
|||
public SimpleAdapter(final Context context, final List<T> items, final View.OnClickListener onClickListener) { |
|||
this(context, items, onClickListener, null); |
|||
} |
|||
|
|||
public SimpleAdapter(final Context context, final List<T> items, final View.OnClickListener onClickListener, |
|||
final View.OnLongClickListener longClickListener) { |
|||
this.layoutInflater = LayoutInflater.from(context); |
|||
this.items = items; |
|||
this.onClickListener = onClickListener; |
|||
this.longClickListener = longClickListener; |
|||
} |
|||
|
|||
public void setItems(final List<T> items) { |
|||
this.items = items; |
|||
notifyDataSetChanged(); |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public SimpleViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) { |
|||
return new SimpleViewHolder(layoutInflater. |
|||
inflate(R.layout.item_dir_list, parent, false), onClickListener, longClickListener); |
|||
} |
|||
|
|||
@Override |
|||
public void onBindViewHolder(@NonNull final SimpleViewHolder holder, final int position) { |
|||
final T item = items.get(position); |
|||
holder.itemView.setTag(item); |
|||
holder.text.setText(item.toString()); |
|||
if (item instanceof DataBox.CookieModel && ((DataBox.CookieModel) item).isSelected() || |
|||
item instanceof String && ((String) item).toLowerCase().endsWith(".zaai")) |
|||
holder.itemView.setBackgroundColor(0xF0_125687); |
|||
else |
|||
holder.itemView.setBackground(null); |
|||
} |
|||
|
|||
@Override |
|||
public int getItemCount() { |
|||
return items != null ? items.size() : 0; |
|||
} |
|||
|
|||
static final class SimpleViewHolder extends RecyclerView.ViewHolder { |
|||
private final TextView text; |
|||
|
|||
private SimpleViewHolder(@NonNull final View itemView, final View.OnClickListener onClickListener, |
|||
final View.OnLongClickListener longClickListener) { |
|||
super(itemView); |
|||
text = itemView.findViewById(android.R.id.text1); |
|||
itemView.setOnClickListener(onClickListener); |
|||
if (longClickListener != null) itemView.setOnLongClickListener(longClickListener); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,169 @@ |
|||
package awais.instagrabber.dialogs; |
|||
|
|||
import android.app.Dialog; |
|||
import android.content.Context; |
|||
import android.content.pm.PackageManager; |
|||
import android.os.Bundle; |
|||
import android.text.Editable; |
|||
import android.text.TextWatcher; |
|||
import android.view.LayoutInflater; |
|||
import android.view.View; |
|||
import android.view.ViewGroup; |
|||
import android.view.Window; |
|||
import android.view.inputmethod.InputMethodManager; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.core.content.ContextCompat; |
|||
import androidx.fragment.app.DialogFragment; |
|||
import androidx.fragment.app.FragmentTransaction; |
|||
|
|||
import java.io.File; |
|||
import java.util.Locale; |
|||
|
|||
import awais.instagrabber.databinding.DialogCreateBackupBinding; |
|||
import awais.instagrabber.utils.DirectoryChooser; |
|||
import awais.instagrabber.utils.ExportImportUtils; |
|||
import awais.instagrabber.utils.TextUtils; |
|||
import awais.instagrabber.utils.Utils; |
|||
|
|||
import static awais.instagrabber.utils.Constants.FOLDER_PATH; |
|||
import static awais.instagrabber.utils.DownloadUtils.PERMS; |
|||
|
|||
public class CreateBackupDialogFragment extends DialogFragment { |
|||
private static final int STORAGE_PERM_REQUEST_CODE = 8020; |
|||
|
|||
private final OnResultListener onResultListener; |
|||
private DialogCreateBackupBinding binding; |
|||
|
|||
public CreateBackupDialogFragment(final OnResultListener onResultListener) { |
|||
this.onResultListener = onResultListener; |
|||
} |
|||
|
|||
@Override |
|||
public View onCreateView(@NonNull final LayoutInflater inflater, |
|||
final ViewGroup container, |
|||
final Bundle savedInstanceState) { |
|||
binding = DialogCreateBackupBinding.inflate(inflater, container, false); |
|||
return binding.getRoot(); |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public Dialog onCreateDialog(Bundle savedInstanceState) { |
|||
Dialog dialog = super.onCreateDialog(savedInstanceState); |
|||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); |
|||
return dialog; |
|||
} |
|||
|
|||
@Override |
|||
public void onStart() { |
|||
super.onStart(); |
|||
final Dialog dialog = getDialog(); |
|||
if (dialog == null) return; |
|||
final Window window = dialog.getWindow(); |
|||
if (window == null) return; |
|||
final int height = ViewGroup.LayoutParams.WRAP_CONTENT; |
|||
final int width = (int) (Utils.displayMetrics.widthPixels * 0.8); |
|||
window.setLayout(width, height); |
|||
} |
|||
|
|||
@Override |
|||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { |
|||
super.onViewCreated(view, savedInstanceState); |
|||
init(); |
|||
} |
|||
|
|||
private void init() { |
|||
binding.etPassword.addTextChangedListener(new TextWatcher() { |
|||
@Override |
|||
public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {} |
|||
|
|||
@Override |
|||
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) { |
|||
binding.btnSaveTo.setEnabled(!TextUtils.isEmpty(s)); |
|||
} |
|||
|
|||
@Override |
|||
public void afterTextChanged(final Editable s) {} |
|||
}); |
|||
final Context context = getContext(); |
|||
if (context == null) { |
|||
return; |
|||
} |
|||
binding.cbPassword.setOnCheckedChangeListener((buttonView, isChecked) -> { |
|||
if (isChecked) { |
|||
if (TextUtils.isEmpty(binding.etPassword.getText())) { |
|||
binding.btnSaveTo.setEnabled(false); |
|||
} |
|||
binding.passwordField.setVisibility(View.VISIBLE); |
|||
binding.etPassword.requestFocus(); |
|||
final InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); |
|||
if (imm == null) return; |
|||
imm.showSoftInput(binding.etPassword, InputMethodManager.SHOW_IMPLICIT); |
|||
return; |
|||
} |
|||
binding.btnSaveTo.setEnabled(true); |
|||
binding.passwordField.setVisibility(View.GONE); |
|||
final InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); |
|||
if (imm == null) return; |
|||
imm.hideSoftInputFromWindow(binding.etPassword.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN); |
|||
}); |
|||
binding.btnSaveTo.setOnClickListener(v -> { |
|||
if (ContextCompat.checkSelfPermission(context, PERMS[0]) == PackageManager.PERMISSION_GRANTED) { |
|||
showChooser(context); |
|||
} else { |
|||
requestPermissions(PERMS, STORAGE_PERM_REQUEST_CODE); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
@Override |
|||
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) { |
|||
super.onRequestPermissionsResult(requestCode, permissions, grantResults); |
|||
if (requestCode == STORAGE_PERM_REQUEST_CODE && grantResults[0] == PackageManager.PERMISSION_GRANTED) { |
|||
final Context context = getContext(); |
|||
if (context == null) return; |
|||
showChooser(context); |
|||
} |
|||
} |
|||
|
|||
private void showChooser(@NonNull final Context context) { |
|||
final String folderPath = Utils.settingsHelper.getString(FOLDER_PATH); |
|||
final Editable passwordText = binding.etPassword.getText(); |
|||
final String password = binding.cbPassword.isChecked() |
|||
&& passwordText != null |
|||
&& !TextUtils.isEmpty(passwordText.toString()) |
|||
? passwordText.toString().trim() |
|||
: null; |
|||
final DirectoryChooser directoryChooser = new DirectoryChooser() |
|||
.setInitialDirectory(folderPath) |
|||
.setInteractionListener(path -> { |
|||
final File file = new File(path, String.format(Locale.ENGLISH, "barinsta_%d.backup", System.currentTimeMillis())); |
|||
int flags = 0; |
|||
if (binding.cbExportFavorites.isChecked()) { |
|||
flags |= ExportImportUtils.FLAG_FAVORITES; |
|||
} |
|||
if (binding.cbExportSettings.isChecked()) { |
|||
flags |= ExportImportUtils.FLAG_SETTINGS; |
|||
} |
|||
if (binding.cbExportLogins.isChecked()) { |
|||
flags |= ExportImportUtils.FLAG_COOKIES; |
|||
} |
|||
ExportImportUtils.exportData(password, flags, file, result -> { |
|||
if (onResultListener != null) { |
|||
onResultListener.onResult(result); |
|||
} |
|||
dismiss(); |
|||
}, context); |
|||
|
|||
}); |
|||
directoryChooser.setEnterTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); |
|||
directoryChooser.setExitTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); |
|||
directoryChooser.show(getChildFragmentManager(), "directory_chooser"); |
|||
} |
|||
|
|||
public interface OnResultListener { |
|||
void onResult(boolean result); |
|||
} |
|||
} |
@ -0,0 +1,180 @@ |
|||
package awais.instagrabber.dialogs; |
|||
|
|||
import android.app.Dialog; |
|||
import android.content.Context; |
|||
import android.content.pm.PackageManager; |
|||
import android.os.Bundle; |
|||
import android.text.Editable; |
|||
import android.text.TextWatcher; |
|||
import android.view.LayoutInflater; |
|||
import android.view.View; |
|||
import android.view.ViewGroup; |
|||
import android.view.Window; |
|||
import android.view.inputmethod.InputMethodManager; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.core.content.ContextCompat; |
|||
import androidx.fragment.app.DialogFragment; |
|||
import androidx.fragment.app.FragmentTransaction; |
|||
|
|||
import java.io.File; |
|||
|
|||
import awais.instagrabber.databinding.DialogRestoreBackupBinding; |
|||
import awais.instagrabber.utils.DirectoryChooser; |
|||
import awais.instagrabber.utils.ExportImportUtils; |
|||
import awais.instagrabber.utils.PasswordUtils.IncorrectPasswordException; |
|||
import awais.instagrabber.utils.TextUtils; |
|||
import awais.instagrabber.utils.Utils; |
|||
|
|||
import static awais.instagrabber.utils.Constants.FOLDER_PATH; |
|||
import static awais.instagrabber.utils.DownloadUtils.PERMS; |
|||
|
|||
public class RestoreBackupDialogFragment extends DialogFragment { |
|||
private static final int STORAGE_PERM_REQUEST_CODE = 8020; |
|||
|
|||
private final OnResultListener onResultListener; |
|||
|
|||
private DialogRestoreBackupBinding binding; |
|||
private File file; |
|||
private boolean isEncrypted; |
|||
|
|||
public RestoreBackupDialogFragment(final OnResultListener onResultListener) { |
|||
this.onResultListener = onResultListener; |
|||
} |
|||
|
|||
@Override |
|||
public View onCreateView(@NonNull final LayoutInflater inflater, |
|||
final ViewGroup container, |
|||
final Bundle savedInstanceState) { |
|||
binding = DialogRestoreBackupBinding.inflate(inflater, container, false); |
|||
return binding.getRoot(); |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public Dialog onCreateDialog(Bundle savedInstanceState) { |
|||
Dialog dialog = super.onCreateDialog(savedInstanceState); |
|||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); |
|||
return dialog; |
|||
} |
|||
|
|||
@Override |
|||
public void onStart() { |
|||
super.onStart(); |
|||
final Dialog dialog = getDialog(); |
|||
if (dialog == null) return; |
|||
final Window window = dialog.getWindow(); |
|||
if (window == null) return; |
|||
final int height = ViewGroup.LayoutParams.WRAP_CONTENT; |
|||
final int width = (int) (Utils.displayMetrics.widthPixels * 0.8); |
|||
window.setLayout(width, height); |
|||
} |
|||
|
|||
@Override |
|||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { |
|||
super.onViewCreated(view, savedInstanceState); |
|||
init(); |
|||
} |
|||
|
|||
@Override |
|||
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) { |
|||
super.onRequestPermissionsResult(requestCode, permissions, grantResults); |
|||
if (requestCode == STORAGE_PERM_REQUEST_CODE && grantResults[0] == PackageManager.PERMISSION_GRANTED) { |
|||
showChooser(); |
|||
} |
|||
} |
|||
|
|||
private void init() { |
|||
final Context context = getContext(); |
|||
if (context == null) { |
|||
return; |
|||
} |
|||
binding.btnRestore.setEnabled(false); |
|||
binding.btnRestore.setOnClickListener(v -> { |
|||
int flags = 0; |
|||
if (binding.cbFavorites.isChecked()) { |
|||
flags |= ExportImportUtils.FLAG_FAVORITES; |
|||
} |
|||
if (binding.cbSettings.isChecked()) { |
|||
flags |= ExportImportUtils.FLAG_SETTINGS; |
|||
} |
|||
if (binding.cbAccounts.isChecked()) { |
|||
flags |= ExportImportUtils.FLAG_COOKIES; |
|||
} |
|||
final Editable text = binding.etPassword.getText(); |
|||
if (isEncrypted && text == null) return; |
|||
try { |
|||
ExportImportUtils.importData( |
|||
context, |
|||
flags, |
|||
file, |
|||
!isEncrypted ? null : text.toString(), |
|||
result -> { |
|||
if (onResultListener != null) { |
|||
onResultListener.onResult(result); |
|||
} |
|||
dismiss(); |
|||
} |
|||
); |
|||
} catch (IncorrectPasswordException e) { |
|||
binding.passwordField.setError("Incorrect password"); |
|||
} |
|||
}); |
|||
binding.etPassword.addTextChangedListener(new TextWatcher() { |
|||
@Override |
|||
public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {} |
|||
|
|||
@Override |
|||
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) { |
|||
binding.btnRestore.setEnabled(!TextUtils.isEmpty(s)); |
|||
binding.passwordField.setError(null); |
|||
} |
|||
|
|||
@Override |
|||
public void afterTextChanged(final Editable s) {} |
|||
}); |
|||
if (ContextCompat.checkSelfPermission(context, PERMS[0]) == PackageManager.PERMISSION_GRANTED) { |
|||
showChooser(); |
|||
return; |
|||
} |
|||
requestPermissions(PERMS, STORAGE_PERM_REQUEST_CODE); |
|||
} |
|||
|
|||
private void showChooser() { |
|||
final String folderPath = Utils.settingsHelper.getString(FOLDER_PATH); |
|||
final Context context = getContext(); |
|||
if (context == null) return; |
|||
final DirectoryChooser directoryChooser = new DirectoryChooser() |
|||
.setInitialDirectory(folderPath) |
|||
.setShowBackupFiles(true) |
|||
.setInteractionListener(file -> { |
|||
isEncrypted = ExportImportUtils.isEncrypted(file); |
|||
if (isEncrypted) { |
|||
binding.passwordGroup.setVisibility(View.VISIBLE); |
|||
binding.passwordGroup.post(() -> { |
|||
binding.etPassword.requestFocus(); |
|||
binding.etPassword.post(() -> { |
|||
final InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); |
|||
if (imm == null) return; |
|||
imm.showSoftInput(binding.etPassword, InputMethodManager.SHOW_IMPLICIT); |
|||
}); |
|||
binding.btnRestore.setEnabled(!TextUtils.isEmpty(binding.etPassword.getText())); |
|||
}); |
|||
} else { |
|||
binding.passwordGroup.setVisibility(View.GONE); |
|||
binding.btnRestore.setEnabled(true); |
|||
} |
|||
this.file = file; |
|||
binding.filePath.setText(file.getAbsolutePath()); |
|||
}); |
|||
directoryChooser.setEnterTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); |
|||
directoryChooser.setExitTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); |
|||
directoryChooser.setOnCancelListener(this::dismiss); |
|||
directoryChooser.show(getChildFragmentManager(), "directory_chooser"); |
|||
} |
|||
|
|||
public interface OnResultListener { |
|||
void onResult(boolean result); |
|||
} |
|||
} |
@ -0,0 +1,107 @@ |
|||
package awais.instagrabber.fragments.settings; |
|||
|
|||
import android.content.Context; |
|||
import android.view.View; |
|||
import android.widget.Toast; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.fragment.app.FragmentActivity; |
|||
import androidx.fragment.app.FragmentManager; |
|||
import androidx.fragment.app.FragmentTransaction; |
|||
import androidx.preference.Preference; |
|||
import androidx.preference.PreferenceScreen; |
|||
|
|||
import com.google.android.material.snackbar.BaseTransientBottomBar; |
|||
import com.google.android.material.snackbar.Snackbar; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.dialogs.CreateBackupDialogFragment; |
|||
import awais.instagrabber.dialogs.RestoreBackupDialogFragment; |
|||
|
|||
public class BackupPreferencesFragment extends BasePreferencesFragment { |
|||
|
|||
@Override |
|||
void setupPreferenceScreen(final PreferenceScreen screen) { |
|||
final Context context = getContext(); |
|||
if (context == null) { |
|||
return; |
|||
} |
|||
screen.addPreference(getCreatePreference(context)); |
|||
screen.addPreference(getRestorePreference(context)); |
|||
} |
|||
|
|||
private Preference getCreatePreference(@NonNull final Context context) { |
|||
final Preference preference = new Preference(context); |
|||
preference.setTitle(R.string.create_backup); |
|||
preference.setIconSpaceReserved(false); |
|||
preference.setOnPreferenceClickListener(preference1 -> { |
|||
final FragmentManager fragmentManager = getParentFragmentManager(); |
|||
final CreateBackupDialogFragment fragment = new CreateBackupDialogFragment(result -> { |
|||
final View view = getView(); |
|||
if (view != null) { |
|||
Snackbar.make(view, |
|||
result ? R.string.dialog_export_success |
|||
: R.string.dialog_export_failed, |
|||
BaseTransientBottomBar.LENGTH_LONG) |
|||
.setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE) |
|||
.setAction(R.string.ok, v -> {}) |
|||
.show(); |
|||
return; |
|||
} |
|||
Toast.makeText(context, |
|||
result ? R.string.dialog_export_success |
|||
: R.string.dialog_export_failed, |
|||
Toast.LENGTH_LONG) |
|||
.show(); |
|||
}); |
|||
final FragmentTransaction ft = fragmentManager.beginTransaction(); |
|||
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) |
|||
.add(fragment, "createBackup") |
|||
.commit(); |
|||
return true; |
|||
}); |
|||
return preference; |
|||
} |
|||
|
|||
private Preference getRestorePreference(@NonNull final Context context) { |
|||
final Preference preference = new Preference(context); |
|||
preference.setTitle(R.string.restore_backup); |
|||
preference.setIconSpaceReserved(false); |
|||
preference.setOnPreferenceClickListener(preference1 -> { |
|||
final FragmentManager fragmentManager = getParentFragmentManager(); |
|||
final RestoreBackupDialogFragment fragment = new RestoreBackupDialogFragment(result -> { |
|||
final View view = getView(); |
|||
if (view != null) { |
|||
Snackbar.make(view, |
|||
result ? R.string.dialog_import_success |
|||
: R.string.dialog_import_failed, |
|||
BaseTransientBottomBar.LENGTH_LONG) |
|||
.setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE) |
|||
.setAction(R.string.ok, v -> {}) |
|||
.addCallback(new BaseTransientBottomBar.BaseCallback<Snackbar>() { |
|||
@Override |
|||
public void onDismissed(final Snackbar transientBottomBar, final int event) { |
|||
recreateActivity(result); |
|||
} |
|||
}) |
|||
.show(); |
|||
return; |
|||
} |
|||
recreateActivity(result); |
|||
}); |
|||
final FragmentTransaction ft = fragmentManager.beginTransaction(); |
|||
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) |
|||
.add(fragment, "restoreBackup") |
|||
.commit(); |
|||
return true; |
|||
}); |
|||
return preference; |
|||
} |
|||
|
|||
private void recreateActivity(final boolean result) { |
|||
if (!result) return; |
|||
final FragmentActivity activity = getActivity(); |
|||
if (activity == null) return; |
|||
activity.recreate(); |
|||
} |
|||
} |
@ -0,0 +1,47 @@ |
|||
package awais.instagrabber.utils; |
|||
|
|||
import android.util.Base64; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
|
|||
import java.security.GeneralSecurityException; |
|||
import java.security.InvalidAlgorithmParameterException; |
|||
import java.security.InvalidKeyException; |
|||
import java.security.NoSuchAlgorithmException; |
|||
|
|||
import javax.crypto.BadPaddingException; |
|||
import javax.crypto.Cipher; |
|||
import javax.crypto.IllegalBlockSizeException; |
|||
import javax.crypto.NoSuchPaddingException; |
|||
import javax.crypto.spec.IvParameterSpec; |
|||
import javax.crypto.spec.SecretKeySpec; |
|||
|
|||
public final class PasswordUtils { |
|||
private static final String cipherAlgo = "AES"; |
|||
private static final String cipherTran = "AES/CBC/PKCS5Padding"; |
|||
|
|||
public static byte[] dec(final String encrypted, final byte[] keyValue) throws Exception { |
|||
try { |
|||
final Cipher cipher = Cipher.getInstance(cipherTran); |
|||
final SecretKeySpec secretKey = new SecretKeySpec(keyValue, cipherAlgo); |
|||
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(new byte[16])); |
|||
return cipher.doFinal(Base64.decode(encrypted, Base64.DEFAULT | Base64.NO_PADDING | Base64.NO_WRAP)); |
|||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) { |
|||
throw new IncorrectPasswordException(e); |
|||
} |
|||
} |
|||
|
|||
public static byte[] enc(@NonNull final String str, final byte[] keyValue) throws Exception { |
|||
final Cipher cipher = Cipher.getInstance(cipherTran); |
|||
final SecretKeySpec secretKey = new SecretKeySpec(keyValue, cipherAlgo); |
|||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(new byte[16])); |
|||
final byte[] bytes = cipher.doFinal(str.getBytes()); |
|||
return Base64.encode(bytes, Base64.DEFAULT | Base64.NO_PADDING | Base64.NO_WRAP); |
|||
} |
|||
|
|||
public static class IncorrectPasswordException extends Exception { |
|||
public IncorrectPasswordException(final GeneralSecurityException e) { |
|||
super(e); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,18 @@ |
|||
package awais.instagrabber.viewmodels; |
|||
|
|||
import androidx.lifecycle.MutableLiveData; |
|||
import androidx.lifecycle.ViewModel; |
|||
|
|||
import java.io.File; |
|||
import java.util.List; |
|||
|
|||
public class FileListViewModel extends ViewModel { |
|||
private MutableLiveData<List<File>> list; |
|||
|
|||
public MutableLiveData<List<File>> getList() { |
|||
if (list == null) { |
|||
list = new MutableLiveData<>(); |
|||
} |
|||
return list; |
|||
} |
|||
} |
@ -0,0 +1,10 @@ |
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:width="24dp" |
|||
android:height="24dp" |
|||
android:viewportWidth="24" |
|||
android:viewportHeight="24" |
|||
android:tint="?attr/colorControlNormal"> |
|||
<path |
|||
android:fillColor="@android:color/white" |
|||
android:pathData="M6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6L6,2zM13,9L13,3.5L18.5,9L13,9z"/> |
|||
</vector> |
@ -0,0 +1,10 @@ |
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:width="24dp" |
|||
android:height="24dp" |
|||
android:viewportWidth="24" |
|||
android:viewportHeight="24" |
|||
android:tint="?attr/colorControlNormal"> |
|||
<path |
|||
android:fillColor="@android:color/white" |
|||
android:pathData="M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z"/> |
|||
</vector> |
@ -0,0 +1,10 @@ |
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:width="24dp" |
|||
android:height="24dp" |
|||
android:viewportWidth="24" |
|||
android:viewportHeight="24" |
|||
android:tint="?attr/colorControlNormal"> |
|||
<path |
|||
android:fillColor="@android:color/white" |
|||
android:pathData="M14,12c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2 0.9,2 2,2 2,-0.9 2,-2zM12,3c-4.97,0 -9,4.03 -9,9L0,12l4,4 4,-4L5,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.51,0 -2.91,-0.49 -4.06,-1.3l-1.42,1.44C8.04,20.3 9.94,21 12,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9z"/> |
|||
</vector> |
@ -0,0 +1,73 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<LinearLayout 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:orientation="vertical" |
|||
android:paddingLeft="16dp" |
|||
android:paddingTop="16dp" |
|||
android:paddingRight="16dp" |
|||
android:paddingBottom="0dp"> |
|||
|
|||
<androidx.appcompat.widget.AppCompatCheckBox |
|||
android:id="@+id/cbExportSettings" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:checked="true" |
|||
android:text="@string/dialog_export_settings" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatCheckBox |
|||
android:id="@+id/cbExportLogins" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:text="@string/dialog_export_accounts" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatCheckBox |
|||
android:id="@+id/cbExportFavorites" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:text="@string/dialog_export_favorites" /> |
|||
|
|||
<include layout="@layout/item_pref_divider" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatCheckBox |
|||
android:id="@+id/cbPassword" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:text="@string/set_password" /> |
|||
|
|||
<com.google.android.material.textfield.TextInputLayout |
|||
android:id="@+id/passwordField" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:hint="@string/password_no_max" |
|||
android:visibility="gone" |
|||
app:counterEnabled="true" |
|||
app:counterMaxLength="32" |
|||
app:endIconMode="password_toggle" |
|||
tools:visibility="visible"> |
|||
|
|||
<com.google.android.material.textfield.TextInputEditText |
|||
android:id="@+id/etPassword" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:autofillHints="no" |
|||
android:inputType="textPassword" |
|||
android:maxLength="2200" |
|||
android:scrollHorizontally="false" |
|||
tools:text="test" /> |
|||
|
|||
</com.google.android.material.textfield.TextInputLayout> |
|||
|
|||
<include layout="@layout/item_pref_divider" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/btnSaveTo" |
|||
style="@style/Widget.MaterialComponents.Button.TextButton.Dialog.Flush" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_gravity="end" |
|||
android:layout_marginTop="16dp" |
|||
android:text="@string/create_backup" /> |
|||
</LinearLayout> |
@ -1,230 +0,0 @@ |
|||
<?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"> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
style="@style/TextAppearance.AppCompat.Headline" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:ellipsize="end" |
|||
android:paddingStart="?attr/dialogPreferredPadding" |
|||
android:paddingLeft="?attr/dialogPreferredPadding" |
|||
android:paddingTop="10dp" |
|||
android:paddingEnd="?attr/dialogPreferredPadding" |
|||
android:paddingRight="?attr/dialogPreferredPadding" |
|||
android:paddingBottom="6dp" |
|||
android:singleLine="true" |
|||
android:text="@string/import_export" /> |
|||
|
|||
<androidx.appcompat.widget.LinearLayoutCompat |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="5dp" |
|||
android:layout_marginLeft="5dp" |
|||
android:background="?android:selectableItemBackground" |
|||
android:orientation="horizontal" |
|||
android:padding="5dp"> |
|||
|
|||
<androidx.appcompat.widget.AppCompatCheckBox |
|||
android:id="@+id/cbExportSettings" |
|||
android:layout_width="30dp" |
|||
android:layout_height="30dp" |
|||
android:checked="true" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:gravity="center_vertical" |
|||
android:padding="5dp" |
|||
android:text="@string/dialog_export_settings" |
|||
android:textColor="?android:textColorPrimary" |
|||
android:textSize="16sp" /> |
|||
</androidx.appcompat.widget.LinearLayoutCompat> |
|||
|
|||
<androidx.appcompat.widget.LinearLayoutCompat |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="5dp" |
|||
android:layout_marginLeft="5dp" |
|||
android:background="?android:selectableItemBackground" |
|||
android:orientation="horizontal" |
|||
android:padding="5dp"> |
|||
|
|||
<androidx.appcompat.widget.AppCompatCheckBox |
|||
android:id="@+id/cbExportLogins" |
|||
android:layout_width="30dp" |
|||
android:layout_height="30dp" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:gravity="center_vertical" |
|||
android:padding="5dp" |
|||
android:text="@string/dialog_export_logins" |
|||
android:textColor="?android:textColorPrimary" |
|||
android:textSize="16sp" /> |
|||
</androidx.appcompat.widget.LinearLayoutCompat> |
|||
|
|||
<androidx.appcompat.widget.LinearLayoutCompat |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="5dp" |
|||
android:layout_marginLeft="5dp" |
|||
android:background="?android:selectableItemBackground" |
|||
android:orientation="horizontal" |
|||
android:padding="5dp"> |
|||
|
|||
<androidx.appcompat.widget.AppCompatCheckBox |
|||
android:id="@+id/cbExportFavorites" |
|||
android:layout_width="30dp" |
|||
android:layout_height="30dp" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:gravity="center_vertical" |
|||
android:padding="5dp" |
|||
android:text="@string/dialog_export_favorites" |
|||
android:textColor="?android:textColorPrimary" |
|||
android:textSize="16sp" /> |
|||
</androidx.appcompat.widget.LinearLayoutCompat> |
|||
|
|||
<androidx.appcompat.widget.LinearLayoutCompat |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="5dp" |
|||
android:layout_marginLeft="5dp" |
|||
android:background="?android:selectableItemBackground" |
|||
android:orientation="horizontal" |
|||
android:padding="5dp"> |
|||
|
|||
<androidx.appcompat.widget.AppCompatCheckBox |
|||
android:id="@+id/cbPassword" |
|||
android:layout_width="30dp" |
|||
android:layout_height="30dp" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:gravity="center_vertical" |
|||
android:padding="5dp" |
|||
android:text="@string/password" |
|||
android:textColor="?android:textColorPrimary" |
|||
android:textSize="16sp" /> |
|||
|
|||
<include |
|||
android:id="@+id/etPassword" |
|||
layout="@layout/layout_password" /> |
|||
</androidx.appcompat.widget.LinearLayoutCompat> |
|||
|
|||
<androidx.appcompat.widget.AppCompatButton |
|||
android:id="@+id/btnSaveTo" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_gravity="end" |
|||
android:layout_marginStart="8dp" |
|||
android:layout_marginLeft="8dp" |
|||
android:layout_marginTop="8dp" |
|||
android:layout_marginEnd="8dp" |
|||
android:layout_marginRight="8dp" |
|||
android:paddingStart="24dp" |
|||
android:paddingLeft="24dp" |
|||
android:paddingEnd="24dp" |
|||
android:paddingRight="24dp" |
|||
android:text="@string/dialog_export_btn_export" /> |
|||
|
|||
<View |
|||
android:layout_width="match_parent" |
|||
android:layout_height="1dip" |
|||
android:layout_marginTop="8dp" |
|||
android:background="?android:attr/dividerVertical" /> |
|||
|
|||
<androidx.appcompat.widget.LinearLayoutCompat |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="5dp" |
|||
android:layout_marginLeft="5dp" |
|||
android:background="?android:selectableItemBackground" |
|||
android:orientation="horizontal" |
|||
android:padding="5dp"> |
|||
|
|||
<androidx.appcompat.widget.AppCompatCheckBox |
|||
android:id="@+id/cbImportSettings" |
|||
android:layout_width="30dp" |
|||
android:layout_height="30dp" |
|||
android:checked="true" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:gravity="center_vertical" |
|||
android:padding="5dp" |
|||
android:text="@string/dialog_import_settings" |
|||
android:textColor="?android:textColorPrimary" |
|||
android:textSize="16sp" /> |
|||
</androidx.appcompat.widget.LinearLayoutCompat> |
|||
|
|||
<androidx.appcompat.widget.LinearLayoutCompat |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="5dp" |
|||
android:layout_marginLeft="5dp" |
|||
android:background="?android:selectableItemBackground" |
|||
android:orientation="horizontal" |
|||
android:padding="5dp"> |
|||
|
|||
<androidx.appcompat.widget.AppCompatCheckBox |
|||
android:id="@+id/cbImportLogins" |
|||
android:layout_width="30dp" |
|||
android:layout_height="30dp" |
|||
android:checked="true" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:gravity="center_vertical" |
|||
android:padding="5dp" |
|||
android:text="@string/dialog_import_logins" |
|||
android:textColor="?android:textColorPrimary" |
|||
android:textSize="16sp" /> |
|||
</androidx.appcompat.widget.LinearLayoutCompat> |
|||
|
|||
<androidx.appcompat.widget.LinearLayoutCompat |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="5dp" |
|||
android:layout_marginLeft="5dp" |
|||
android:background="?android:selectableItemBackground" |
|||
android:orientation="horizontal" |
|||
android:padding="5dp"> |
|||
|
|||
<androidx.appcompat.widget.AppCompatCheckBox |
|||
android:id="@+id/cbImportFavorites" |
|||
android:layout_width="30dp" |
|||
android:layout_height="30dp" |
|||
android:checked="true" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:gravity="center_vertical" |
|||
android:padding="5dp" |
|||
android:text="@string/dialog_import_favorites" |
|||
android:textColor="?android:textColorPrimary" |
|||
android:textSize="16sp" /> |
|||
</androidx.appcompat.widget.LinearLayoutCompat> |
|||
|
|||
<androidx.appcompat.widget.AppCompatButton |
|||
android:id="@+id/btnImport" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_gravity="end" |
|||
android:layout_margin="8dp" |
|||
android:paddingStart="24dp" |
|||
android:paddingLeft="24dp" |
|||
android:paddingEnd="24dp" |
|||
android:paddingRight="24dp" |
|||
android:text="@string/dialog_export_btn_import" /> |
|||
</LinearLayout> |
@ -0,0 +1,143 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<androidx.constraintlayout.widget.ConstraintLayout 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:orientation="vertical" |
|||
android:paddingLeft="16dp" |
|||
android:paddingTop="16dp" |
|||
android:paddingRight="16dp" |
|||
android:paddingBottom="0dp"> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/file_chosen_label" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:text="@string/file_chosen_label" |
|||
android:textAppearance="@style/TextAppearance.AppCompat.Body2" |
|||
app:layout_constraintBottom_toTopOf="@id/file_path" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toTopOf="parent" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/file_path" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/file_chosen_label" |
|||
tools:text="file path file path file path file path file path file path file path file path file path file path file path " /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatCheckBox |
|||
android:id="@+id/cbSettings" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:checked="true" |
|||
android:text="@string/dialog_export_settings" |
|||
app:layout_constraintBottom_toTopOf="@id/cbAccounts" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/file_path" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatCheckBox |
|||
android:id="@+id/cbAccounts" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:text="@string/dialog_export_accounts" |
|||
app:layout_constraintBottom_toTopOf="@id/cbFavorites" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/cbSettings" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatCheckBox |
|||
android:id="@+id/cbFavorites" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:text="@string/dialog_export_favorites" |
|||
app:layout_constraintBottom_toTopOf="@id/top_password_divider" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/cbAccounts" /> |
|||
|
|||
<include |
|||
android:id="@+id/top_password_divider" |
|||
layout="@layout/item_pref_divider" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="1dp" |
|||
app:layout_constraintBottom_toTopOf="@id/enter_password_label" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/cbFavorites" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatTextView |
|||
android:id="@+id/enter_password_label" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginTop="8dp" |
|||
android:layout_marginBottom="4dp" |
|||
android:text="@string/enter_password" |
|||
android:textAppearance="@style/TextAppearance.AppCompat.Body1" |
|||
app:layout_constraintBottom_toTopOf="@id/passwordField" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/top_password_divider" /> |
|||
|
|||
<com.google.android.material.textfield.TextInputLayout |
|||
android:id="@+id/passwordField" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginBottom="8dp" |
|||
android:hint="@string/password_no_max" |
|||
app:counterEnabled="true" |
|||
app:counterMaxLength="32" |
|||
app:endIconMode="password_toggle" |
|||
app:layout_constraintBottom_toTopOf="@id/bottom_password_divider" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/enter_password_label"> |
|||
|
|||
<com.google.android.material.textfield.TextInputEditText |
|||
android:id="@+id/etPassword" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:autofillHints="no" |
|||
android:inputType="textPassword" |
|||
android:maxLength="2200" |
|||
android:scrollHorizontally="false" |
|||
tools:text="test" /> |
|||
|
|||
</com.google.android.material.textfield.TextInputLayout> |
|||
|
|||
<include |
|||
android:id="@+id/bottom_password_divider" |
|||
layout="@layout/item_pref_divider" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="1dp" |
|||
app:layout_constraintBottom_toTopOf="@id/btn_restore" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/passwordField" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/btn_restore" |
|||
style="@style/Widget.MaterialComponents.Button.TextButton.Dialog.Flush" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_gravity="end" |
|||
android:layout_marginTop="16dp" |
|||
android:text="@string/restore_backup" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/bottom_password_divider" /> |
|||
|
|||
<androidx.constraintlayout.widget.Group |
|||
android:id="@+id/password_group" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:visibility="gone" |
|||
app:constraint_referenced_ids="top_password_divider,bottom_password_divider,enter_password_label,passwordField" |
|||
tools:visibility="visible" /> |
|||
|
|||
|
|||
</androidx.constraintlayout.widget.ConstraintLayout> |
@ -1,15 +1,31 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<TextView xmlns:android="http://schemas.android.com/apk/res/android" |
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:tools="http://schemas.android.com/tools" |
|||
android:id="@android:id/text1" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:background="?android:selectableItemBackground" |
|||
android:gravity="center_vertical" |
|||
android:minHeight="?android:attr/listPreferredItemHeightSmall" |
|||
android:paddingStart="?android:attr/listPreferredItemPaddingStart" |
|||
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" |
|||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" |
|||
android:paddingRight="?android:attr/listPreferredItemPaddingRight" |
|||
android:textAppearance="?android:attr/textAppearanceListItemSmall" |
|||
tools:viewBindingIgnore="true" /> |
|||
android:background="?attr/selectableItemBackground" |
|||
android:minHeight="56dp"> |
|||
|
|||
<ImageView |
|||
android:id="@+id/icon" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_gravity="center" |
|||
android:paddingStart="16dp" |
|||
android:paddingTop="8dp" |
|||
android:paddingEnd="16dp" |
|||
android:paddingBottom="8dp" |
|||
tools:ignore="ContentDescription" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/text" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_gravity="center_vertical" |
|||
android:maxLines="1" |
|||
android:paddingStart="16dp" |
|||
android:paddingEnd="16dp" |
|||
android:textAppearance="?attr/textAppearanceSubtitle1" |
|||
tools:text="Line line" /> |
|||
|
|||
</LinearLayout> |
@ -1,116 +1,65 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
<androidx.constraintlayout.widget.ConstraintLayout 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"> |
|||
|
|||
<RelativeLayout |
|||
android:id="@+id/footer" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="48dp" |
|||
android:layout_alignParentBottom="true"> |
|||
|
|||
<View |
|||
android:layout_width="match_parent" |
|||
android:layout_height="1dip" |
|||
android:layout_alignParentTop="true" |
|||
android:background="@android:color/darker_gray" /> |
|||
|
|||
<View |
|||
android:id="@+id/horizontalDivider" |
|||
android:layout_width="1dip" |
|||
android:layout_height="match_parent" |
|||
android:layout_alignParentTop="true" |
|||
android:layout_centerHorizontal="true" |
|||
android:layout_marginTop="4dp" |
|||
android:layout_marginBottom="4dp" |
|||
android:background="@android:color/darker_gray" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatButton |
|||
android:id="@+id/btnCancel" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_alignParentStart="true" |
|||
android:layout_alignParentLeft="true" |
|||
android:layout_alignParentTop="true" |
|||
android:layout_alignParentBottom="true" |
|||
android:layout_toStartOf="@id/horizontalDivider" |
|||
android:layout_toLeftOf="@id/horizontalDivider" |
|||
android:background="?android:selectableItemBackground" |
|||
android:text="@string/cancel" /> |
|||
|
|||
<androidx.appcompat.widget.AppCompatButton |
|||
android:id="@+id/btnConfirm" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_alignParentTop="true" |
|||
android:layout_alignParentEnd="true" |
|||
android:layout_alignParentRight="true" |
|||
android:layout_alignParentBottom="true" |
|||
android:layout_toEndOf="@id/horizontalDivider" |
|||
android:layout_toRightOf="@id/horizontalDivider" |
|||
android:background="?android:selectableItemBackground" |
|||
android:text="@string/confirm" /> |
|||
</RelativeLayout> |
|||
|
|||
<RelativeLayout |
|||
android:id="@+id/directoryInfo" |
|||
<com.google.android.material.appbar.AppBarLayout |
|||
android:id="@+id/appBarLayout" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_alignParentTop="true"> |
|||
app:layout_constraintBottom_toTopOf="@id/directoryList" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toTopOf="parent"> |
|||
|
|||
<ImageButton |
|||
android:id="@+id/btnNavUp" |
|||
android:layout_width="56dp" |
|||
android:layout_height="56dp" |
|||
android:layout_alignParentStart="true" |
|||
android:layout_alignParentLeft="true" |
|||
android:layout_alignParentTop="true" |
|||
android:background="?android:selectableItemBackground" |
|||
android:contentDescription="@string/nav_up" |
|||
android:padding="8dp" |
|||
android:scaleType="fitCenter" |
|||
app:srcCompat="@drawable/ic_arrow_upward_24" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/txtvSelectedFolderLabel" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_alignParentTop="true" |
|||
android:layout_marginLeft="8dp" |
|||
android:layout_marginTop="8dp" |
|||
android:layout_marginRight="8dp" |
|||
android:layout_toEndOf="@id/btnNavUp" |
|||
android:layout_toRightOf="@id/btnNavUp" |
|||
android:text="@string/selected_folder_label" |
|||
android:textStyle="bold" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/txtvSelectedFolder" |
|||
<com.google.android.material.appbar.MaterialToolbar |
|||
android:id="@+id/toolbar" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_below="@id/txtvSelectedFolderLabel" |
|||
android:layout_marginStart="8dp" |
|||
android:layout_marginLeft="8dp" |
|||
android:layout_marginTop="4dp" |
|||
android:layout_marginBottom="8dp" |
|||
android:layout_toEndOf="@id/btnNavUp" |
|||
android:layout_toRightOf="@id/btnNavUp" |
|||
android:ellipsize="start" |
|||
android:scrollHorizontally="true" |
|||
android:singleLine="true" /> |
|||
|
|||
<View |
|||
android:layout_width="match_parent" |
|||
android:layout_height="1dp" |
|||
android:layout_below="@id/btnNavUp" |
|||
android:background="@android:color/darker_gray" /> |
|||
</RelativeLayout> |
|||
app:navigationIcon="@drawable/ic_arrow_upward_24" |
|||
tools:title="/this/that/thy" /> |
|||
</com.google.android.material.appbar.AppBarLayout> |
|||
|
|||
<androidx.recyclerview.widget.RecyclerView |
|||
android:id="@+id/directoryList" |
|||
android:layout_width="match_parent" |
|||
android:layout_width="0dp" |
|||
android:layout_height="0dp" |
|||
android:layout_above="@id/footer" |
|||
android:layout_below="@id/directoryInfo" /> |
|||
</RelativeLayout> |
|||
app:layout_constraintBottom_toTopOf="@id/bottom_horizontal_divider" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/appBarLayout" /> |
|||
|
|||
<include |
|||
android:id="@+id/bottom_horizontal_divider" |
|||
layout="@layout/item_pref_divider" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="1dp" |
|||
app:layout_constraintBottom_toTopOf="@id/btnCancel" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/directoryList" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/btnCancel" |
|||
style="@style/Widget.MaterialComponents.Button.TextButton" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:text="@string/cancel" |
|||
app:icon="@drawable/ic_close_24" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/directoryList" /> |
|||
|
|||
<com.google.android.material.button.MaterialButton |
|||
android:id="@+id/btnConfirm" |
|||
style="@style/Widget.MaterialComponents.Button.TextButton" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:text="@string/confirm" |
|||
app:icon="@drawable/ic_check_24" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@id/directoryList" /> |
|||
</androidx.constraintlayout.widget.ConstraintLayout> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue