diff --git a/changelog/unreleased/4871 b/changelog/unreleased/4871
new file mode 100644
index 00000000000..fea65f1f46d
--- /dev/null
+++ b/changelog/unreleased/4871
@@ -0,0 +1,6 @@
+Enhancement: Passcode and pattern screens in landscape mode
+
+Orientation restrictions defined in the manifest have been removed, and support for both
+orientations (portrait and landscape) has been added to the passcode and pattern screens.
+
+https://github.com/owncloud/android/pull/4871
diff --git a/owncloudApp/src/main/AndroidManifest.xml b/owncloudApp/src/main/AndroidManifest.xml
index 8576ebf3229..73bf0a56ddf 100644
--- a/owncloudApp/src/main/AndroidManifest.xml
+++ b/owncloudApp/src/main/AndroidManifest.xml
@@ -187,7 +187,6 @@
diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/security/passcode/PassCodeActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/security/passcode/PassCodeActivity.kt
index cf89b1d4223..bcd791fe4f4 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/presentation/security/passcode/PassCodeActivity.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/security/passcode/PassCodeActivity.kt
@@ -13,7 +13,7 @@
* @author Jorge Aguado Recio
*
* Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2025 ownCloud GmbH.
+ * Copyright (C) 2026 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
@@ -42,7 +42,7 @@ import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity
import com.owncloud.android.BuildConfig
import com.owncloud.android.R
-import com.owncloud.android.databinding.PasscodelockBinding
+import com.owncloud.android.databinding.PasscodeLockActivityBinding
import com.owncloud.android.domain.utils.Event
import com.owncloud.android.extensions.showBiometricDialog
import com.owncloud.android.extensions.showMessageInSnackbar
@@ -66,7 +66,7 @@ class PassCodeActivity : AppCompatActivity(), NumberKeyboardListener, EnableBiom
private val biometricViewModel by viewModel()
- private var _binding: PasscodelockBinding? = null
+ private var _binding: PasscodeLockActivityBinding? = null
val binding get() = _binding!!
private lateinit var passCodeEditTexts: Array
@@ -87,7 +87,7 @@ class PassCodeActivity : AppCompatActivity(), NumberKeyboardListener, EnableBiom
subscribeToViewModel()
- _binding = PasscodelockBinding.inflate(layoutInflater)
+ _binding = PasscodeLockActivityBinding.inflate(layoutInflater)
// protection against screen recording
if (!BuildConfig.DEBUG) {
@@ -106,7 +106,7 @@ class PassCodeActivity : AppCompatActivity(), NumberKeyboardListener, EnableBiom
// Allow or disallow touches with other visible windows
binding.passcodeLockLayout.filterTouchesWhenObscured =
PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(this)
- binding.explanation.filterTouchesWhenObscured =
+ binding.passcodeExplanation.filterTouchesWhenObscured =
PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(this)
inflatePasscodeTxtLine()
@@ -120,8 +120,8 @@ class PassCodeActivity : AppCompatActivity(), NumberKeyboardListener, EnableBiom
when (intent.action) {
ACTION_CHECK -> { //When you start the app with passcode
// this is a pass code request; the user has to input the right value
- binding.header.text = getString(R.string.pass_code_enter_pass_code)
- binding.explanation.visibility = View.INVISIBLE
+ binding.passcodeHeader.text = getString(R.string.pass_code_enter_pass_code)
+ binding.passcodeExplanation.visibility = View.INVISIBLE
supportActionBar?.setDisplayHomeAsUpEnabled(false) //Don´t show the back arrow
}
@@ -131,14 +131,14 @@ class PassCodeActivity : AppCompatActivity(), NumberKeyboardListener, EnableBiom
requestPassCodeConfirmation()
} else {
if (intent.extras?.getBoolean(EXTRAS_MIGRATION) == true) {
- binding.header.text =
+ binding.passcodeHeader.text =
getString(R.string.pass_code_configure_your_pass_code_migration, passCodeViewModel.getNumberOfPassCodeDigits())
} else {
// pass code preference has just been activated in Preferences;
// will receive and confirm pass code value
- binding.header.text = getString(R.string.pass_code_configure_your_pass_code)
+ binding.passcodeHeader.text = getString(R.string.pass_code_configure_your_pass_code)
}
- binding.explanation.visibility = View.VISIBLE
+ binding.passcodeExplanation.visibility = View.VISIBLE
when {
intent.extras?.getBoolean(EXTRAS_MIGRATION) == true -> {
supportActionBar?.setDisplayHomeAsUpEnabled(false)
@@ -158,8 +158,8 @@ class PassCodeActivity : AppCompatActivity(), NumberKeyboardListener, EnableBiom
ACTION_REMOVE -> { // Remove password
// pass code preference has just been disabled in Preferences;
// will confirm user knows pass code, then remove it
- binding.header.text = getString(R.string.pass_code_remove_your_pass_code)
- binding.explanation.visibility = View.INVISIBLE
+ binding.passcodeHeader.text = getString(R.string.pass_code_remove_your_pass_code)
+ binding.passcodeExplanation.visibility = View.INVISIBLE
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
@@ -180,7 +180,7 @@ class PassCodeActivity : AppCompatActivity(), NumberKeyboardListener, EnableBiom
}
private fun inflatePasscodeTxtLine() {
- val layoutCode = findViewById(R.id.layout_code)
+ val layoutCode = findViewById(R.id.passcode_value)
val numberOfPasscodeDigits = (passCodeViewModel.getPassCode()?.length ?: passCodeViewModel.getNumberOfPassCodeDigits())
for (i in 0 until numberOfPasscodeDigits) {
val txt = layoutInflater.inflate(R.layout.passcode_edit_text, layoutCode, false) as EditText
@@ -259,14 +259,14 @@ class PassCodeActivity : AppCompatActivity(), NumberKeyboardListener, EnableBiom
private fun actionCheckOk() {
// pass code accepted in request, user is allowed to access the app
- binding.error.visibility = View.INVISIBLE
+ binding.passcodeError.visibility = View.INVISIBLE
PassCodeManager.onActivityStopped(this)
finish()
}
private fun actionCheckMigration() {
- binding.error.visibility = View.INVISIBLE
+ binding.passcodeError.visibility = View.INVISIBLE
val intent = Intent(baseContext, PassCodeActivity::class.java)
intent.apply {
@@ -305,7 +305,7 @@ class PassCodeActivity : AppCompatActivity(), NumberKeyboardListener, EnableBiom
}
private fun actionCreateNoConfirm() {
- binding.error.visibility = View.INVISIBLE
+ binding.passcodeError.visibility = View.INVISIBLE
requestPassCodeConfirmation()
}
@@ -341,10 +341,10 @@ class PassCodeActivity : AppCompatActivity(), NumberKeyboardListener, EnableBiom
errorMessage: Int, headerMessage: String,
explanationVisibility: Int
) {
- binding.error.setText(errorMessage)
- binding.error.visibility = View.VISIBLE
- binding.header.text = headerMessage
- binding.explanation.visibility = explanationVisibility
+ binding.passcodeError.setText(errorMessage)
+ binding.passcodeError.visibility = View.VISIBLE
+ binding.passcodeHeader.text = headerMessage
+ binding.passcodeExplanation.visibility = explanationVisibility
clearBoxes()
}
@@ -354,8 +354,8 @@ class PassCodeActivity : AppCompatActivity(), NumberKeyboardListener, EnableBiom
*/
private fun requestPassCodeConfirmation() {
clearBoxes()
- binding.header.setText(R.string.pass_code_reenter_your_pass_code)
- binding.explanation.visibility = View.INVISIBLE
+ binding.passcodeHeader.setText(R.string.pass_code_reenter_your_pass_code)
+ binding.passcodeExplanation.visibility = View.INVISIBLE
confirmingPassCode = true
}
diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/security/pattern/PatternActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/security/pattern/PatternActivity.kt
index 4afc2bc51dc..3d3b0d238f7 100644
--- a/owncloudApp/src/main/java/com/owncloud/android/presentation/security/pattern/PatternActivity.kt
+++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/security/pattern/PatternActivity.kt
@@ -8,7 +8,7 @@
* @author Juan Carlos Garrote Gascón
* @author Jorge Aguado Recio
*
- * Copyright (C) 2025 ownCloud GmbH.
+ * Copyright (C) 2026 ownCloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
@@ -40,7 +40,7 @@ import com.andrognito.patternlockview.utils.PatternLockUtils
import com.owncloud.android.BuildConfig
import com.owncloud.android.R
import com.owncloud.android.data.providers.implementation.OCSharedPreferencesProvider
-import com.owncloud.android.databinding.ActivityPatternLockBinding
+import com.owncloud.android.databinding.PatternLockActivityBinding
import com.owncloud.android.extensions.showBiometricDialog
import com.owncloud.android.extensions.showMessageInSnackbar
import com.owncloud.android.presentation.documentsprovider.DocumentsProviderUtils.notifyDocumentsProviderRoots
@@ -59,7 +59,7 @@ class PatternActivity : AppCompatActivity(), EnableBiometrics {
private val patternViewModel by viewModel()
private val biometricViewModel by viewModel()
- private var _binding: ActivityPatternLockBinding? = null
+ private var _binding: PatternLockActivityBinding? = null
val binding get() = _binding!!
private var confirmingPattern = false
@@ -75,7 +75,7 @@ class PatternActivity : AppCompatActivity(), EnableBiometrics {
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
}
- _binding = ActivityPatternLockBinding.inflate(layoutInflater)
+ _binding = PatternLockActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
@@ -102,8 +102,8 @@ class PatternActivity : AppCompatActivity(), EnableBiometrics {
* This block is executed when the user opens the app after setting the pattern lock
* this block takes the pattern input by the user and checks it with the pattern initially set by the user.
*/
- binding.headerPattern.text = getString(R.string.pattern_enter_pattern)
- binding.explanationPattern.visibility = View.INVISIBLE
+ binding.patternHeader.text = getString(R.string.pattern_enter_pattern)
+ binding.patternExplanation.visibility = View.INVISIBLE
supportActionBar?.setDisplayHomeAsUpEnabled(false)
}
ACTION_REQUEST_WITH_RESULT -> {
@@ -118,13 +118,13 @@ class PatternActivity : AppCompatActivity(), EnableBiometrics {
patternExpShouldVisible = savedInstanceState.getBoolean(PATTERN_EXP_VIEW_STATE)
}
if (confirmingPattern) {
- binding.headerPattern.text = headerPatternViewText
+ binding.patternHeader.text = headerPatternViewText
if (!patternExpShouldVisible) {
- binding.explanationPattern.visibility = View.INVISIBLE
+ binding.patternExplanation.visibility = View.INVISIBLE
}
} else {
- binding.headerPattern.text = getString(R.string.pattern_configure_pattern)
- binding.explanationPattern.visibility = View.VISIBLE
+ binding.patternHeader.text = getString(R.string.pattern_configure_pattern)
+ binding.patternExplanation.visibility = View.VISIBLE
if (intent.extras?.getBoolean(EXTRAS_LOCK_ENFORCED) == true) {
supportActionBar?.setDisplayHomeAsUpEnabled(false)
} else {
@@ -136,9 +136,9 @@ class PatternActivity : AppCompatActivity(), EnableBiometrics {
/**
* This block is executed when the user is removing the pattern lock (i.e disabling the pattern lock)
*/
- binding.headerPattern.text = getString(R.string.pattern_remove_pattern)
- binding.explanationPattern.text = getString(R.string.pattern_no_longer_required)
- binding.explanationPattern.visibility = View.VISIBLE
+ binding.patternHeader.text = getString(R.string.pattern_remove_pattern)
+ binding.patternExplanation.text = getString(R.string.pattern_no_longer_required)
+ binding.patternExplanation.visibility = View.VISIBLE
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
else -> {
@@ -223,7 +223,7 @@ class PatternActivity : AppCompatActivity(), EnableBiometrics {
private fun handleActionCheck() {
if (patternViewModel.checkPatternIsValid(patternValue)) {
- binding.errorPattern.visibility = View.INVISIBLE
+ binding.patternError.visibility = View.INVISIBLE
val preferencesProvider = OCSharedPreferencesProvider(applicationContext)
preferencesProvider.putLong(PREFERENCE_LAST_UNLOCK_TIMESTAMP, SystemClock.elapsedRealtime())
PatternManager.onActivityStopped(this)
@@ -241,7 +241,7 @@ class PatternActivity : AppCompatActivity(), EnableBiometrics {
patternViewModel.removePattern()
val result = Intent()
setResult(RESULT_OK, result)
- binding.errorPattern.visibility = View.INVISIBLE
+ binding.patternError.visibility = View.INVISIBLE
notifyDocumentsProviderRoots(applicationContext)
finish()
} else {
@@ -254,7 +254,7 @@ class PatternActivity : AppCompatActivity(), EnableBiometrics {
private fun handleActionRequestWithResult() {
if (!confirmingPattern) {
- binding.errorPattern.visibility = View.INVISIBLE
+ binding.patternError.visibility = View.INVISIBLE
requestPatternConfirmation()
} else if (confirmPattern()) {
savePatternAndExit()
@@ -271,10 +271,10 @@ class PatternActivity : AppCompatActivity(), EnableBiometrics {
explanationVisibility: Int
) {
patternValue = null
- binding.errorPattern.setText(errorMessage)
- binding.errorPattern.visibility = View.VISIBLE
- binding.headerPattern.setText(headerMessage)
- binding.explanationPattern.visibility = explanationVisibility
+ binding.patternError.setText(errorMessage)
+ binding.patternError.visibility = View.VISIBLE
+ binding.patternHeader.setText(headerMessage)
+ binding.patternExplanation.visibility = explanationVisibility
}
/**
@@ -282,8 +282,8 @@ class PatternActivity : AppCompatActivity(), EnableBiometrics {
*/
private fun requestPatternConfirmation() {
binding.patternLockView.clearPattern()
- binding.headerPattern.setText(R.string.pattern_reenter_pattern)
- binding.explanationPattern.visibility = View.INVISIBLE
+ binding.patternHeader.setText(R.string.pattern_reenter_pattern)
+ binding.patternExplanation.visibility = View.INVISIBLE
confirmingPattern = true
}
@@ -309,8 +309,8 @@ class PatternActivity : AppCompatActivity(), EnableBiometrics {
outState.apply {
putBoolean(KEY_CONFIRMING_PATTERN, confirmingPattern)
putString(KEY_PATTERN_STRING, patternValue)
- putString(PATTERN_HEADER_VIEW_TEXT, binding.headerPattern.text.toString())
- putBoolean(PATTERN_EXP_VIEW_STATE, binding.explanationPattern.isVisible)
+ putString(PATTERN_HEADER_VIEW_TEXT, binding.patternHeader.text.toString())
+ putBoolean(PATTERN_EXP_VIEW_STATE, binding.patternExplanation.isVisible)
}
}
diff --git a/owncloudApp/src/main/res/layout-land/passcode_lock_activity.xml b/owncloudApp/src/main/res/layout-land/passcode_lock_activity.xml
new file mode 100644
index 00000000000..0a11ace7381
--- /dev/null
+++ b/owncloudApp/src/main/res/layout-land/passcode_lock_activity.xml
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/owncloudApp/src/main/res/layout-land/pattern_lock_activity.xml b/owncloudApp/src/main/res/layout-land/pattern_lock_activity.xml
new file mode 100644
index 00000000000..8b0e0a4c65a
--- /dev/null
+++ b/owncloudApp/src/main/res/layout-land/pattern_lock_activity.xml
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/owncloudApp/src/main/res/layout-w600dp/passcodelock.xml b/owncloudApp/src/main/res/layout-sw600dp/passcode_lock_activity.xml
similarity index 80%
rename from owncloudApp/src/main/res/layout-w600dp/passcodelock.xml
rename to owncloudApp/src/main/res/layout-sw600dp/passcode_lock_activity.xml
index 24d517ad060..9429c81f805 100644
--- a/owncloudApp/src/main/res/layout-w600dp/passcodelock.xml
+++ b/owncloudApp/src/main/res/layout-sw600dp/passcode_lock_activity.xml
@@ -2,7 +2,7 @@
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2020 ownCloud GmbH.
+ Copyright (C) 2026 ownCloud GmbH.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2,
@@ -18,21 +18,21 @@
-->
+ app:layout_constraintTop_toBottomOf="@id/passcode_header" />
+ app:layout_constraintTop_toBottomOf="@id/passcode_explanation">
-
+
+ app:layout_constraintTop_toBottomOf="@id/passcode_value" />
+ app:layout_constraintTop_toBottomOf="@id/passcode_error" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/owncloudApp/src/main/res/layout/passcodelock.xml b/owncloudApp/src/main/res/layout/passcode_lock_activity.xml
similarity index 82%
rename from owncloudApp/src/main/res/layout/passcodelock.xml
rename to owncloudApp/src/main/res/layout/passcode_lock_activity.xml
index 12f4a28fc65..9a2113774e6 100644
--- a/owncloudApp/src/main/res/layout/passcodelock.xml
+++ b/owncloudApp/src/main/res/layout/passcode_lock_activity.xml
@@ -2,7 +2,7 @@
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2020 ownCloud GmbH.
+ Copyright (C) 2026 ownCloud GmbH.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2,
@@ -18,7 +18,7 @@
-->
+ app:layout_constraintTop_toBottomOf="@id/passcode_header" />
-
+
+ app:layout_constraintTop_toBottomOf="@id/passcode_value" />
+ app:layout_constraintTop_toBottomOf="@id/passcode_error" />