Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
import com.owncloud.android.ui.activity.UploadListActivity;
import com.owncloud.android.ui.activity.UserInfoActivity;
import com.owncloud.android.ui.dialog.AccountRemovalDialog;
import com.owncloud.android.ui.dialog.AppPassCodeDialog;
import com.owncloud.android.ui.dialog.ChooseRichDocumentsTemplateDialogFragment;
import com.owncloud.android.ui.dialog.ChooseTemplateDialogFragment;
import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
Expand Down Expand Up @@ -427,6 +428,9 @@ abstract class ComponentsModule {
@ContributesAndroidInjector
abstract ThemeSelectionDialog themeSelectionDialog();

@ContributesAndroidInjector
abstract AppPassCodeDialog appPassCodeDialog();

@ContributesAndroidInjector
abstract SharePasswordDialogFragment sharePasswordDialogFragment();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ class ExtendedSettingsActivity : AppCompatActivity() {
return
}

dialogType.showDialog(this)
val dismissable = intent.getBooleanExtra(EXTRA_DISMISSABLE, true)
dialogType.showDialog(this, dismissable)
dialogShown = true
}

Expand All @@ -49,12 +50,18 @@ class ExtendedSettingsActivity : AppCompatActivity() {
}

companion object {
private const val EXTRA_DISMISSABLE = "dismissable"
private const val EXTRA_DIALOG_TYPE = "dialog_type"
private const val KEY_DIALOG_SHOWN = "dialog_shown"

fun createIntent(context: Context, dialogType: ExtendedSettingsActivityDialog): Intent =
Intent(context, ExtendedSettingsActivity::class.java).apply {
putExtra(EXTRA_DIALOG_TYPE, dialogType.key)
}
@JvmOverloads
fun createIntent(
context: Context,
dialogType: ExtendedSettingsActivityDialog,
dismissable: Boolean = true
): Intent = Intent(context, ExtendedSettingsActivity::class.java).apply {
putExtra(EXTRA_DIALOG_TYPE, dialogType.key)
putExtra(EXTRA_DISMISSABLE, dismissable)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
Expand Down Expand Up @@ -65,7 +64,6 @@
import com.owncloud.android.lib.common.ExternalLinkType;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.providers.DocumentsStorageProvider;
import com.owncloud.android.ui.ListPreferenceDialog;
import com.owncloud.android.ui.ThemeableSwitchPreference;
import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask;
import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment;
Expand All @@ -80,7 +78,6 @@
import com.owncloud.android.utils.theme.CapabilityUtils;
import com.owncloud.android.utils.theme.ViewThemeUtils;

import java.util.ArrayList;
import java.util.Objects;

import javax.inject.Inject;
Expand Down Expand Up @@ -129,7 +126,7 @@ public class SettingsActivity extends PreferenceActivity

private Uri serverBaseUri;

private ListPreferenceDialog lock;
private Preference lock;
private ThemeableSwitchPreference showHiddenFiles;
private ThemeableSwitchPreference showEcosystemApps;
private AppCompatDelegate delegate;
Expand Down Expand Up @@ -212,9 +209,8 @@ public void onBackPressed() {

private void showPasscodeDialogIfEnforceAppProtection() {
if (MDMConfig.INSTANCE.enforceProtection(this) && Objects.equals(preferences.getLockPreference(), SettingsActivity.LOCK_NONE) && lock != null) {
lock.showDialog();
lock.dismissible(false);
lock.enableCancelButton(false);
Intent intent = ExtendedSettingsActivity.Companion.createIntent(this, ExtendedSettingsActivityDialog.AppPasscode, false);
startActivityForResult(intent, ExtendedSettingsActivityDialog.AppPasscode.getResultId());
}
}

Expand Down Expand Up @@ -742,63 +738,33 @@ private void setupShowEcosystemAppsPreference(PreferenceCategory preferenceCateg
private void setupLockPreference(PreferenceCategory preferenceCategoryDetails,
boolean passCodeEnabled,
boolean deviceCredentialsEnabled) {
boolean enforceProtection = MDMConfig.INSTANCE.enforceProtection(this);
lock = (ListPreferenceDialog) findPreference(PREFERENCE_LOCK);
int optionSize = 3;
if (enforceProtection) {
optionSize = 2;
}

lock = findPreference(PREFERENCE_LOCK);
if (lock != null && (passCodeEnabled || deviceCredentialsEnabled)) {
ArrayList<String> lockEntries = new ArrayList<>(optionSize);
lockEntries.add(getString(R.string.prefs_lock_using_passcode));
lockEntries.add(getString(R.string.prefs_lock_using_device_credentials));

ArrayList<String> lockValues = new ArrayList<>(optionSize);
lockValues.add(LOCK_PASSCODE);
lockValues.add(LOCK_DEVICE_CREDENTIALS);

if (!enforceProtection) {
lockEntries.add(getString(R.string.prefs_lock_none));
lockValues.add(LOCK_NONE);
}
String currentLock = preferences.getLockPreference();
updateLockSummary(lock, currentLock);

if (!passCodeEnabled) {
lockEntries.remove(getString(R.string.prefs_lock_using_passcode));
lockValues.remove(LOCK_PASSCODE);
} else if (!deviceCredentialsEnabled || !DeviceCredentialUtils.areCredentialsAvailable(getApplicationContext())) {
lockEntries.remove(getString(R.string.prefs_lock_using_device_credentials));
lockValues.remove(LOCK_DEVICE_CREDENTIALS);
}

String[] lockEntriesArr = new String[lockEntries.size()];
lockEntriesArr = lockEntries.toArray(lockEntriesArr);
String[] lockValuesArr = new String[lockValues.size()];
lockValuesArr = lockValues.toArray(lockValuesArr);

lock.setEntries(lockEntriesArr);
lock.setEntryValues(lockValuesArr);
lock.setSummary(lock.getEntry());

lock.setOnPreferenceChangeListener((preference, o) -> {
pendingLock = LOCK_NONE;
String oldValue = ((ListPreference) preference).getValue();
String newValue = (String) o;
if (!oldValue.equals(newValue)) {
if (LOCK_NONE.equals(oldValue)) {
enableLock(newValue);
} else {
pendingLock = newValue;
disableLock(oldValue);
}
}
return false;
lock.setOnPreferenceClickListener(preference -> {
Intent intent = ExtendedSettingsActivity.Companion.createIntent(this, ExtendedSettingsActivityDialog.AppPasscode);
startActivityForResult(intent, ExtendedSettingsActivityDialog.AppPasscode.getResultId());
return true;
});
} else {
preferenceCategoryDetails.removePreference(lock);
}
}

private void updateLockSummary(Preference lockPreference, String lockValue) {
String summary;
if (LOCK_PASSCODE.equals(lockValue)) {
summary = getString(R.string.prefs_lock_using_passcode);
} else if (LOCK_DEVICE_CREDENTIALS.equals(lockValue)) {
summary = getString(R.string.prefs_lock_using_device_credentials);
} else {
summary = getString(R.string.prefs_lock_none);
}
lockPreference.setSummary(summary);
}

private void setupAutoUploadCategory(PreferenceScreen preferenceScreen) {
final PreferenceCategory preferenceCategorySyncedFolders =
(PreferenceCategory) findPreference("synced_folders_category");
Expand Down Expand Up @@ -853,8 +819,10 @@ private void enableLock(String lock) {
}

private void changeLockSetting(String value) {
lock.setValue(value);
lock.setSummary(lock.getEntry());
preferences.setLockPreference(value);
if (lock != null) {
updateLockSummary(lock, value);
}
DocumentsStorageProvider.notifyRootsChanged(this);
}

Expand Down Expand Up @@ -1067,6 +1035,19 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// needed for to change status bar color
recreate();
}
} else if (requestCode == ExtendedSettingsActivityDialog.AppPasscode.getResultId() && data != null) {
String selectedLock = data.getStringExtra(ExtendedSettingsActivityDialog.AppPasscode.getKey());
if (selectedLock != null) {
String currentLock = preferences.getLockPreference();
if (!currentLock.equals(selectedLock)) {
if (LOCK_NONE.equals(currentLock)) {
enableLock(selectedLock);
} else {
pendingLock = selectedLock;
disableLock(currentLock);
}
}
}
} else if (requestCode == REQ_ALL_FILES_ACCESS) {
final PreferenceCategory preferenceCategorySync = (PreferenceCategory) findPreference("sync");
setupAllFilesAccessPreference(preferenceCategorySync);
Expand Down
143 changes: 143 additions & 0 deletions app/src/main/java/com/owncloud/android/ui/dialog/AppPassCodeDialog.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.owncloud.android.ui.dialog

import android.app.Dialog
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.core.os.bundleOf
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.setFragmentResult
import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.nextcloud.client.di.Injectable
import com.nextcloud.client.preferences.AppPreferences
import com.nextcloud.utils.extensions.setVisibleIf
import com.nextcloud.utils.mdm.MDMConfig
import com.owncloud.android.R
import com.owncloud.android.databinding.DialogAppPasscodeBinding
import com.owncloud.android.ui.activity.SettingsActivity
import com.owncloud.android.ui.model.ExtendedSettingsActivityDialog
import com.owncloud.android.utils.DeviceCredentialUtils
import com.owncloud.android.utils.theme.ViewThemeUtils
import javax.inject.Inject

class AppPassCodeDialog :
DialogFragment(),
Injectable {

@Inject
lateinit var preferences: AppPreferences

@Inject
lateinit var viewThemeUtils: ViewThemeUtils

private lateinit var binding: DialogAppPasscodeBinding

override fun onStart() {
super.onStart()
val alertDialog = dialog as AlertDialog

val positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) as? MaterialButton
positiveButton?.let {
viewThemeUtils.material.colorMaterialButtonPrimaryTonal(it)
}

val dismissable = arguments?.getBoolean(ARG_DISMISSABLE, true) ?: true
isCancelable = dismissable
}

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
binding = DialogAppPasscodeBinding.inflate(layoutInflater)

val currentLock = preferences.lockPreference ?: SettingsActivity.LOCK_NONE
val passCodeEnabled = resources.getBoolean(R.bool.passcode_enabled)
val deviceCredentialsEnabled = resources.getBoolean(R.bool.device_credentials_enabled)
val enforceProtection = MDMConfig.enforceProtection(requireContext())
val deviceCredentialsAvailable = DeviceCredentialUtils.areCredentialsAvailable(requireContext())
val dismissable = arguments?.getBoolean(ARG_DISMISSABLE, true) ?: true

binding.lockPasscode.setVisibleIf(passCodeEnabled)
binding.lockDeviceCredentials.setVisibleIf(deviceCredentialsEnabled && deviceCredentialsAvailable)
binding.lockNone.setVisibleIf(!enforceProtection)

setupTheme()
setCurrentSelection(currentLock)
setupListener()

val builder = MaterialAlertDialogBuilder(requireContext())
.setView(binding.root)
.setPositiveButton(R.string.common_ok) { _, _ ->
applySelection()
dismiss()
}

if (!enforceProtection && dismissable) {
builder.setNegativeButton(R.string.common_cancel) { _, _ ->
dismiss()
}
}

builder.setCancelable(dismissable)

viewThemeUtils.dialog.colorMaterialAlertDialogBackground(requireContext(), builder)

return builder.create()
}

private fun setupTheme() {
viewThemeUtils.platform.apply {
colorTextView(binding.dialogTitle)
themeRadioButton(binding.lockPasscode)
themeRadioButton(binding.lockDeviceCredentials)
themeRadioButton(binding.lockNone)
}
}

private fun setCurrentSelection(currentLock: String) {
val radioGroup = binding.lockRadioGroup

when (currentLock) {
SettingsActivity.LOCK_PASSCODE -> radioGroup.check(R.id.lock_passcode)
SettingsActivity.LOCK_DEVICE_CREDENTIALS -> radioGroup.check(R.id.lock_device_credentials)
SettingsActivity.LOCK_NONE -> radioGroup.check(R.id.lock_none)
}
}

private fun setupListener() {
binding.lockRadioGroup.setOnCheckedChangeListener { _, checkedId ->
val selectedLock = when (checkedId) {
R.id.lock_passcode -> SettingsActivity.LOCK_PASSCODE
R.id.lock_device_credentials -> SettingsActivity.LOCK_DEVICE_CREDENTIALS
R.id.lock_none -> SettingsActivity.LOCK_NONE
else -> SettingsActivity.LOCK_NONE
}

currentSelection = selectedLock
}
}

private var currentSelection: String? = null

private fun applySelection() {
val selectedLock = currentSelection ?: return

setFragmentResult(
ExtendedSettingsActivityDialog.AppPasscode.key,
bundleOf(ExtendedSettingsActivityDialog.AppPasscode.key to selectedLock)
)
}

companion object {
private const val ARG_DISMISSABLE = "dismissable"

fun instance(dismissable: Boolean): AppPassCodeDialog = AppPassCodeDialog().apply {
arguments = bundleOf(ARG_DISMISSABLE to dismissable)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ import android.app.Activity.RESULT_OK
import android.content.Intent
import com.nextcloud.ui.ChooseStorageLocationDialogFragment
import com.owncloud.android.ui.activity.ExtendedSettingsActivity
import com.owncloud.android.ui.dialog.AppPassCodeDialog
import com.owncloud.android.ui.dialog.ThemeSelectionDialog

@Suppress("MagicNumber")
enum class ExtendedSettingsActivityDialog(val tag: String, val key: String, val resultId: Int) {
StorageLocation("choose_storage_location", "storage_selection_result", 13),
ThemeSelection("theme_selection", "theme_selection_result", 14);
ThemeSelection("theme_selection", "theme_selection_result", 14),
AppPasscode("app_passcode", "app_passcode_result", 15);

fun showDialog(activity: ExtendedSettingsActivity) {
fun showDialog(activity: ExtendedSettingsActivity, dismissable: Boolean = true) {
activity.run {
if (supportFragmentManager.findFragmentByTag(tag) != null) {
return
Expand All @@ -38,13 +40,11 @@ enum class ExtendedSettingsActivityDialog(val tag: String, val key: String, val
finish()
}

if (this@ExtendedSettingsActivityDialog == StorageLocation) {
ChooseStorageLocationDialogFragment()
} else {
ThemeSelectionDialog()
}.run {
show(supportFragmentManager, tag)
}
when (this@ExtendedSettingsActivityDialog) {
StorageLocation -> ChooseStorageLocationDialogFragment()
ThemeSelection -> ThemeSelectionDialog()
AppPasscode -> AppPassCodeDialog.instance(dismissable)
}.show(supportFragmentManager, tag)
}
}
}
Loading
Loading