aboutsummaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/build.gradle142
-rw-r--r--app/build.gradle.kts147
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt2
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/UserPreference.kt11
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillStrategyDsl.kt3
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/autofill/oreo/FeatureAndTrustDetection.kt10
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/autofill/oreo/FormField.kt59
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/autofill/oreo/OreoAutofillService.kt2
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt9
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt9
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/BreakOutOfDetached.kt13
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/GitConfigActivity.kt2
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/GitOperation.kt7
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/GitServerConfigActivity.kt13
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/ResetToRemoteOperation.kt32
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/config/SshjConfig.kt12
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/pwgen/RandomPasswordGenerator.kt3
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/ui/adapters/PasswordItemRecyclerAdapter.kt19
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/ui/dialogs/FolderCreationDialogFragment.kt2
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/PreferenceKeys.kt1
-rw-r--r--app/src/main/res/layout/activity_git_clone.xml37
-rw-r--r--app/src/main/res/layout/git_credential_layout.xml1
-rw-r--r--app/src/main/res/values/strings.xml1
-rw-r--r--app/src/main/res/xml/oreo_autofill_service.xml16
24 files changed, 321 insertions, 232 deletions
diff --git a/app/build.gradle b/app/build.gradle
deleted file mode 100644
index 19671094..00000000
--- a/app/build.gradle
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-plugins {
- id 'kotlin-android'
-}
-
-final def keystorePropertiesFile = rootProject.file 'keystore.properties'
-
-static final def isSnapshot() {
- return System.env['GITHUB_WORKFLOW'] != null && System.env['SNAPSHOT'] != null
-}
-
-android {
- if (isSnapshot()) {
- android.applicationVariants.all { final variant ->
- variant.outputs.all {
- outputFileName = "aps-${variant.getFlavorName()}_${defaultConfig.versionName}.apk"
- }
- }
- }
-
- buildFeatures.viewBinding = true
-
- defaultConfig {
- applicationId 'dev.msfjarvis.aps'
- versionCode 11030
- versionName '1.10.3'
- }
-
- lintOptions {
- abortOnError = true // make sure build fails with lint errors!
- disable 'MissingTranslation', 'PluralsCandidate'
- }
-
- packagingOptions {
- exclude '.readme'
- exclude 'META-INF/LICENSE.txt'
- exclude 'META-INF/NOTICE.txt'
- }
-
- buildTypes {
- release {
- minifyEnabled = true
- proguardFiles 'proguard-android-optimize.txt', 'proguard-rules.pro'
- buildConfigField 'boolean', 'ENABLE_DEBUG_FEATURES', isSnapshot() ? 'true' : 'false'
- }
- debug {
- applicationIdSuffix = '.debug'
- versionNameSuffix = '-debug'
- minifyEnabled = false
- buildConfigField 'boolean', 'ENABLE_DEBUG_FEATURES', 'true'
- }
- }
-
- if (keystorePropertiesFile.exists()) {
- final def keystoreProperties = new Properties()
- keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
- signingConfigs {
- release {
- keyAlias = keystoreProperties['keyAlias']
- keyPassword = keystoreProperties['keyPassword']
- storeFile = rootProject.file keystoreProperties['storeFile']
- storePassword = keystoreProperties['storePassword']
- }
- }
- buildTypes.release.signingConfig = signingConfigs.release
- buildTypes.debug.signingConfig = signingConfigs.release
- }
-
- flavorDimensions "free"
- productFlavors {
- free {
- versionNameSuffix "-free"
- }
- nonFree {
- }
- }
-}
-
-dependencies {
- implementation deps.androidx.activity_ktx
- implementation deps.androidx.annotation
- implementation deps.androidx.autofill
- implementation deps.androidx.appcompat
- implementation deps.androidx.biometric
- implementation deps.androidx.constraint_layout
- implementation deps.androidx.core_ktx
- implementation deps.androidx.documentfile
- implementation deps.androidx.fragment_ktx
- implementation deps.androidx.lifecycle_common
- implementation deps.androidx.lifecycle_livedata_ktx
- implementation deps.androidx.lifecycle_viewmodel_ktx
- implementation deps.androidx.material
- implementation deps.androidx.preference
- implementation deps.androidx.recycler_view
- implementation deps.androidx.recycler_view_selection
- implementation deps.androidx.security
- implementation deps.androidx.swiperefreshlayout
-
- implementation deps.kotlin.coroutines.android
- implementation deps.kotlin.coroutines.core
-
- implementation deps.first_party.openpgp_ktx
- implementation deps.first_party.zxing_android_embedded
-
- implementation deps.third_party.commons_codec
- implementation deps.third_party.fastscroll
- implementation(deps.third_party.jgit) {
- exclude group: 'org.apache.httpcomponents', module: 'httpclient'
- }
- implementation deps.third_party.jsch
- implementation deps.third_party.sshj
- implementation deps.third_party.bouncycastle
- implementation deps.third_party.plumber
- implementation deps.third_party.ssh_auth
- implementation deps.third_party.timber
- implementation deps.third_party.timberkt
-
- if (isSnapshot()) {
- implementation deps.third_party.leakcanary
- implementation deps.third_party.whatthestack
- } else {
- debugImplementation deps.third_party.leakcanary
- debugImplementation deps.third_party.whatthestack
- }
-
- nonFreeImplementation deps.non_free.google_play_auth_api_phone
-
- // Testing-only dependencies
- androidTestImplementation deps.testing.junit
- androidTestImplementation deps.testing.kotlin_test_junit
- androidTestImplementation deps.testing.androidx.runner
- androidTestImplementation deps.testing.androidx.rules
- androidTestImplementation deps.testing.androidx.junit
- androidTestImplementation deps.testing.androidx.espresso_core
- androidTestImplementation deps.testing.androidx.espresso_intents
-
- testImplementation deps.testing.junit
- testImplementation deps.testing.kotlin_test_junit
-}
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
new file mode 100644
index 00000000..19d9ca7a
--- /dev/null
+++ b/app/build.gradle.kts
@@ -0,0 +1,147 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+import java.util.Properties
+import com.android.build.gradle.internal.api.BaseVariantOutputImpl
+
+plugins {
+ kotlin("android")
+}
+
+val keystorePropertiesFile = rootProject.file("keystore.properties")
+
+fun isSnapshot(): Boolean {
+ return System.getenv("GITHUB_WORKFLOW") != null && System.getenv("SNAPSHOT") != null
+}
+
+android {
+ if (isSnapshot()) {
+ applicationVariants.all {
+ outputs.all {
+ (this as BaseVariantOutputImpl).outputFileName = "aps-${flavorName}_$versionName.apk"
+ }
+ }
+ }
+
+ buildFeatures.viewBinding = true
+
+ defaultConfig {
+ applicationId = "dev.msfjarvis.aps"
+ versionCode = 11031
+ versionName = "1.11.0-SNAPSHOT"
+ }
+
+ lintOptions {
+ isAbortOnError = true
+ isCheckReleaseBuilds = false
+ disable("MissingTranslation", "PluralsCandidate")
+ }
+
+ packagingOptions {
+ exclude("**/*.version")
+ exclude("**/*.txt")
+ exclude("**/*.kotlin_module")
+ }
+
+ buildTypes {
+ named("release") {
+ isMinifyEnabled = true
+ setProguardFiles(listOf("proguard-android-optimize.txt", "proguard-rules.pro"))
+ buildConfigField("boolean", "ENABLE_DEBUG_FEATURES", if (isSnapshot()) "true" else "false")
+ }
+ named("debug") {
+ applicationIdSuffix = ".debug"
+ versionNameSuffix = "-debug"
+ isMinifyEnabled = false
+ buildConfigField("boolean", "ENABLE_DEBUG_FEATURES", "true")
+ }
+ }
+
+ if (keystorePropertiesFile.exists()) {
+ val keystoreProperties = Properties()
+ keystoreProperties.load(keystorePropertiesFile.inputStream())
+ signingConfigs {
+ register("release") {
+ keyAlias = keystoreProperties["keyAlias"] as String
+ keyPassword = keystoreProperties["keyPassword"] as String
+ storeFile = rootProject.file(keystoreProperties["storeFile"] as String)
+ storePassword = keystoreProperties["storePassword"] as String
+ }
+ }
+ listOf("release", "debug").map {
+ buildTypes.getByName(it).signingConfig = signingConfigs.getByName(it)
+ }
+ }
+
+ flavorDimensions("free")
+ productFlavors {
+ create("free") {
+ versionNameSuffix = "-free"
+ }
+ create("nonFree") {
+ }
+ }
+}
+
+dependencies {
+ compileOnly(Dependencies.AndroidX.annotation)
+ implementation(Dependencies.AndroidX.activity_ktx)
+ implementation(Dependencies.AndroidX.autofill)
+ implementation(Dependencies.AndroidX.appcompat)
+ implementation(Dependencies.AndroidX.biometric)
+ implementation(Dependencies.AndroidX.constraint_layout)
+ implementation(Dependencies.AndroidX.core_ktx)
+ implementation(Dependencies.AndroidX.documentfile)
+ implementation(Dependencies.AndroidX.fragment_ktx)
+ implementation(Dependencies.AndroidX.lifecycle_common)
+ implementation(Dependencies.AndroidX.lifecycle_livedata_ktx)
+ implementation(Dependencies.AndroidX.lifecycle_viewmodel_ktx)
+ implementation(Dependencies.AndroidX.material)
+ implementation(Dependencies.AndroidX.preference)
+ implementation(Dependencies.AndroidX.recycler_view)
+ implementation(Dependencies.AndroidX.recycler_view_selection)
+ implementation(Dependencies.AndroidX.security)
+ implementation(Dependencies.AndroidX.swiperefreshlayout)
+
+ implementation(Dependencies.Kotlin.Coroutines.android)
+ implementation(Dependencies.Kotlin.Coroutines.core)
+
+ implementation(Dependencies.FirstParty.openpgp_ktx)
+ implementation(Dependencies.FirstParty.zxing_android_embedded)
+
+ implementation(Dependencies.ThirdParty.commons_codec)
+ implementation(Dependencies.ThirdParty.fastscroll)
+ implementation(Dependencies.ThirdParty.jgit) {
+ exclude(group = "org.apache.httpcomponents", module = "httpclient")
+ }
+ implementation(Dependencies.ThirdParty.jsch)
+ implementation(Dependencies.ThirdParty.sshj)
+ implementation(Dependencies.ThirdParty.bouncycastle)
+ implementation(Dependencies.ThirdParty.plumber)
+ implementation(Dependencies.ThirdParty.ssh_auth)
+ implementation(Dependencies.ThirdParty.timber)
+ implementation(Dependencies.ThirdParty.timberkt)
+
+ if (isSnapshot()) {
+ implementation(Dependencies.ThirdParty.leakcanary)
+ implementation(Dependencies.ThirdParty.whatthestack)
+ } else {
+ debugImplementation(Dependencies.ThirdParty.leakcanary)
+ debugImplementation(Dependencies.ThirdParty.whatthestack)
+ }
+
+ "nonFreeImplementation"(Dependencies.NonFree.google_play_auth_api_phone)
+
+ // Testing-only dependencies
+ androidTestImplementation(Dependencies.Testing.junit)
+ androidTestImplementation(Dependencies.Testing.kotlin_test_junit)
+ androidTestImplementation(Dependencies.Testing.AndroidX.runner)
+ androidTestImplementation(Dependencies.Testing.AndroidX.rules)
+ androidTestImplementation(Dependencies.Testing.AndroidX.junit)
+ androidTestImplementation(Dependencies.Testing.AndroidX.espresso_core)
+ androidTestImplementation(Dependencies.Testing.AndroidX.espresso_intents)
+
+ testImplementation(Dependencies.Testing.junit)
+ testImplementation(Dependencies.Testing.kotlin_test_junit)
+}
diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt
index fc32b8da..1cd7c1ea 100644
--- a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt
@@ -21,6 +21,7 @@ import androidx.fragment.app.activityViewModels
import androidx.fragment.app.setFragmentResultListener
import androidx.lifecycle.observe
import androidx.preference.PreferenceManager
+import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar
import com.zeapo.pwdstore.databinding.PasswordRecyclerViewBinding
@@ -125,6 +126,7 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
}
val recyclerView = binding.passRecycler
recyclerView.apply {
+ addItemDecoration(DividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL))
layoutManager = LinearLayoutManager(requireContext())
itemAnimator = OnOffItemAnimator()
adapter = recyclerAdapter
diff --git a/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt b/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt
index e2cde88e..8938df00 100644
--- a/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt
@@ -95,8 +95,11 @@ class UserPreference : AppCompatActivity() {
if (!PasswordRepository.isGitRepo()) {
listOfNotNull(
- gitServerPreference, gitConfigPreference, sshKeyPreference,
- viewSshKeyPreference, clearSavedPassPreference
+ gitServerPreference,
+ gitConfigPreference,
+ sshKeyPreference,
+ viewSshKeyPreference,
+ clearSavedPassPreference,
).forEach {
it.parent?.removePreference(it)
}
@@ -119,12 +122,12 @@ class UserPreference : AppCompatActivity() {
autoFillAppsPreference,
autoFillDefaultPreference,
autoFillAlwaysShowDialogPreference,
- autoFillShowFullNamePreference
+ autoFillShowFullNamePreference,
)
oreoAutofillDependencies = listOfNotNull(
oreoAutofillDirectoryStructurePreference,
oreoAutofillDefaultUsername,
- oreoAutofillCustomPublixSuffixes
+ oreoAutofillCustomPublixSuffixes,
)
oreoAutofillCustomPublixSuffixes?.apply {
setOnBindEditTextListener {
diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillStrategyDsl.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillStrategyDsl.kt
index ec333bec..cae84d54 100644
--- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillStrategyDsl.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillStrategyDsl.kt
@@ -232,7 +232,8 @@ class AutofillRule private constructor(
fun genericPassword(optional: Boolean = false, block: FieldMatcher.Builder.() -> Unit) {
require(matchers.none {
it.type in listOf(
- FillableFieldType.CurrentPassword, FillableFieldType.NewPassword
+ FillableFieldType.CurrentPassword,
+ FillableFieldType.NewPassword,
)
}) { "Every rule block can only have either genericPassword or {current,new}Password blocks" }
matchers.add(
diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/FeatureAndTrustDetection.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/FeatureAndTrustDetection.kt
index 8864f877..5c8b6b3b 100644
--- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/FeatureAndTrustDetection.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/FeatureAndTrustDetection.kt
@@ -74,7 +74,7 @@ private val TRUSTED_BROWSER_CERTIFICATE_HASH = mapOf(
"org.mozilla.firefox_beta" to "p4tipRZbRJSy/q2edqKA0i2Tf+5iUa7OWZRGsuoxmwQ=",
"org.mozilla.focus" to "YgOkc7421k7jf4f6UA7bx56rkwYQq5ufpMp9XB8bT/w=",
"org.mozilla.klar" to "YgOkc7421k7jf4f6UA7bx56rkwYQq5ufpMp9XB8bT/w=",
- "org.torproject.torbrowser" to "IAYfBF5zfGc3XBd5TP7bQ2oDzsa6y3y5+WZCIFyizsg="
+ "org.torproject.torbrowser" to "IAYfBF5zfGc3XBd5TP7bQ2oDzsa6y3y5+WZCIFyizsg=",
)
private fun isTrustedBrowser(context: Context, appPackage: String): Boolean {
@@ -112,7 +112,7 @@ private val BROWSER_MULTI_ORIGIN_METHOD = mapOf(
"org.mozilla.firefox_beta" to BrowserMultiOriginMethod.WebView,
"org.mozilla.focus" to BrowserMultiOriginMethod.Field,
"org.mozilla.klar" to BrowserMultiOriginMethod.Field,
- "org.torproject.torbrowser" to BrowserMultiOriginMethod.WebView
+ "org.torproject.torbrowser" to BrowserMultiOriginMethod.WebView,
)
private fun getBrowserMultiOriginMethod(appPackage: String): BrowserMultiOriginMethod =
@@ -135,7 +135,7 @@ private val BROWSER_SAVE_FLAG = mapOf(
"org.mozilla.fennec_aurora" to 0,
"com.opera.mini.native" to 0,
"com.opera.mini.native.beta" to 0,
- "com.opera.touch" to 0
+ "com.opera.touch" to 0,
)
@RequiresApi(Build.VERSION_CODES.O)
@@ -162,7 +162,7 @@ private val FLAKY_BROWSERS = listOf(
"com.android.chrome",
"com.chrome.beta",
"com.chrome.canary",
- "com.chrome.dev"
+ "com.chrome.dev",
)
enum class BrowserAutofillSupportLevel {
@@ -170,7 +170,7 @@ enum class BrowserAutofillSupportLevel {
FlakyFill,
PasswordFill,
GeneralFill,
- GeneralFillAndSave
+ GeneralFillAndSave,
}
@RequiresApi(Build.VERSION_CODES.O)
diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/FormField.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/FormField.kt
index 2b18bbb6..6435a83f 100644
--- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/FormField.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/FormField.kt
@@ -33,19 +33,19 @@ class FormField(
private val HINTS_USERNAME = listOf(
HintConstants.AUTOFILL_HINT_USERNAME,
- HintConstants.AUTOFILL_HINT_NEW_USERNAME
+ HintConstants.AUTOFILL_HINT_NEW_USERNAME,
)
private val HINTS_NEW_PASSWORD = listOf(
- HintConstants.AUTOFILL_HINT_NEW_PASSWORD
+ HintConstants.AUTOFILL_HINT_NEW_PASSWORD,
)
private val HINTS_PASSWORD = HINTS_NEW_PASSWORD + listOf(
- HintConstants.AUTOFILL_HINT_PASSWORD
+ HintConstants.AUTOFILL_HINT_PASSWORD,
)
private val HINTS_OTP = listOf(
- HintConstants.AUTOFILL_HINT_SMS_OTP
+ HintConstants.AUTOFILL_HINT_SMS_OTP,
)
@Suppress("DEPRECATION")
@@ -54,7 +54,7 @@ class FormField(
HintConstants.AUTOFILL_HINT_NAME,
HintConstants.AUTOFILL_HINT_PERSON_NAME,
HintConstants.AUTOFILL_HINT_PHONE,
- HintConstants.AUTOFILL_HINT_PHONE_NUMBER
+ HintConstants.AUTOFILL_HINT_PHONE_NUMBER,
)
private val ANDROID_TEXT_FIELD_CLASS_NAMES = listOf(
@@ -62,7 +62,7 @@ class FormField(
"android.widget.AutoCompleteTextView",
"androidx.appcompat.widget.AppCompatEditText",
"android.support.v7.widget.AppCompatEditText",
- "com.google.android.material.textfield.TextInputEditText"
+ "com.google.android.material.textfield.TextInputEditText",
)
private const val ANDROID_WEB_VIEW_CLASS_NAME = "android.webkit.WebView"
@@ -75,15 +75,24 @@ class FormField(
InputType.TYPE_CLASS_TEXT -> typeVariation in listOf(
InputType.TYPE_TEXT_VARIATION_PASSWORD,
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD,
- InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD
+ InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD,
)
else -> false
}
}
- private val HTML_INPUT_FIELD_TYPES_USERNAME = listOf("email", "tel", "text")
- private val HTML_INPUT_FIELD_TYPES_PASSWORD = listOf("password")
- private val HTML_INPUT_FIELD_TYPES_OTP = listOf("tel", "text")
+ private val HTML_INPUT_FIELD_TYPES_USERNAME = listOf(
+ "email",
+ "tel",
+ "text",
+ )
+ private val HTML_INPUT_FIELD_TYPES_PASSWORD = listOf(
+ "password",
+ )
+ private val HTML_INPUT_FIELD_TYPES_OTP = listOf(
+ "tel",
+ "text",
+ )
private val HTML_INPUT_FIELD_TYPES_FILLABLE =
(HTML_INPUT_FIELD_TYPES_USERNAME + HTML_INPUT_FIELD_TYPES_PASSWORD + HTML_INPUT_FIELD_TYPES_OTP).toSet().toList()
@@ -95,20 +104,29 @@ class FormField(
"url_field", // Opera address bar
"location_bar_edit_text", // Samsung address bar
"search", "find", "captcha",
- "postal" // Prevent postal code fields from being mistaken for OTP fields
+ "postal", // Prevent postal code fields from being mistaken for OTP fields
)
private val PASSWORD_HEURISTIC_TERMS = listOf(
- "pass", "pswd", "pwd"
+ "pass",
+ "pswd",
+ "pwd",
)
private val USERNAME_HEURISTIC_TERMS = listOf(
- "alias", "e-mail", "email", "login", "user"
+ "alias",
+ "e-mail",
+ "email",
+ "login",
+ "user",
)
private val OTP_HEURISTIC_TERMS = listOf(
- "einmal", "otp"
+ "einmal",
+ "otp",
+ "challenge",
+ "verification",
)
private val OTP_WEAK_HEURISTIC_TERMS = listOf(
- "code"
+ "code",
)
}
@@ -214,7 +232,8 @@ class FormField(
// TODO: Revisit this decision in the future
private val excludedByHints = excludedByAutofillHints
- val relevantField = isTextField && hasAutofillTypeText && !excludedByHints
+ // Only offer to fill into custom views if they explicitly opted into Autofill.
+ val relevantField = hasAutofillTypeText && (isTextField || autofillHints.isNotEmpty()) && !excludedByHints
// Exclude fields based on hint, resource ID or HTML name.
// Note: We still report excluded fields as relevant since they count for adjacency heuristics,
@@ -232,7 +251,7 @@ class FormField(
if (isCertainPasswordField) CertaintyLevel.Certain else if (isLikelyPasswordField) CertaintyLevel.Likely else if (isPossiblePasswordField) CertaintyLevel.Possible else CertaintyLevel.Impossible
// OTP field heuristics (based only on the current field)
- private val isPossibleOtpField = notExcluded && !isPossiblePasswordField && isTextField
+ private val isPossibleOtpField = notExcluded && !isPossiblePasswordField
private val isCertainOtpField = isPossibleOtpField && hasHintOtp
private val isLikelyOtpField = isPossibleOtpField && (
isCertainOtpField || OTP_HEURISTIC_TERMS.anyMatchesFieldInfo ||
@@ -241,7 +260,7 @@ class FormField(
if (isCertainOtpField) CertaintyLevel.Certain else if (isLikelyOtpField) CertaintyLevel.Likely else if (isPossibleOtpField) CertaintyLevel.Possible else CertaintyLevel.Impossible
// Username field heuristics (based only on the current field)
- private val isPossibleUsernameField = notExcluded && !isPossiblePasswordField && !isCertainOtpField && isTextField
+ private val isPossibleUsernameField = notExcluded && !isPossiblePasswordField && !isCertainOtpField
private val isCertainUsernameField = isPossibleUsernameField && hasHintUsername
private val isLikelyUsernameField = isPossibleUsernameField && (isCertainUsernameField || (USERNAME_HEURISTIC_TERMS.anyMatchesFieldInfo))
val usernameCertainty =
@@ -252,7 +271,7 @@ class FormField(
}
infix fun directlyPrecedes(that: Iterable<FormField>): Boolean {
- val firstIndex = that.map { it.index }.min() ?: return false
+ val firstIndex = that.map { it.index }.minOrNull() ?: return false
return index == firstIndex - 1
}
@@ -261,7 +280,7 @@ class FormField(
}
infix fun directlyFollows(that: Iterable<FormField>): Boolean {
- val lastIndex = that.map { it.index }.max() ?: return false
+ val lastIndex = that.map { it.index }.maxOrNull() ?: return false
return index == lastIndex + 1
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/OreoAutofillService.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/OreoAutofillService.kt
index b6404c6e..fd2997d8 100644
--- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/OreoAutofillService.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/OreoAutofillService.kt
@@ -33,7 +33,7 @@ class OreoAutofillService : AutofillService() {
"com.android.settings.intelligence",
"com.android.systemui",
"com.oneplus.applocker",
- "org.sufficientlysecure.keychain"
+ "org.sufficientlysecure.keychain",
)
private const val DISABLE_AUTOFILL_DURATION_MS = 1000 * 60 * 60 * 24L
diff --git a/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt b/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt
index 615b695e..a05da554 100644
--- a/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt
@@ -11,7 +11,6 @@ import android.text.InputType
import android.view.Menu
import android.view.MenuItem
import android.view.View
-import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.IntentSenderRequest
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult
@@ -59,7 +58,7 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
private var oldCategory: String? = null
private var copy: Boolean = false
- private val userInteractionRequiredResult: ActivityResultLauncher<IntentSenderRequest> = registerForActivityResult(StartIntentSenderForResult()) { result ->
+ private val userInteractionRequiredResult = registerForActivityResult(StartIntentSenderForResult()) { result ->
if (result.data == null) {
setResult(RESULT_CANCELED, null)
finish()
@@ -315,7 +314,11 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
.filter { it.isNotBlank() }
.map { line ->
parseGpgIdentifier(line) ?: run {
- snackbar(message = resources.getString(R.string.invalid_gpg_id))
+ if (line.removePrefix("0x").matches("[a-fA-F0-9]{8}".toRegex())) {
+ snackbar(message = resources.getString(R.string.short_key_ids_unsupported))
+ } else {
+ snackbar(message = resources.getString(R.string.invalid_gpg_id))
+ }
return@with
}
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt b/app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt
index a8f4c5b3..7a818bcf 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt
@@ -41,6 +41,7 @@ abstract class BaseGitActivity : AppCompatActivity() {
lateinit var serverPath: String
lateinit var username: String
lateinit var email: String
+ lateinit var branch: String
private var identityBuilder: SshApiSessionFactory.IdentityBuilder? = null
private var identity: SshApiSessionFactory.ApiIdentity? = null
lateinit var settings: SharedPreferences
@@ -61,6 +62,7 @@ abstract class BaseGitActivity : AppCompatActivity() {
serverPath = settings.getString(PreferenceKeys.GIT_REMOTE_LOCATION, null) ?: ""
username = settings.getString(PreferenceKeys.GIT_CONFIG_USER_NAME, null) ?: ""
email = settings.getString(PreferenceKeys.GIT_CONFIG_USER_EMAIL, null) ?: ""
+ branch = settings.getString(PreferenceKeys.GIT_BRANCH_NAME, null) ?: "master"
updateUrl()
}
@@ -248,10 +250,9 @@ abstract class BaseGitActivity : AppCompatActivity() {
const val REQUEST_PULL = 101
const val REQUEST_PUSH = 102
const val REQUEST_CLONE = 103
- const val REQUEST_INIT = 104
- const val REQUEST_SYNC = 105
- const val BREAK_OUT_OF_DETACHED = 106
- const val REQUEST_RESET = 107
+ const val REQUEST_SYNC = 104
+ const val BREAK_OUT_OF_DETACHED = 105
+ const val REQUEST_RESET = 106
const val TAG = "AbstractGitActivity"
}
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/BreakOutOfDetached.kt b/app/src/main/java/com/zeapo/pwdstore/git/BreakOutOfDetached.kt
index 664497bd..149cabd8 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/BreakOutOfDetached.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/BreakOutOfDetached.kt
@@ -5,8 +5,10 @@
package com.zeapo.pwdstore.git
import androidx.appcompat.app.AppCompatActivity
+import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R
+import com.zeapo.pwdstore.utils.PreferenceKeys
import java.io.File
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.GitCommand
@@ -16,6 +18,9 @@ import org.eclipse.jgit.api.RebaseCommand
class BreakOutOfDetached(fileDir: File, callingActivity: AppCompatActivity) : GitOperation(fileDir, callingActivity) {
private lateinit var commands: List<GitCommand<out Any>>
+ private val gitBranch = PreferenceManager
+ .getDefaultSharedPreferences(callingActivity.applicationContext)
+ .getString(PreferenceKeys.GIT_BRANCH_NAME, "master")
/**
* Sets the command
@@ -24,7 +29,7 @@ class BreakOutOfDetached(fileDir: File, callingActivity: AppCompatActivity) : Gi
*/
fun setCommands(): BreakOutOfDetached {
val git = Git(repository)
- val branchName = "conflicting-master-${System.currentTimeMillis()}"
+ val branchName = "conflicting-$gitBranch-${System.currentTimeMillis()}"
this.commands = listOf(
// abort the rebase
@@ -33,8 +38,8 @@ class BreakOutOfDetached(fileDir: File, callingActivity: AppCompatActivity) : Gi
git.checkout().setCreateBranch(true).setName(branchName),
// push the changes
git.push().setRemote("origin"),
- // switch back to master
- git.checkout().setName("master")
+ // switch back to ${gitBranch}
+ git.checkout().setName(gitBranch)
)
return this
}
@@ -76,7 +81,7 @@ class BreakOutOfDetached(fileDir: File, callingActivity: AppCompatActivity) : Gi
MaterialAlertDialogBuilder(callingActivity)
.setTitle(callingActivity.resources.getString(R.string.git_abort_and_push_title))
.setMessage("There was a conflict when trying to rebase. " +
- "Your local master branch was pushed to another branch named conflicting-master-....\n" +
+ "Your local $gitBranch branch was pushed to another branch named conflicting-$gitBranch-....\n" +
"Use this branch to resolve conflict on your computer")
.setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ ->
callingActivity.finish()
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitConfigActivity.kt b/app/src/main/java/com/zeapo/pwdstore/git/GitConfigActivity.kt
index 35cbc68a..0498f6ed 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/GitConfigActivity.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/GitConfigActivity.kt
@@ -36,7 +36,7 @@ class GitConfigActivity : BaseGitActivity() {
if (repo != null) {
try {
val objectId = repo.resolve(Constants.HEAD)
- val ref = repo.getRef("refs/heads/master")
+ val ref = repo.getRef("refs/heads/$branch")
val head = if (ref.objectId.equals(objectId)) ref.name else "DETACHED"
binding.gitCommitHash.text = String.format("%s (%s)", objectId.abbreviate(8).name(), head)
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitOperation.kt b/app/src/main/java/com/zeapo/pwdstore/git/GitOperation.kt
index 9d2c629f..90f72b7c 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/GitOperation.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/GitOperation.kt
@@ -9,12 +9,12 @@ import android.content.Intent
import android.view.LayoutInflater
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
-import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import com.google.android.material.checkbox.MaterialCheckBox
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputEditText
+import com.google.android.material.textfield.TextInputLayout
import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.UserPreference
import com.zeapo.pwdstore.git.config.ConnectionMode
@@ -37,7 +37,6 @@ import org.eclipse.jgit.transport.CredentialItem
import org.eclipse.jgit.transport.CredentialsProvider
import org.eclipse.jgit.transport.SshSessionFactory
import org.eclipse.jgit.transport.URIish
-import com.google.android.material.R as materialR
private class GitOperationCredentialFinder(
@@ -78,13 +77,13 @@ private class GitOperationCredentialFinder(
@SuppressLint("InflateParams")
val dialogView = layoutInflater.inflate(R.layout.git_credential_layout, null)
+ val credentialLayout = dialogView.findViewById<TextInputLayout>(R.id.git_auth_passphrase_layout)
val editCredential = dialogView.findViewById<TextInputEditText>(R.id.git_auth_credential)
editCredential.setHint(hintRes)
val rememberCredential = dialogView.findViewById<MaterialCheckBox>(R.id.git_auth_remember_credential)
rememberCredential.setText(rememberRes)
if (isRetry)
- editCredential.setError(callingActivity.resources.getString(errorRes),
- ContextCompat.getDrawable(callingActivity, materialR.drawable.mtrl_ic_error))
+ credentialLayout.error = callingActivity.resources.getString(errorRes)
MaterialAlertDialogBuilder(callingActivity).run {
setTitle(R.string.passphrase_dialog_title)
setMessage(messageRes)
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitServerConfigActivity.kt b/app/src/main/java/com/zeapo/pwdstore/git/GitServerConfigActivity.kt
index 10f44960..6c87e0f6 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/GitServerConfigActivity.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/GitServerConfigActivity.kt
@@ -102,6 +102,13 @@ class GitServerConfigActivity : BaseGitActivity() {
}
}
+ binding.serverBranch.apply {
+ setText(branch)
+ doOnTextChanged { text, _, _, _ ->
+ branch = text.toString().trim()
+ }
+ }
+
binding.saveButton.setOnClickListener {
if (isClone && PasswordRepository.getRepository(null) == null)
PasswordRepository.initialize(this)
@@ -114,12 +121,14 @@ class GitServerConfigActivity : BaseGitActivity() {
putString(PreferenceKeys.GIT_REMOTE_PORT, serverPort)
putString(PreferenceKeys.GIT_REMOTE_USERNAME, serverUser)
putString(PreferenceKeys.GIT_REMOTE_LOCATION, serverPath)
+ putString(PreferenceKeys.GIT_BRANCH_NAME, branch)
}
if (!isClone) {
Snackbar.make(binding.root, getString(R.string.git_server_config_save_success), Snackbar.LENGTH_SHORT).show()
Handler().postDelayed(500) { finish() }
- } else
+ } else {
cloneRepository()
+ }
}
else -> Snackbar.make(binding.root, getString(R.string.git_server_config_save_error_prefix, getString(result.textRes)), Snackbar.LENGTH_LONG).show()
}
@@ -157,7 +166,7 @@ class GitServerConfigActivity : BaseGitActivity() {
!(localDirFiles.size == 1 && localDirFiles[0].name == ".git")) {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.dialog_delete_title)
- .setMessage(resources.getString(R.string.dialog_delete_msg) + " " + localDir.toString())
+ .setMessage(resources.getString(R.string.dialog_delete_msg, localDir.toString()))
.setCancelable(false)
.setPositiveButton(R.string.dialog_delete) { dialog, _ ->
try {
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/ResetToRemoteOperation.kt b/app/src/main/java/com/zeapo/pwdstore/git/ResetToRemoteOperation.kt
index c570bf27..60a9fbf3 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/ResetToRemoteOperation.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/ResetToRemoteOperation.kt
@@ -6,13 +6,15 @@ package com.zeapo.pwdstore.git
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
+import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R
+import com.zeapo.pwdstore.utils.PreferenceKeys
import java.io.File
-import org.eclipse.jgit.api.AddCommand
-import org.eclipse.jgit.api.FetchCommand
import org.eclipse.jgit.api.Git
+import org.eclipse.jgit.api.GitCommand
import org.eclipse.jgit.api.ResetCommand
+import org.eclipse.jgit.api.TransportCommand
/**
* Creates a new git operation
@@ -22,9 +24,7 @@ import org.eclipse.jgit.api.ResetCommand
*/
class ResetToRemoteOperation(fileDir: File, callingActivity: AppCompatActivity) : GitOperation(fileDir, callingActivity) {
- private var addCommand: AddCommand? = null
- private var fetchCommand: FetchCommand? = null
- private var resetCommand: ResetCommand? = null
+ private lateinit var commands: List<GitCommand<out Any>>
/**
* Sets the command
@@ -32,17 +32,27 @@ class ResetToRemoteOperation(fileDir: File, callingActivity: AppCompatActivity)
* @return the current object
*/
fun setCommands(): ResetToRemoteOperation {
+ val remoteBranch = PreferenceManager
+ .getDefaultSharedPreferences(callingActivity.applicationContext)
+ .getString(PreferenceKeys.GIT_BRANCH_NAME, "master")
val git = Git(repository)
- this.addCommand = git.add().addFilepattern(".")
- this.fetchCommand = git.fetch().setRemote("origin")
- this.resetCommand = git.reset().setRef("origin/master").setMode(ResetCommand.ResetType.HARD)
+ val cmds = arrayListOf(
+ git.add().addFilepattern("."),
+ git.fetch().setRemote("origin"),
+ git.reset().setRef("origin/$remoteBranch").setMode(ResetCommand.ResetType.HARD)
+ )
+ if (git.branchList().call().none { it.name == remoteBranch }) {
+ cmds.add(
+ git.branchCreate().setName(remoteBranch).setForce(true)
+ )
+ }
+ commands = cmds
return this
}
override fun execute() {
- this.fetchCommand?.setCredentialsProvider(this.provider)
- GitAsyncTask(callingActivity, this, Intent())
- .execute(this.addCommand, this.fetchCommand, this.resetCommand)
+ commands.filterIsInstance<TransportCommand<*, *>>().map { it.setCredentialsProvider(provider) }
+ GitAsyncTask(callingActivity, this, Intent()).execute(*commands.toTypedArray())
}
override fun onError(err: Exception) {
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/config/SshjConfig.kt b/app/src/main/java/com/zeapo/pwdstore/git/config/SshjConfig.kt
index 7ba246be..1ea0359c 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/config/SshjConfig.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/config/SshjConfig.kt
@@ -217,7 +217,7 @@ class SshjConfig : ConfigImpl() {
ECDHNistP.Factory521(),
ECDHNistP.Factory384(),
ECDHNistP.Factory256(),
- DHGexSHA256.Factory()
+ DHGexSHA256.Factory(),
)
}
@@ -228,7 +228,7 @@ class SshjConfig : ConfigImpl() {
SignatureECDSA.Factory384(),
SignatureECDSA.Factory521(),
SignatureRSA.Factory(),
- FactoryCERT()
+ FactoryCERT(),
)
}
@@ -242,7 +242,7 @@ class SshjConfig : ConfigImpl() {
PKCS8KeyFile.Factory(),
PKCS5KeyFile.Factory(),
OpenSSHKeyFile.Factory(),
- PuTTYKeyFile.Factory()
+ PuTTYKeyFile.Factory(),
)
}
@@ -251,7 +251,7 @@ class SshjConfig : ConfigImpl() {
cipherFactories = listOf(
BlockCiphers.AES128CTR(),
BlockCiphers.AES192CTR(),
- BlockCiphers.AES256CTR()
+ BlockCiphers.AES256CTR(),
)
}
@@ -260,13 +260,13 @@ class SshjConfig : ConfigImpl() {
Macs.HMACSHA2256(),
Macs.HMACSHA2256Etm(),
Macs.HMACSHA2512(),
- Macs.HMACSHA2512Etm()
+ Macs.HMACSHA2512Etm(),
)
}
private fun initCompressionFactories() {
compressionFactories = listOf(
- NoneCompression.Factory()
+ NoneCompression.Factory(),
)
}
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/pwgen/RandomPasswordGenerator.kt b/app/src/main/java/com/zeapo/pwdstore/pwgen/RandomPasswordGenerator.kt
index e841ed76..67d8fc40 100644
--- a/app/src/main/java/com/zeapo/pwdstore/pwgen/RandomPasswordGenerator.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/pwgen/RandomPasswordGenerator.kt
@@ -22,14 +22,13 @@ object RandomPasswordGenerator {
* set, the password will not contain any symbols.
* - [PasswordGenerator.NO_AMBIGUOUS]: If set, the password will not contain any ambiguous
* characters.
- * - [PasswordGenerator.NO_VOWELS]: If set, the password will not contain any vowels.
*/
fun generate(targetLength: Int, pwFlags: Int): String? {
val bank = listOfNotNull(
PasswordGenerator.DIGITS_STR.takeIf { pwFlags hasFlag PasswordGenerator.DIGITS },
PasswordGenerator.UPPERS_STR.takeIf { pwFlags hasFlag PasswordGenerator.UPPERS },
PasswordGenerator.LOWERS_STR.takeIf { pwFlags hasFlag PasswordGenerator.LOWERS },
- PasswordGenerator.SYMBOLS_STR.takeIf { pwFlags hasFlag PasswordGenerator.SYMBOLS }
+ PasswordGenerator.SYMBOLS_STR.takeIf { pwFlags hasFlag PasswordGenerator.SYMBOLS },
).joinToString("")
var password = ""
diff --git a/app/src/main/java/com/zeapo/pwdstore/ui/adapters/PasswordItemRecyclerAdapter.kt b/app/src/main/java/com/zeapo/pwdstore/ui/adapters/PasswordItemRecyclerAdapter.kt
index 15e13745..4e153b88 100644
--- a/app/src/main/java/com/zeapo/pwdstore/ui/adapters/PasswordItemRecyclerAdapter.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/ui/adapters/PasswordItemRecyclerAdapter.kt
@@ -52,7 +52,15 @@ open class PasswordItemRecyclerAdapter :
val settings =
PreferenceManager.getDefaultSharedPreferences(itemView.context.applicationContext)
val showHidden = settings.getBoolean(PreferenceKeys.SHOW_HIDDEN_FOLDERS, false)
- name.text = item.toString()
+ val parentPath = item.fullPathToParent.replace("(^/)|(/$)".toRegex(), "")
+ val source = if (parentPath.isNotEmpty()) {
+ "$parentPath\n$item"
+ } else {
+ "$item"
+ }
+ val spannable = SpannableString(source)
+ spannable.setSpan(RelativeSizeSpan(0.7f), 0, parentPath.length, 0)
+ name.text = spannable
if (item.type == PasswordItem.TYPE_CATEGORY) {
folderIndicator.visibility = View.VISIBLE
val children = item.file.listFiles { pathname ->
@@ -62,15 +70,6 @@ open class PasswordItemRecyclerAdapter :
childCount.visibility = if (count > 0) View.VISIBLE else View.GONE
childCount.text = "$count"
} else {
- val parentPath = item.fullPathToParent.replace("(^/)|(/$)".toRegex(), "")
- val source = if (parentPath.isNotEmpty()) {
- "$parentPath\n$item"
- } else {
- "$item"
- }
- val spannable = SpannableString(source)
- spannable.setSpan(RelativeSizeSpan(0.7f), 0, parentPath.length, 0)
- name.text = spannable
childCount.visibility = View.GONE
folderIndicator.visibility = View.GONE
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/FolderCreationDialogFragment.kt b/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/FolderCreationDialogFragment.kt
index 5776187b..25a935bb 100644
--- a/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/FolderCreationDialogFragment.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/ui/dialogs/FolderCreationDialogFragment.kt
@@ -37,7 +37,7 @@ class FolderCreationDialogFragment : DialogFragment() {
val materialTextView = dialog.findViewById<TextInputEditText>(R.id.folder_name_text)
val folderName = materialTextView.text.toString()
val newFolder = File("$currentDir/$folderName")
- newFolder.mkdir()
+ newFolder.mkdirs()
(requireActivity() as PasswordStore).refreshPasswordList(newFolder)
dismiss()
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/PreferenceKeys.kt b/app/src/main/java/com/zeapo/pwdstore/utils/PreferenceKeys.kt
index 9eb549da..24bf2096 100644
--- a/app/src/main/java/com/zeapo/pwdstore/utils/PreferenceKeys.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/utils/PreferenceKeys.kt
@@ -35,6 +35,7 @@ object PreferenceKeys {
const val GIT_REMOTE_SERVER = "git_remote_server"
const val GIT_REMOTE_USERNAME = "git_remote_username"
const val GIT_SERVER_INFO = "git_server_info"
+ const val GIT_BRANCH_NAME = "git_branch"
const val HTTPS_PASSWORD = "https_password"
const val LENGTH = "length"
const val OREO_AUTOFILL_CUSTOM_PUBLIC_SUFFIXES = "oreo_autofill_custom_public_suffixes"
diff --git a/app/src/main/res/layout/activity_git_clone.xml b/app/src/main/res/layout/activity_git_clone.xml
index 8026fa4a..f9bec00a 100644
--- a/app/src/main/res/layout/activity_git_clone.xml
+++ b/app/src/main/res/layout/activity_git_clone.xml
@@ -20,7 +20,7 @@
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/server_label"
style="@style/TextAppearance.MaterialComponents.Headline5"
- android:layout_width="match_parent"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/server_name"
@@ -32,7 +32,7 @@
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/label_server_protocol"
style="@style/TextAppearance.MaterialComponents.Headline6"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/server_protocol"
@@ -42,7 +42,7 @@
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/clone_protocol_group"
style="@style/TextAppearance.MaterialComponents.Headline1"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:layout_constraintStart_toStartOf="parent"
@@ -67,10 +67,12 @@
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/server_user_layout"
- android:layout_width="match_parent"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:hint="@string/server_user"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/clone_protocol_group">
<com.google.android.material.textfield.TextInputEditText
@@ -125,10 +127,11 @@
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/label_server_path"
- android:layout_width="match_parent"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:hint="@string/server_path"
+ app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/label_server_url">
@@ -136,11 +139,31 @@
android:id="@+id/server_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:imeOptions="actionDone"
+ android:imeOptions="actionNext"
+ android:nextFocusForward="@id/server_branch"
android:inputType="textWebEmailAddress" />
</com.google.android.material.textfield.TextInputLayout>
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/label_server_branch"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:hint="Branch"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/label_server_path">
+
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/server_branch"
+ android:imeOptions="actionDone"
+ android:layout_width="match_parent"
+ android:inputType="textNoSuggestions"
+ android:layout_height="wrap_content" />
+
+ </com.google.android.material.textfield.TextInputLayout>
+
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/label_connection_mode"
style="@style/TextAppearance.MaterialComponents.Headline6"
@@ -151,7 +174,7 @@
android:layout_marginBottom="16dp"
android:text="@string/connection_mode"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/label_server_path" />
+ app:layout_constraintTop_toBottomOf="@id/label_server_branch" />
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/connection_mode_group"
diff --git a/app/src/main/res/layout/git_credential_layout.xml b/app/src/main/res/layout/git_credential_layout.xml
index 757658e6..180b904a 100644
--- a/app/src/main/res/layout/git_credential_layout.xml
+++ b/app/src/main/res/layout/git_credential_layout.xml
@@ -15,6 +15,7 @@
android:layout_height="wrap_content"
app:endIconMode="password_toggle"
app:hintEnabled="true"
+ app:errorEnabled="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a59d5965..9aaebf98 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -368,6 +368,7 @@
<string name="exporting_passwords">Exporting passwords…</string>
<string name="failed_to_find_key_id">Failed to locate .gpg-id, is your store set up correctly?</string>
<string name="invalid_gpg_id">Found .gpg-id, but it contains an invalid key ID, fingerprint or user ID</string>
+ <string name="short_key_ids_unsupported">A key ID in .gpg-id is too short, please use either long key IDs (16 characters) or fingerprints (40 characters)</string>
<string name="invalid_filename_text">File name must not contain \'/\', set directory above</string>
<string name="directory_hint">Directory</string>
</resources>
diff --git a/app/src/main/res/xml/oreo_autofill_service.xml b/app/src/main/res/xml/oreo_autofill_service.xml
index a790da21..8b76c803 100644
--- a/app/src/main/res/xml/oreo_autofill_service.xml
+++ b/app/src/main/res/xml/oreo_autofill_service.xml
@@ -3,7 +3,9 @@
~ SPDX-License-Identifier: GPL-3.0-only
-->
-<autofill-service xmlns:android="http://schemas.android.com/apk/res/android">
+<autofill-service xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:ignore="UnusedAttribute">
<compatibility-package android:name="com.android.chrome" />
<compatibility-package android:name="com.brave.browser" />
<compatibility-package android:name="com.chrome.beta" />
@@ -12,7 +14,13 @@
<compatibility-package android:name="com.microsoft.emmx" />
<compatibility-package android:name="com.opera.mini.native" />
<compatibility-package android:name="com.opera.mini.native.beta" />
- <compatibility-package android:name="org.mozilla.fennec_fdroid" />
- <compatibility-package android:name="org.mozilla.firefox" />
- <compatibility-package android:name="org.mozilla.firefox_beta" />
+ <compatibility-package
+ android:name="org.mozilla.fennec_fdroid"
+ android:maxLongVersionCode="679999" />
+ <compatibility-package
+ android:name="org.mozilla.firefox"
+ android:maxLongVersionCode="679999" />
+ <compatibility-package
+ android:name="org.mozilla.firefox_beta"
+ android:maxLongVersionCode="679999" />
</autofill-service>