Merge pull request #12047 from nextcloud/feature/use-m3-SharePasswordDialogFragment

Use Material Design 3 share password dialog fragment
This commit is contained in:
Andy Scherzinger 2023-10-30 19:33:22 +01:00 committed by GitHub
commit c52bad82f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 250 additions and 227 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

View file

@ -1,227 +0,0 @@
/*
* ownCloud Android client application
*
* @author masensio
* @author Andy Scherzinger
* Copyright (C) 2015 ownCloud GmbH.
* Copyright (C) 2018 Andy Scherzinger
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.owncloud.android.ui.dialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.nextcloud.client.di.Injectable;
import com.owncloud.android.R;
import com.owncloud.android.databinding.PasswordDialogBinding;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.resources.shares.OCShare;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.KeyboardUtils;
import com.owncloud.android.utils.theme.ViewThemeUtils;
import javax.inject.Inject;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
/**
* Dialog to input the password for sharing a file/folder.
* <p>
* Triggers the share when the password is introduced.
*/
public class SharePasswordDialogFragment extends DialogFragment implements DialogInterface.OnClickListener, Injectable {
private static final String ARG_FILE = "FILE";
private static final String ARG_SHARE = "SHARE";
private static final String ARG_CREATE_SHARE = "CREATE_SHARE";
private static final String ARG_ASK_FOR_PASSWORD = "ASK_FOR_PASSWORD";
public static final String PASSWORD_FRAGMENT = "PASSWORD_FRAGMENT";
@Inject ViewThemeUtils viewThemeUtils;
@Inject KeyboardUtils keyboardUtils;
private PasswordDialogBinding binding;
private OCFile file;
private OCShare share;
private boolean createShare;
private boolean askForPassword;
@Override
public void onStart() {
super.onStart();
AlertDialog alertDialog = (AlertDialog) getDialog();
if (alertDialog != null) {
viewThemeUtils.platform.colorTextButtons(alertDialog.getButton(AlertDialog.BUTTON_POSITIVE),
alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE));
viewThemeUtils.platform.colorTextButtons(getResources().getColor(R.color.highlight_textColor_Warning),
alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL));
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
String password = binding.sharePassword.getText().toString();
if (!askForPassword && TextUtils.isEmpty(password)) {
DisplayUtils.showSnackMessage(binding.getRoot(), R.string.share_link_empty_password);
return;
}
if (share == null) {
setPassword(createShare, file, password);
} else {
setPassword(share, password);
}
alertDialog.dismiss();
});
}
}
@Override
public void onResume() {
super.onResume();
keyboardUtils.showKeyboardForEditText(requireDialog().getWindow(), binding.sharePassword);
}
/**
* Public factory method to create new SharePasswordDialogFragment instances.
*
* @param file OCFile bound to the public share that which password will be set or updated
* @param createShare When 'true', the request for password will be followed by the creation of a new public link;
* when 'false', a public share is assumed to exist, and the password is bound to it.
* @return Dialog ready to show.
*/
public static SharePasswordDialogFragment newInstance(OCFile file, boolean createShare, boolean askForPassword) {
SharePasswordDialogFragment frag = new SharePasswordDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_FILE, file);
args.putBoolean(ARG_CREATE_SHARE, createShare);
args.putBoolean(ARG_ASK_FOR_PASSWORD, askForPassword);
frag.setArguments(args);
return frag;
}
/**
* Public factory method to create new SharePasswordDialogFragment instances.
*
* @param share OCFile bound to the public share that which password will be set or updated
* @return Dialog ready to show.
*/
public static SharePasswordDialogFragment newInstance(OCShare share, boolean askForPassword) {
SharePasswordDialogFragment frag = new SharePasswordDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_SHARE, share);
args.putBoolean(ARG_ASK_FOR_PASSWORD, askForPassword);
frag.setArguments(args);
return frag;
}
/**
* Public factory method to create new SharePasswordDialogFragment instances.
*
* @param share OCFile bound to the public share that which password will be set or updated
* @return Dialog ready to show.
*/
public static SharePasswordDialogFragment newInstance(OCShare share) {
SharePasswordDialogFragment frag = new SharePasswordDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_SHARE, share);
frag.setArguments(args);
return frag;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
file = getArguments().getParcelable(ARG_FILE);
share = getArguments().getParcelable(ARG_SHARE);
createShare = getArguments().getBoolean(ARG_CREATE_SHARE, false);
askForPassword = getArguments().getBoolean(ARG_ASK_FOR_PASSWORD, false);
// Inflate the layout for the dialog
LayoutInflater inflater = requireActivity().getLayoutInflater();
binding = PasswordDialogBinding.inflate(inflater, null, false);
View view = binding.getRoot();
// Setup layout
binding.sharePassword.setText("");
viewThemeUtils.material.colorTextInputLayout(binding.sharePasswordContainer);
int negativeButtonCaption;
int title;
if (askForPassword) {
title = R.string.share_link_optional_password_title;
negativeButtonCaption = R.string.common_skip;
} else {
title = R.string.share_link_password_title;
negativeButtonCaption = R.string.common_cancel;
}
// Build the dialog
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(view.getContext());
builder.setView(view)
.setPositiveButton(R.string.common_ok, null)
.setNegativeButton(negativeButtonCaption, this)
.setNeutralButton(R.string.common_delete, this)
.setTitle(title);
viewThemeUtils.dialog.colorMaterialAlertDialogBackground(view.getContext(), builder);
return builder.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == AlertDialog.BUTTON_NEUTRAL) {
if (share == null) {
setPassword(createShare, file, null);
} else {
setPassword(share, null);
}
} else if (which == AlertDialog.BUTTON_NEGATIVE && askForPassword) {
if (share == null) {
setPassword(createShare, file, null);
} else {
setPassword(share, null);
}
}
}
private void setPassword(boolean createShare, OCFile file, String password) {
if (createShare) {
((FileActivity) getActivity()).getFileOperationsHelper().shareFileViaPublicShare(file, password);
} else {
((FileActivity) getActivity()).getFileOperationsHelper().setPasswordToShare(share, password);
}
}
private void setPassword(OCShare share, String password) {
((FileActivity) getActivity()).getFileOperationsHelper().setPasswordToShare(share, password);
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}

View file

@ -0,0 +1,250 @@
/*
* ownCloud Android client application
*
* @author masensio
* @author Andy Scherzinger
* Copyright (C) 2015 ownCloud GmbH.
* Copyright (C) 2018 Andy Scherzinger
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.owncloud.android.ui.dialog
import android.app.Dialog
import android.content.DialogInterface
import android.os.Build
import android.os.Bundle
import android.text.TextUtils
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import androidx.fragment.app.DialogFragment
import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.nextcloud.client.di.Injectable
import com.owncloud.android.R
import com.owncloud.android.databinding.PasswordDialogBinding
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.lib.resources.shares.OCShare
import com.owncloud.android.ui.activity.FileActivity
import com.owncloud.android.utils.DisplayUtils
import com.owncloud.android.utils.KeyboardUtils
import com.owncloud.android.utils.theme.ViewThemeUtils
import javax.inject.Inject
/**
* Dialog to input the password for sharing a file/folder.
*
*
* Triggers the share when the password is introduced.
*/
class SharePasswordDialogFragment : DialogFragment(), Injectable {
@JvmField
@Inject
var viewThemeUtils: ViewThemeUtils? = null
@JvmField
@Inject
var keyboardUtils: KeyboardUtils? = null
private var binding: PasswordDialogBinding? = null
private var file: OCFile? = null
private var share: OCShare? = null
private var createShare = false
private var askForPassword = false
override fun onStart() {
super.onStart()
val alertDialog = dialog as AlertDialog?
if (alertDialog != null) {
val positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) as MaterialButton?
if (positiveButton != null) {
viewThemeUtils?.material?.colorMaterialButtonPrimaryTonal(positiveButton)
positiveButton.setOnClickListener {
val sharePassword = binding?.sharePassword?.text
if (sharePassword != null) {
val password = sharePassword.toString()
if (!askForPassword && TextUtils.isEmpty(password)) {
DisplayUtils.showSnackMessage(binding?.root, R.string.share_link_empty_password)
return@setOnClickListener
}
if (share == null) {
setPassword(createShare, file, password)
} else {
setPassword(share!!, password)
}
}
alertDialog.dismiss()
}
}
val negativeButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE) as MaterialButton?
if (negativeButton != null) {
viewThemeUtils?.material?.colorMaterialButtonPrimaryBorderless(negativeButton)
}
val neutralButton = alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL) as MaterialButton?
if (neutralButton != null) {
val warningColorId = ContextCompat.getColor(requireContext(), R.color.highlight_textColor_Warning)
viewThemeUtils?.platform?.colorTextButtons(warningColorId, neutralButton)
}
}
}
override fun onResume() {
super.onResume()
keyboardUtils?.showKeyboardForEditText(requireDialog().window, binding!!.sharePassword)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
file = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requireArguments().getParcelable(ARG_FILE, OCFile::class.java)
} else {
@Suppress("DEPRECATION")
requireArguments().getParcelable(ARG_FILE)
}
share = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requireArguments().getParcelable(ARG_SHARE, OCShare::class.java)
} else {
@Suppress("DEPRECATION")
requireArguments().getParcelable(ARG_SHARE)
}
createShare = requireArguments().getBoolean(ARG_CREATE_SHARE, false)
askForPassword = requireArguments().getBoolean(ARG_ASK_FOR_PASSWORD, false)
// Inflate the layout for the dialog
val inflater = requireActivity().layoutInflater
binding = PasswordDialogBinding.inflate(inflater, null, false)
// Setup layout
binding?.sharePassword?.setText("")
viewThemeUtils?.material?.colorTextInputLayout(binding!!.sharePasswordContainer)
val neutralButtonTextId: Int
val title: Int
if (askForPassword) {
title = R.string.share_link_optional_password_title
neutralButtonTextId = R.string.common_skip
} else {
title = R.string.share_link_password_title
neutralButtonTextId = R.string.common_cancel
}
// Build the dialog
val builder = MaterialAlertDialogBuilder(requireContext())
builder.setView(binding!!.root)
.setPositiveButton(R.string.common_ok, null)
.setNegativeButton(R.string.common_delete) { _: DialogInterface?, _: Int -> callSetPassword() }
.setNeutralButton(neutralButtonTextId) { _: DialogInterface?, _: Int ->
if (askForPassword) {
callSetPassword()
}
}
.setTitle(title)
viewThemeUtils?.dialog?.colorMaterialAlertDialogBackground(requireContext(), builder)
return builder.create()
}
private fun callSetPassword() {
if (share == null) {
setPassword(createShare, file, null)
} else {
setPassword(share!!, null)
}
}
private fun setPassword(createShare: Boolean, file: OCFile?, password: String?) {
val fileOperationsHelper = (requireActivity() as FileActivity).fileOperationsHelper ?: return
if (createShare) {
fileOperationsHelper.shareFileViaPublicShare(file, password)
} else {
fileOperationsHelper.setPasswordToShare(share, password)
}
}
private fun setPassword(share: OCShare, password: String?) {
val fileOperationsHelper = (requireActivity() as FileActivity).fileOperationsHelper ?: return
fileOperationsHelper.setPasswordToShare(share, password)
}
override fun onDestroyView() {
super.onDestroyView()
binding = null
}
companion object {
private const val ARG_FILE = "FILE"
private const val ARG_SHARE = "SHARE"
private const val ARG_CREATE_SHARE = "CREATE_SHARE"
private const val ARG_ASK_FOR_PASSWORD = "ASK_FOR_PASSWORD"
const val PASSWORD_FRAGMENT = "PASSWORD_FRAGMENT"
/**
* Public factory method to create new SharePasswordDialogFragment instances.
*
* @param file OCFile bound to the public share that which
* password will be set or updated
* @param createShare When 'true', the request for password will be
* followed by the creation of a new public link
* when 'false', a public share is assumed to exist, and the password is bound to it.
* @return Dialog ready to show.
*/
@JvmStatic
fun newInstance(file: OCFile?, createShare: Boolean, askForPassword: Boolean): SharePasswordDialogFragment {
val frag = SharePasswordDialogFragment()
val args = Bundle()
args.putParcelable(ARG_FILE, file)
args.putBoolean(ARG_CREATE_SHARE, createShare)
args.putBoolean(ARG_ASK_FOR_PASSWORD, askForPassword)
frag.arguments = args
return frag
}
/**
* Public factory method to create new SharePasswordDialogFragment instances.
*
* @param share OCFile bound to the public share that which password will be set or updated
* @return Dialog ready to show.
*/
@JvmStatic
fun newInstance(share: OCShare?, askForPassword: Boolean): SharePasswordDialogFragment {
val frag = SharePasswordDialogFragment()
val args = Bundle()
args.putParcelable(ARG_SHARE, share)
args.putBoolean(ARG_ASK_FOR_PASSWORD, askForPassword)
frag.arguments = args
return frag
}
/**
* Public factory method to create new SharePasswordDialogFragment instances.
*
* @param share OCFile bound to the public share that which password will be set or updated
* @return Dialog ready to show.
*/
fun newInstance(share: OCShare?): SharePasswordDialogFragment {
val frag = SharePasswordDialogFragment()
val args = Bundle()
args.putParcelable(ARG_SHARE, share)
frag.arguments = args
return frag
}
}
}