Merge remote-tracking branch 'origin/master' into dev

This commit is contained in:
Tobias Kaminsky 2023-10-19 03:36:31 +02:00
commit 8f6425604c
22 changed files with 139 additions and 914 deletions

View file

@ -33,7 +33,7 @@ jobs:
echo "pr=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT"
echo "repo=${{ github.event.pull_request.head.repo.full_name }}" >> "$GITHUB_OUTPUT"
fi
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
repository: ${{ steps.get-vars.outputs.repo }}
ref: ${{ steps.get-vars.outputs.branch }}

View file

@ -26,7 +26,7 @@ jobs:
language: [ 'java' ]
steps:
- name: Checkout repository
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set Swap Space
uses: pierotofy/set-swap-space@49819abfb41bd9b44fb781159c033dba90353a7c # v1.0
with:

View file

@ -18,5 +18,5 @@ jobs:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: gradle/wrapper-validation-action@56b90f209b02bf6d1deae490e9ef18b21a389cd4 # v1.1.0

View file

@ -24,7 +24,7 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false

View file

@ -15,11 +15,17 @@ height="80">](https://f-droid.org/packages/com.nextcloud.client/)
## How to contribute :rocket:
If you want to [contribute](https://nextcloud.com/contribute/) to Nextcloud, you are very welcome:
If you want to [contribute](https://nextcloud.com/contribute/) to the Nextcloud Android client app, there are many ways to help whether or not you are a coder:
* our forum at https://help.nextcloud.com
* for translations of the app on [Transifex](https://app.transifex.com/nextcloud/nextcloud/android/)
* opening issues and PRs (including a corresponding issue)
* helping out other users on our forum at https://help.nextcloud.com
* providing translations of the app on [Transifex](https://app.transifex.com/nextcloud/nextcloud/android/)
* reporting problems / suggesting enhancements by [opening new issues](https://github.com/nextcloud/android/issues/new/choose)
* implementing proposed bug fixes and enhancement ideas by submitting PRs (associated with a corresponding issue preferably)
* reviewing [pull requests](https://github.com/nextcloud/android/pulls) and providing feedback on code, implementation, and functionality
* installing and testing [pull request builds](https://github.com/nextcloud/android/pulls), [daily/dev builds](https://github.com/nextcloud/android#development-version-hammer), or [RCs/release candidate builds](https://github.com/nextcloud/android/releases)
* enhancing Admin, User, or Developer [documentation](https://github.com/nextcloud/documentation/)
* hitting hard on the latest stable release by testing fundamental features and evaluating the user experience
* proactively getting familiar with [how to gather debug logs](https://github.com/nextcloud/android#getting-debug-info-via-logcat-mag) from your devices (so that you are prepared to provide a detailed report if you encounter a problem with the app in the future)
## Contribution Guidelines & License :scroll:
@ -38,7 +44,7 @@ More information on how to contribute: <https://nextcloud.com/contribute/>
## Start contributing :hammer\_and\_wrench:
Make sure you read [SETUP.md](https://github.com/nextcloud/android/blob/master/SETUP.md) and [CONTRIBUTING.md](https://github.com/nextcloud/android/blob/master/CONTRIBUTING.md) before you start working on this project. But basically: fork this repository and contribute back using pull requests to the master branch.
Easy starting points are also reviewing [pull requests](https://github.com/nextcloud/android/pulls) and working on [starter issues](https://github.com/nextcloud/android/issues?q=is%3Aopen+is%3Aissue+label%3A%22starter+issue%22).
Easy starting points are also reviewing [pull requests](https://github.com/nextcloud/android/pulls) and working on [starter issues](https://github.com/nextcloud/android/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
### Getting debug info via logcat :mag:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -25,6 +25,7 @@ package com.owncloud.android.ui.dialog;
import android.app.Dialog;
import android.os.Bundle;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.nextcloud.client.account.User;
import com.nextcloud.client.di.Injectable;
@ -59,7 +60,11 @@ public class AccountRemovalConfirmationDialog extends DialogFragment implements
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
user = getArguments().getParcelable(KEY_USER);
Bundle arguments = getArguments();
if (arguments != null) {
user = arguments.getParcelable(KEY_USER);
}
}
@Override
@ -67,9 +72,18 @@ public class AccountRemovalConfirmationDialog extends DialogFragment implements
super.onStart();
AlertDialog alertDialog = (AlertDialog) getDialog();
if (alertDialog != null) {
viewThemeUtils.platform.colorTextButtons(alertDialog.getButton(AlertDialog.BUTTON_POSITIVE),
alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL));
MaterialButton positiveButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
if (positiveButton != null) {
viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton);
}
MaterialButton negativeButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
if (negativeButton != null) {
viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(negativeButton);
}
}
}
@NonNull
@ -82,7 +96,7 @@ public class AccountRemovalConfirmationDialog extends DialogFragment implements
.setPositiveButton(R.string.common_ok,
(dialogInterface, i) -> backgroundJobManager.startAccountRemovalJob(user.getAccountName(),
false))
.setNeutralButton(R.string.common_cancel, null);
.setNegativeButton(R.string.common_cancel, null);
viewThemeUtils.dialog.colorMaterialAlertDialogBackground(requireActivity(), builder);

View file

@ -35,6 +35,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.common.collect.Sets;
import com.nextcloud.client.account.CurrentAccountProvider;
@ -99,7 +100,7 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
private RichDocumentsTemplateAdapter adapter;
private OCFile parentFolder;
private OwnCloudClient client;
private Button positiveButton;
private MaterialButton positiveButton;
private DialogFragment waitDialog;
public enum Type {
@ -126,11 +127,18 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
AlertDialog alertDialog = (AlertDialog) getDialog();
positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
viewThemeUtils.platform.colorTextButtons(positiveButton,
alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL));
positiveButton.setOnClickListener(this);
positiveButton.setEnabled(false);
if (alertDialog != null) {
positiveButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton);
MaterialButton negativeButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
if (negativeButton != null) {
viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(negativeButton);
}
positiveButton.setOnClickListener(this);
positiveButton.setEnabled(false);
}
checkEnablingCreateButton();
}
@ -205,12 +213,14 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
}
});
int titleTextId = getTitle(type);
// Build the dialog
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity);
builder.setView(view)
.setPositiveButton(R.string.create, null)
.setNeutralButton(R.string.common_cancel, null)
.setTitle(getTitle(type));
.setNegativeButton(R.string.common_cancel, null)
.setTitle(titleTextId);
viewThemeUtils.dialog.colorMaterialAlertDialogBackground(activity, builder);

View file

@ -30,6 +30,7 @@ import android.os.Bundle;
import android.text.format.DateUtils;
import android.widget.DatePicker;
import com.google.android.material.button.MaterialButton;
import com.nextcloud.client.di.Injectable;
import com.owncloud.android.R;
import com.owncloud.android.utils.theme.ViewThemeUtils;
@ -81,12 +82,24 @@ public class ExpirationDatePickerDialogFragment
public void onStart() {
super.onStart();
final Dialog currentDialog = getDialog();
if (currentDialog != null) {
final DatePickerDialog dialog = (DatePickerDialog) currentDialog;
viewThemeUtils.platform.colorTextButtons(dialog.getButton(DatePickerDialog.BUTTON_NEUTRAL),
dialog.getButton(DatePickerDialog.BUTTON_NEGATIVE),
dialog.getButton(DatePickerDialog.BUTTON_POSITIVE));
MaterialButton positiveButton = (MaterialButton) dialog.getButton(DatePickerDialog.BUTTON_POSITIVE);
if (positiveButton != null) {
viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton);
}
MaterialButton negativeButton = (MaterialButton) dialog.getButton(DatePickerDialog.BUTTON_NEGATIVE);
if (negativeButton != null) {
viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(negativeButton);
}
MaterialButton neutralButton = (MaterialButton) dialog.getButton(DatePickerDialog.BUTTON_NEUTRAL);
if (neutralButton != null) {
viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(neutralButton);
}
}
}
@ -118,7 +131,7 @@ public class ExpirationDatePickerDialogFragment
//show unset button only when date is already selected
if (chosenDateInMillis > 0) {
dialog.setButton(
Dialog.BUTTON_NEUTRAL,
Dialog.BUTTON_NEGATIVE,
getText(R.string.share_via_link_unset_password),
(dialog1, which) -> {
if (onExpiryDateListener != null) {

View file

@ -23,6 +23,7 @@ import android.app.Dialog;
import android.os.Bundle;
import android.view.ActionMode;
import com.google.android.material.button.MaterialButton;
import com.nextcloud.client.di.Injectable;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.OCFile;
@ -96,15 +97,20 @@ public class RemoveFilesDialogFragment extends ConfirmationDialogFragment implem
R.string.confirmation_remove_files_alert;
}
int localRemoveButton = (containsFolder || containsDown) ? R.string.confirmation_remove_local : -1;
args.putInt(ARG_MESSAGE_RESOURCE_ID, messageStringId);
if (files.size() == SINGLE_SELECTION) {
args.putStringArray(ARG_MESSAGE_ARGUMENTS, new String[]{files.get(0).getFileName()});
args.putStringArray(ARG_MESSAGE_ARGUMENTS, new String[] { files.get(0).getFileName() } );
}
args.putInt(ARG_POSITIVE_BTN_RES, R.string.file_delete);
args.putInt(ARG_NEUTRAL_BTN_RES, R.string.file_keep);
args.putInt(ARG_NEGATIVE_BTN_RES, localRemoveButton);
if (containsFolder || containsDown) {
args.putInt(ARG_NEGATIVE_BTN_RES, R.string.confirmation_remove_local);
args.putInt(ARG_NEUTRAL_BTN_RES, R.string.file_keep);
} else {
args.putInt(ARG_NEGATIVE_BTN_RES, R.string.file_keep);
}
args.putParcelableArrayList(ARG_TARGET_FILES, files);
frag.setArguments(args);
@ -131,9 +137,16 @@ public class RemoveFilesDialogFragment extends ConfirmationDialogFragment implem
AlertDialog alertDialog = (AlertDialog) getDialog();
if (alertDialog != null) {
viewThemeUtils.platform.colorTextButtons(alertDialog.getButton(AlertDialog.BUTTON_POSITIVE),
alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE),
alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL));
MaterialButton positiveButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton);
MaterialButton negativeButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(negativeButton);
MaterialButton neutralButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL);
if (neutralButton != null) {
viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(neutralButton);
}
}
}
@ -141,10 +154,14 @@ public class RemoveFilesDialogFragment extends ConfirmationDialogFragment implem
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
mTargetFiles = getArguments().getParcelableArrayList(ARG_TARGET_FILES);
Bundle arguments = getArguments();
if (arguments == null) {
return dialog;
}
mTargetFiles = arguments.getParcelableArrayList(ARG_TARGET_FILES);
setOnConfirmationListener(this);
return dialog;
}
@ -154,9 +171,7 @@ public class RemoveFilesDialogFragment extends ConfirmationDialogFragment implem
*/
@Override
public void onConfirmation(String callerTag) {
ComponentsGetter cg = (ComponentsGetter) getActivity();
cg.getFileOperationsHelper().removeFiles(mTargetFiles, false, false);
finishActionMode();
removeFiles(false);
}
/**
@ -164,8 +179,14 @@ public class RemoveFilesDialogFragment extends ConfirmationDialogFragment implem
*/
@Override
public void onCancel(String callerTag) {
removeFiles(true);
}
private void removeFiles(boolean onlyLocalCopy) {
ComponentsGetter cg = (ComponentsGetter) getActivity();
cg.getFileOperationsHelper().removeFiles(mTargetFiles, true, false);
if (cg != null) {
cg.getFileOperationsHelper().removeFiles(mTargetFiles, onlyLocalCopy, false);
}
finishActionMode();
}

View file

@ -28,6 +28,7 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
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;
@ -52,8 +53,6 @@ public class RenamePublicShareDialogFragment
private static final String ARG_PUBLIC_SHARE = "PUBLIC_SHARE";
public static final String RENAME_PUBLIC_SHARE_FRAGMENT = "RENAME_PUBLIC_SHARE_FRAGMENT";
@Inject ViewThemeUtils viewThemeUtils;
@Inject KeyboardUtils keyboardUtils;
@ -75,8 +74,10 @@ public class RenamePublicShareDialogFragment
AlertDialog alertDialog = (AlertDialog) getDialog();
if (alertDialog != null) {
viewThemeUtils.platform.colorTextButtons(alertDialog.getButton(AlertDialog.BUTTON_POSITIVE),
alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL));
MaterialButton positiveButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
MaterialButton negativeButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton);
viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(negativeButton);
}
}
@ -104,7 +105,7 @@ public class RenamePublicShareDialogFragment
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(view.getContext());
builder.setView(view)
.setPositiveButton(R.string.file_rename, this)
.setNeutralButton(R.string.common_cancel, this)
.setNegativeButton(R.string.common_cancel, this)
.setTitle(R.string.public_share_name);
viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.userInput.getContext(), builder);

View file

@ -1,329 +0,0 @@
/**
* ownCloud Android client application
*
* @author David A. Velasco
* Copyright (C) 2015 ownCloud Inc.
*
* 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.Context;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import com.owncloud.android.R;
import com.owncloud.android.databinding.SslValidatorLayoutBinding;
import com.owncloud.android.lib.common.network.CertificateCombinedException;
import com.owncloud.android.lib.common.network.NetworkUtils;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.x500.X500Principal;
/**
* Dialog to request the user about a certificate that could not be validated with the certificates store in the system.
*/
public class SslValidatorDialog extends Dialog {
private final static String TAG = SslValidatorDialog.class.getSimpleName();
private OnSslValidatorListener mListener;
private CertificateCombinedException mException;
private SslValidatorLayoutBinding binding;
/**
* Creates a new SslValidatorDialog to ask the user if an untrusted certificate from a server should
* be trusted.
*
* @param context Android context where the dialog will live.
* @param result Result of a failed remote operation.
* @param listener Object to notice when the server certificate was added to the local certificates store.
* @return A new SslValidatorDialog instance. NULL if the operation can not be recovered
* by setting the certificate as reliable.
*/
public static SslValidatorDialog newInstance(Context context, RemoteOperationResult result, OnSslValidatorListener listener) {
if (result != null && result.isSslRecoverableException()) {
return new SslValidatorDialog(context, listener);
} else {
return null;
}
}
/**
* Private constructor.
*
* Instances have to be created through static {@link SslValidatorDialog#newInstance}.
*
* @param context Android context where the dialog will live
* @param listener Object to notice when the server certificate was added to the local certificates store.
*/
private SslValidatorDialog(Context context, OnSslValidatorListener listener) {
super(context);
mListener = listener;
}
/**
* {@inheritDoc}
*/
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
binding = SslValidatorLayoutBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.ok.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
saveServerCert();
dismiss();
if (mListener != null) {
mListener.onSavedCertificate();
} else {
Log_OC.d(TAG, "Nobody there to notify the certificate was saved");
}
} catch (GeneralSecurityException | IOException e) {
dismiss();
if (mListener != null) {
mListener.onFailedSavingCertificate();
}
Log_OC.e(TAG, "Server certificate could not be saved in the known servers trust store ", e);
}
}
});
binding.cancel.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
cancel();
}
});
binding.detailsBtn.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
View detailsScroll = findViewById(R.id.details_scroll);
if (detailsScroll.getVisibility() == View.VISIBLE) {
detailsScroll.setVisibility(View.GONE);
((Button) v).setText(R.string.ssl_validator_btn_details_see);
} else {
detailsScroll.setVisibility(View.VISIBLE);
((Button) v).setText(R.string.ssl_validator_btn_details_hide);
}
}
});
}
public void updateResult(RemoteOperationResult result) {
if (result.isSslRecoverableException()) {
mException = (CertificateCombinedException) result.getException();
/// clean
binding.reasonCertNotTrusted.setVisibility(View.GONE);
binding.reasonCertExpired.setVisibility(View.GONE);
binding.reasonCertNotYetValid.setVisibility(View.GONE);
binding.reasonHostnameNotVerified.setVisibility(View.GONE);
binding.detailsScroll.setVisibility(View.GONE);
/// refresh
if (mException.getCertPathValidatorException() != null) {
binding.reasonCertNotTrusted.setVisibility(View.VISIBLE);
}
if (mException.getCertificateExpiredException() != null) {
binding.reasonCertExpired.setVisibility(View.VISIBLE);
}
if (mException.getCertificateNotYetValidException() != null) {
binding.reasonCertNotYetValid.setVisibility(View.VISIBLE);
}
if (mException.getSslPeerUnverifiedException() != null ) {
binding.reasonHostnameNotVerified.setVisibility(View.VISIBLE);
}
showCertificateData(mException.getServerCertificate());
}
}
private void showCertificateData(X509Certificate cert) {
if (cert != null) {
showSubject(cert.getSubjectX500Principal());
showIssuer(cert.getIssuerX500Principal());
showValidity(cert.getNotBefore(), cert.getNotAfter());
showSignature(cert);
} else {
// this should not happen, TODO
Log_OC.d("certNull", "This should not happen");
}
}
private void showSignature(X509Certificate cert) {
binding.valueSignature.setText(getHex(cert.getSignature()));
binding.valueSignatureAlgorithm.setText(cert.getSigAlgName());
}
public String getHex(final byte [] raw) {
if (raw == null) {
return null;
}
final StringBuilder hex = new StringBuilder(2 * raw.length);
for (final byte b : raw) {
final int hiVal = (b & 0xF0) >> 4;
final int loVal = b & 0x0F;
hex.append((char) ('0' + (hiVal + (hiVal / 10 * 7))));
hex.append((char) ('0' + (loVal + (loVal / 10 * 7))));
}
return hex.toString();
}
@SuppressWarnings("deprecation")
private void showValidity(Date notBefore, Date notAfter) {
binding.valueValidityFrom.setText(notBefore.toLocaleString());
binding.valueValidityTo.setText(notAfter.toLocaleString());
}
private void showSubject(X500Principal subject) {
Map<String, String> s = parsePrincipal(subject);
if (s.get("CN") != null) {
binding.valueSubjectCN.setText(s.get("CN"));
binding.valueSubjectCN.setVisibility(View.VISIBLE);
} else {
binding.valueSubjectCN.setVisibility(View.GONE);
}
if (s.get("O") != null) {
binding.valueSubjectO.setText(s.get("O"));
binding.valueSubjectO.setVisibility(View.VISIBLE);
} else {
binding.valueSubjectO.setVisibility(View.GONE);
}
if (s.get("OU") != null) {
binding.valueSubjectOU.setText(s.get("OU"));
binding.valueSubjectOU.setVisibility(View.VISIBLE);
} else {
binding.valueSubjectOU.setVisibility(View.GONE);
}
if (s.get("C") != null) {
binding.valueSubjectC.setText(s.get("C"));
binding.valueSubjectC.setVisibility(View.VISIBLE);
} else {
binding.valueSubjectC.setVisibility(View.GONE);
}
if (s.get("ST") != null) {
binding.valueSubjectST.setText(s.get("ST"));
binding.valueSubjectST.setVisibility(View.VISIBLE);
} else {
binding.valueSubjectST.setVisibility(View.GONE);
}
if (s.get("L") != null) {
binding.valueSubjectL.setText(s.get("L"));
binding.valueSubjectL.setVisibility(View.VISIBLE);
} else {
binding.valueSubjectL.setVisibility(View.GONE);
}
}
private void showIssuer(X500Principal issuer) {
Map<String, String> s = parsePrincipal(issuer);
if (s.get("CN") != null) {
binding.valueIssuerCN.setText(s.get("CN"));
binding.valueIssuerCN.setVisibility(View.VISIBLE);
} else {
binding.valueIssuerCN.setVisibility(View.GONE);
}
if (s.get("O") != null) {
binding.valueIssuerO.setText(s.get("O"));
binding.valueIssuerO.setVisibility(View.VISIBLE);
} else {
binding.valueIssuerO.setVisibility(View.GONE);
}
if (s.get("OU") != null) {
binding.valueIssuerOU.setText(s.get("OU"));
binding.valueIssuerOU.setVisibility(View.VISIBLE);
} else {
binding.valueIssuerOU.setVisibility(View.GONE);
}
if (s.get("C") != null) {
binding.valueIssuerC.setText(s.get("C"));
binding.valueIssuerC.setVisibility(View.VISIBLE);
} else {
binding.valueIssuerC.setVisibility(View.GONE);
}
if (s.get("ST") != null) {
binding.valueIssuerST.setText(s.get("ST"));
binding.valueIssuerST.setVisibility(View.VISIBLE);
} else {
binding.valueIssuerST.setVisibility(View.GONE);
}
if (s.get("L") != null) {
binding.valueIssuerL.setText(s.get("L"));
binding.valueIssuerL.setVisibility(View.VISIBLE);
} else {
binding.valueIssuerL.setVisibility(View.GONE);
}
}
private Map<String, String> parsePrincipal(X500Principal principal) {
Map<String, String> result = new HashMap<>();
String toParse = principal.getName();
String[] pieces = toParse.split(",");
String[] tokens = {"CN", "O", "OU", "C", "ST", "L"};
for (String piece : pieces) {
for (String token : tokens) {
if (piece.startsWith(token + "=")) {
result.put(token, piece.substring(token.length() + 1));
}
}
}
return result;
}
private void saveServerCert() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
if (mException.getServerCertificate() != null) {
// TODO make this asynchronously, it can take some time
NetworkUtils.addCertToKnownServersStore(mException.getServerCertificate(), getContext());
}
}
public interface OnSslValidatorListener {
public void onSavedCertificate();
public void onFailedSavingCertificate();
}
}

View file

@ -24,15 +24,14 @@ import android.app.Dialog
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.core.os.bundleOf
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.StoragePermissionDialogBinding
import com.owncloud.android.utils.theme.ViewThemeUtils
import kotlinx.parcelize.Parcelize
import javax.inject.Inject
@ -43,10 +42,7 @@ import javax.inject.Inject
* Allows choosing "full access" (MANAGE_ALL_FILES) or "read-only media" (READ_EXTERNAL_STORAGE)
*/
@RequiresApi(Build.VERSION_CODES.R)
class StoragePermissionDialogFragment :
DialogFragment(), Injectable {
private lateinit var binding: StoragePermissionDialogBinding
class StoragePermissionDialogFragment : DialogFragment(), Injectable {
private var permissionRequired = false
@ -64,51 +60,48 @@ class StoragePermissionDialogFragment :
super.onStart()
dialog?.let {
val alertDialog = it as AlertDialog
viewThemeUtils.platform.colorTextButtons(alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE))
val positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) as MaterialButton
viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton)
val negativeButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE) as MaterialButton
viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(negativeButton)
val neutralButton = alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL) as MaterialButton
viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(neutralButton)
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
// Inflate the layout for the dialog
val inflater = requireActivity().layoutInflater
binding = StoragePermissionDialogBinding.inflate(inflater, null, false)
val view: View = binding.root
val title = when {
permissionRequired -> R.string.file_management_permission
else -> R.string.file_management_permission_optional
}
val explanationResource = when {
permissionRequired -> R.string.file_management_permission_text
else -> R.string.file_management_permission_optional_text
}
binding.storagePermissionExplanation.text = getString(explanationResource, getString(R.string.app_name))
val message = getString(explanationResource, getString(R.string.app_name))
// Setup layout
viewThemeUtils.material.colorMaterialButtonPrimaryFilled(binding.btnFullAccess)
binding.btnFullAccess.setOnClickListener {
setResult(Result.FULL_ACCESS)
dismiss()
}
viewThemeUtils.platform.colorTextButtons(binding.btnReadOnly)
binding.btnReadOnly.setOnClickListener {
setResult(Result.MEDIA_READ_ONLY)
dismiss()
}
// Build the dialog
val titleResource = when {
permissionRequired -> R.string.file_management_permission
else -> R.string.file_management_permission_optional
}
val builder = MaterialAlertDialogBuilder(binding.btnReadOnly.context)
.setTitle(titleResource)
.setView(view)
.setNegativeButton(R.string.common_cancel) { _, _ ->
val dialogBuilder = MaterialAlertDialogBuilder(requireContext())
.setTitle(title)
.setMessage(message)
.setPositiveButton(R.string.storage_permission_full_access) { _, _ ->
setResult(Result.FULL_ACCESS)
dismiss()
}
.setNegativeButton(R.string.storage_permission_media_read_only) { _, _ ->
setResult(Result.MEDIA_READ_ONLY)
dismiss()
}
.setNeutralButton(R.string.common_cancel) { _, _ ->
setResult(Result.CANCEL)
dismiss()
}
viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.btnReadOnly.context, builder)
viewThemeUtils.dialog.colorMaterialAlertDialogBackground(requireContext(), dialogBuilder)
return builder.create()
return dialogBuilder.create()
}
private fun setResult(result: Result) {

View file

@ -1,443 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
ownCloud Android client application
Copyright (C) 2015 ownCloud Inc.
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/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/standard_padding">
<TextView
android:id="@+id/header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ssl_validator_header"
android:paddingBottom="@dimen/standard_padding"
android:textStyle="bold"
android:textColor="@color/text_color"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/reason_cert_not_trusted"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:paddingStart="@dimen/standard_half_padding"
android:paddingEnd="@dimen/zero"
android:text="@string/ssl_validator_reason_cert_not_trusted"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/reason_cert_expired"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:paddingStart="@dimen/standard_half_padding"
android:paddingEnd="@dimen/zero"
android:text="@string/ssl_validator_reason_cert_expired"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/reason_cert_not_yet_valid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:paddingStart="@dimen/standard_half_padding"
android:paddingEnd="@dimen/zero"
android:text="@string/ssl_validator_reason_cert_not_yet_valid"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/reason_hostname_not_verified"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:paddingStart="@dimen/standard_half_padding"
android:paddingEnd="@dimen/zero"
android:text="@string/ssl_validator_reason_hostname_not_verified"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<ScrollView
android:id="@+id/details_scroll"
android:visibility="gone"
android:padding="@dimen/standard_half_padding"
android:layout_width="wrap_content"
android:layout_height="@dimen/scroll_view_height">
<LinearLayout
android:id="@+id/details_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start"
android:orientation="vertical" >
<TextView
android:id="@+id/label_subject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text="@string/ssl_validator_label_subject"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<TextView
android:id="@+id/label_subject_CN"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ssl_validator_label_CN"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/value_subject_CN"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/label_subject_O"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ssl_validator_label_O"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/value_subject_O"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/label_subject_OU"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ssl_validator_label_OU"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/value_subject_OU"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/label_subject_ST"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ssl_validator_label_ST"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/value_subject_ST"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/label_subject_C"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ssl_validator_label_C"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/value_subject_C"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/label_subject_L"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ssl_validator_label_L"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/value_subject_L"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/label_issuer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text="@string/ssl_validator_label_issuer"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<TextView
android:id="@+id/label_issuer_CN"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ssl_validator_label_CN"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/value_issuer_CN"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/label_issuer_O"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ssl_validator_label_O"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/value_issuer_O"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/label_issuer_OU"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ssl_validator_label_OU"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/value_issuer_OU"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/label_issuer_ST"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ssl_validator_label_ST"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/value_issuer_ST"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/label_issuer_C"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ssl_validator_label_C"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/value_issuer_C"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/label_issuer_L"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ssl_validator_label_L"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/value_issuer_L"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/label_validity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text="@string/ssl_validator_label_validity"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<TextView
android:id="@+id/label_validity_from"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ssl_validator_label_validity_from"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/value_validity_from"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/label_validity_to"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ssl_validator_label_validity_to"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/value_validity_to"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/label_signature"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text="@string/ssl_validator_label_signature"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<TextView
android:id="@+id/label_signature_algorithm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ssl_validator_label_signature_algorithm"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/value_signature_algorithm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall"
/>
<TextView
android:id="@+id/value_signature"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/alternate_half_padding"
android:text=""
android:textAppearance="?android:attr/textAppearanceSmall"
/>
</LinearLayout>
</ScrollView>
<TextView
android:id="@+id/question"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="@dimen/standard_padding"
android:text="@string/ssl_validator_question"
android:textAppearance="?android:attr/textAppearanceMedium"
>
</TextView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center" >
<com.google.android.material.button.MaterialButton
android:id="@+id/cancel"
style="@style/Button.Borderless"
android:layout_width="@dimen/zero"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/common_no" />
<com.google.android.material.button.MaterialButton
android:id="@+id/details_btn"
style="@style/Button.Borderless"
android:layout_width="@dimen/zero"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="@string/ssl_validator_btn_details_see" />
<com.google.android.material.button.MaterialButton
android:id="@+id/ok"
style="@style/Button.Borderless"
android:layout_width="@dimen/zero"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/common_yes" />
</LinearLayout>
</LinearLayout>

View file

@ -1,58 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Nextcloud Android client application
~
~ @author Álvaro Brey Vilas
~ Copyright (C) 2022 Álvaro Brey Vilas
~ Copyright (C) 2022 Nextcloud GmbH
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ 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 <https://www.gnu.org/licenses/>.
-->
<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:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:paddingHorizontal="?dialogPreferredPadding">
<TextView
android:id="@+id/storage_permission_explanation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/file_management_permission_optional_text" />
<com.google.android.material.button.MaterialButton
android:layout_marginTop="@dimen/standard_padding"
android:id="@+id/btn_full_access"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/storage_permission_full_access"
android:theme="@style/Button.Primary"
app:cornerRadius="@dimen/button_corner_radius"
app:layout_constraintTop_toBottomOf="@id/storage_permission_explanation" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_read_only"
style="@style/OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/storage_permission_media_read_only"
app:cornerRadius="@dimen/button_corner_radius"
app:layout_constraintTop_toBottomOf="@id/btn_full_access" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -79,7 +79,6 @@
<dimen name="search_users_groups_layout_list_view_margin">20dp</dimen>
<dimen name="share_file_layout_text_size">12sp</dimen>
<dimen name="ssl_untrusted_cert_layout_padding">20dp</dimen>
<dimen name="scroll_view_height">180dp</dimen>
<dimen name="upload_list_item_frame_layout_width">60dp</dimen>
<dimen name="upload_list_item_text_size">12sp</dimen>
<dimen name="uploader_list_item_layout_image_margin">12dp</dimen>

View file

@ -800,8 +800,6 @@
<string name="permission_deny">Deny</string>
<string name="permission_allow">Allow</string>
<string name="share_send_note">Note to recipient</string>
<string name="note_confirm">Send</string>
<string name="send_note">Send note to recipient</string>
<string name="note_could_not_sent">Could not send note</string>
<string name="hint_note">Note</string>
<string name="no_browser_available">No app available to handle links</string>