Treewide codestyle cleanup (#765)

- Updated gitignore and checked in the IDE's codestyle config
- Removed spotless as the underlying ktlint backend has failed to resolve the super frustrating import order bug[1] in nearly a year
- Reformat the entire codebase based on the previously committed code style configuration.

1: https://github.com/pinterest/ktlint/issues/527
This commit is contained in:
Harsh Shandilya 2020-05-10 19:21:39 +05:30 committed by GitHub
parent 94dc92f8d7
commit 041cf00510
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
136 changed files with 1603 additions and 1255 deletions

View file

@ -20,7 +20,7 @@
## :pencil: Checklist ## :pencil: Checklist
<!--- Put an `x` in the boxes that apply --> <!--- Put an `x` in the boxes that apply -->
- [ ] I ran `./gradlew spotlessApply` before submitting the PR - [ ] I formatted the code with the IDE's reformat action (Ctrl + Shift + L/Cmd + Shift + L)
- [ ] I reviewed submitted code - [ ] I reviewed submitted code

View file

@ -28,9 +28,6 @@ jobs:
- name: Download gradle dependencies - name: Download gradle dependencies
run: ./gradlew dependencies run: ./gradlew dependencies
- name: Validate codestyle with Spotless
run: ./gradlew spotlessCheck
- name: Build release app - name: Build release app
run: ./gradlew :app:assembleRelease run: ./gradlew :app:assembleRelease
env: env:

View file

@ -52,7 +52,7 @@ jobs:
${{ runner.os }}-gradlebuildcache- ${{ runner.os }}-gradlebuildcache-
- name: Run unit tests - name: Run unit tests
run: ./gradlew spotlessCheck test${{ matrix.variant }} lint${{ matrix.variant}} -Dpre-dex=false run: ./gradlew test${{ matrix.variant }} lint${{ matrix.variant}} -Dpre-dex=false
- name: Run instrumentation tests - name: Run instrumentation tests
uses: reactivecircus/android-emulator-runner@v2 uses: reactivecircus/android-emulator-runner@v2

175
.gitignore vendored
View file

@ -1,42 +1,153 @@
#Android specific
bin
gen
obj
lint.xml
local.properties
release.properties
ant.properties
*.class
*.apk
!app-release.apk
!app-debug.apk
#Gradle # Created by https://www.gitignore.io/api/androidstudio,gradle
# Edit at https://www.gitignore.io/?templates=androidstudio,gradle
### Gradle ###
.gradle .gradle
build build/
.DS_Store
#Maven # Ignore Gradle GUI config
target gradle-app.setting
pom.xml.*
#Eclipse # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
.project !gradle-wrapper.jar
.classpath
.settings
.metadata
#Eclipse/Gradle integration # Cache of project
libs .gradletasknamecache
project.properties
#IntelliJ IDEA # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
.idea # gradle/wrapper/gradle-wrapper.properties
*.iml
# Visual Studio Code ### Gradle Patch ###
.vscode/ **/build/
captures/ ### AndroidStudio ###
# Covers files to be ignored for android development using Android Studio.
# Built application files
*.apk
*.ap_
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Gradle files
.gradle/
# Signing files
.signing/
keystore.* keystore.*
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio
/*/build/
/*/local.properties
/*/out
/*/*/build
/*/*/production
captures/
.navigation/
*.ipr
*~
*.swp
# Android Patch
gen-external-apklibs
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
# NDK
obj/
# IntelliJ IDEA
*.iml
*.iws
/out/
# User-specific configurations
.idea/caches/
.idea/libraries/
.idea/shelf/
.idea/workspace.xml
.idea/tasks.xml
.idea/.name
.idea/compiler.xml
.idea/copyright/profiles_settings.xml
.idea/encodings.xml
.idea/misc.xml
.idea/modules.xml
.idea/scopes/scope_settings.xml
.idea/dictionaries
.idea/vcs.xml
.idea/jsLibraryMappings.xml
.idea/datasources.xml
.idea/dataSources.ids
.idea/sqlDataSources.xml
.idea/dynamic.xml
.idea/uiDesigner.xml
.idea/assetWizardSettings.xml
# OS-specific files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Legacy Eclipse project files
.classpath
.project
.cproject
.settings/
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.war
*.ear
# virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
hs_err_pid*
## Plugin-specific files:
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Mongo Explorer plugin
.idea/mongoSettings.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### AndroidStudio Patch ###
!/gradle/wrapper/gradle-wrapper.jar
# End of https://www.gitignore.io/api/androidstudio,gradle

168
.idea/codeStyles/Project.xml generated Normal file
View file

@ -0,0 +1,168 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="AUTODETECT_INDENTS" value="false" />
<option name="LINE_SEPARATOR" value="&#10;" />
<option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
<option name="FORMATTER_TAGS_ENABLED" value="true" />
<option name="SOFT_MARGINS" value="100" />
<JavaCodeStyleSettings>
<option name="DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION" value="true" />
</JavaCodeStyleSettings>
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value />
</option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
<option name="CONTINUATION_INDENT_IN_PARAMETER_LISTS" value="true" />
<option name="CONTINUATION_INDENT_IN_ARGUMENT_LISTS" value="true" />
<option name="CONTINUATION_INDENT_FOR_EXPRESSION_BODIES" value="true" />
<option name="CONTINUATION_INDENT_FOR_CHAINED_CALLS" value="true" />
<option name="CONTINUATION_INDENT_IN_SUPERTYPE_LISTS" value="true" />
<option name="CONTINUATION_INDENT_IN_IF_CONDITIONS" value="true" />
<option name="CONTINUATION_INDENT_IN_ELVIS" value="true" />
<option name="WRAP_EXPRESSION_BODY_FUNCTIONS" value="0" />
<option name="IF_RPAREN_ON_NEW_LINE" value="false" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="JAVA">
<option name="METHOD_ANNOTATION_WRAP" value="0" />
<option name="FIELD_ANNOTATION_WRAP" value="0" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="XML">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
<option name="RIGHT_MARGIN" value="100" />
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
<option name="CALL_PARAMETERS_WRAP" value="0" />
<option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="false" />
<option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="false" />
<option name="METHOD_PARAMETERS_WRAP" value="0" />
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="false" />
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="false" />
<option name="EXTENDS_LIST_WRAP" value="0" />
<option name="METHOD_CALL_CHAIN_WRAP" value="0" />
<option name="ASSIGNMENT_WRAP" value="0" />
<option name="METHOD_ANNOTATION_WRAP" value="0" />
<option name="FIELD_ANNOTATION_WRAP" value="0" />
<option name="WRAP_ON_TYPING" value="0" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View file

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

6
.idea/copyright/APS.xml generated Normal file
View file

@ -0,0 +1,6 @@
<component name="CopyrightManager">
<copyright>
<option name="notice" value="/*&#10; * Copyright © 2014-&amp;#36;today.year The Android Password Store Authors. All Rights Reserved.&#10; * SPDX-License-Identifier: GPL-3.0-only&#10; */&#10;" />
<option name="myName" value="APS" />
</copyright>
</component>

22
.idea/gradle.xml generated Normal file
View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="PLATFORM" />
<option name="distributionType" value="LOCAL" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="$USER_HOME$/.sdkman/candidates/gradle/current" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
<option name="useQualifiedModuleNames" value="true" />
</GradleProjectSettings>
</option>
</component>
</project>

12
.idea/runConfigurations.xml generated Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

View file

@ -4,11 +4,11 @@
*/ */
package com.zeapo.pwdstore package com.zeapo.pwdstore
import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertNull import kotlin.test.assertNull
import kotlin.test.assertTrue import kotlin.test.assertTrue
import org.junit.Test
class PasswordEntryTest { class PasswordEntryTest {
@Test fun testGetPassword() { @Test fun testGetPassword() {
@ -39,13 +39,13 @@ class PasswordEntryTest {
assertEquals("username", PasswordEntry("\n${field.toUpperCase()} username").username) assertEquals("username", PasswordEntry("\n${field.toUpperCase()} username").username)
} }
assertEquals( assertEquals(
"username", "username",
PasswordEntry("secret\nextra\nlogin: username\ncontent\n").username) PasswordEntry("secret\nextra\nlogin: username\ncontent\n").username)
assertEquals( assertEquals(
"username", "username",
PasswordEntry("\nextra\nusername: username\ncontent\n").username) PasswordEntry("\nextra\nusername: username\ncontent\n").username)
assertEquals( assertEquals(
"username", PasswordEntry("\nUSERNaMe: username\ncontent\n").username) "username", PasswordEntry("\nUSERNaMe: username\ncontent\n").username)
assertEquals("username", PasswordEntry("\nlogin: username").username) assertEquals("username", PasswordEntry("\nlogin: username").username)
assertEquals("foo@example.com", PasswordEntry("\nemail: foo@example.com").username) assertEquals("foo@example.com", PasswordEntry("\nemail: foo@example.com").username)
assertEquals("username", PasswordEntry("\nidentity: username\nlogin: another_username").username) assertEquals("username", PasswordEntry("\nidentity: username\nlogin: another_username").username)
@ -75,7 +75,7 @@ class PasswordEntryTest {
@Test fun testTotpUriInContent() { @Test fun testTotpUriInContent() {
val entry = PasswordEntry( val entry = PasswordEntry(
"secret\nusername: test\notpauth://totp/test?secret=JBSWY3DPEHPK3PXP") "secret\nusername: test\notpauth://totp/test?secret=JBSWY3DPEHPK3PXP")
assertTrue(entry.hasTotp()) assertTrue(entry.hasTotp())
assertEquals("JBSWY3DPEHPK3PXP", entry.totpSecret) assertEquals("JBSWY3DPEHPK3PXP", entry.totpSecret)
} }
@ -96,7 +96,7 @@ class PasswordEntryTest {
@Test fun testHotpUriInContent() { @Test fun testHotpUriInContent() {
val entry = PasswordEntry( val entry = PasswordEntry(
"secret\nusername: test\notpauth://hotp/test?secret=JBSWY3DPEHPK3PXP&counter=25") "secret\nusername: test\notpauth://hotp/test?secret=JBSWY3DPEHPK3PXP&counter=25")
assertTrue(entry.hasHotp()) assertTrue(entry.hasHotp())
assertEquals("JBSWY3DPEHPK3PXP", entry.hotpSecret) assertEquals("JBSWY3DPEHPK3PXP", entry.hotpSecret)
assertEquals(25, entry.hotpCounter) assertEquals(25, entry.hotpCounter)

View file

@ -4,10 +4,10 @@
*/ */
package com.zeapo.pwdstore package com.zeapo.pwdstore
import org.junit.Test
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertNull import kotlin.test.assertNull
import kotlin.test.assertTrue import kotlin.test.assertTrue
import org.junit.Test
private infix fun String.matchedForDomain(domain: String) = private infix fun String.matchedForDomain(domain: String) =
SearchableRepositoryViewModel.generateStrictDomainRegex(domain)?.containsMatchIn(this) == true SearchableRepositoryViewModel.generateStrictDomainRegex(domain)?.containsMatchIn(this) == true

View file

@ -3,23 +3,24 @@
android:height="108dp" android:height="108dp"
android:viewportWidth="110.34687" android:viewportWidth="110.34687"
android:viewportHeight="110.34687"> android:viewportHeight="110.34687">
<group android:translateX="24.828047" <group
android:translateY="24.828047"> android:translateX="24.828047"
<path android:translateY="24.828047">
android:pathData="m18.8,30.2129v-11.546c0,-6.4144 5.1315,-11.546 11.546,-11.546 6.4144,0 11.546,5.1315 11.546,11.546v11.546" <path
android:strokeWidth="5.349" android:fillColor="#00000000"
android:fillColor="#00000000" android:pathData="m18.8,30.2129v-11.546c0,-6.4144 5.1315,-11.546 11.546,-11.546 6.4144,0 11.546,5.1315 11.546,11.546v11.546"
android:strokeColor="#013e5b"/> android:strokeWidth="5.349"
<path android:strokeColor="#013e5b" />
android:pathData="M15.4099,21.8429L45.2811,21.8429A2.2639,2.2639 0,0 1,47.545 24.1068L47.545,53.977A2.2639,2.2639 0,0 1,45.2811 56.2409L15.4099,56.2409A2.2639,2.2639 0,0 1,13.146 53.977L13.146,24.1068A2.2639,2.2639 0,0 1,15.4099 21.8429z" <path
android:fillColor="#c74c00"/> android:fillColor="#c74c00"
<path android:pathData="M15.4099,21.8429L45.2811,21.8429A2.2639,2.2639 0,0 1,47.545 24.1068L47.545,53.977A2.2639,2.2639 0,0 1,45.2811 56.2409L15.4099,56.2409A2.2639,2.2639 0,0 1,13.146 53.977L13.146,24.1068A2.2639,2.2639 0,0 1,15.4099 21.8429z" />
android:pathData="m44.8267,37.6961 l-13.1408,-13.1393c-0.7569,-0.7566 -1.9838,-0.7566 -2.7408,0l-13.08,13.0785c-0.7567,0.7573 -0.7567,1.9846 0,2.7419l13.1415,13.14c0.7572,0.7567 1.9842,0.7567 2.7414,0l13.0778,-13.0785c0.7572,-0.7572 0.7572,-1.9849 0,-2.7421" <path
android:strokeWidth=".35344" android:fillColor="#fff"
android:fillColor="#fff"/> android:pathData="m44.8267,37.6961 l-13.1408,-13.1393c-0.7569,-0.7566 -1.9838,-0.7566 -2.7408,0l-13.08,13.0785c-0.7567,0.7573 -0.7567,1.9846 0,2.7419l13.1415,13.14c0.7572,0.7567 1.9842,0.7567 2.7414,0l13.0778,-13.0785c0.7572,-0.7572 0.7572,-1.9849 0,-2.7421"
<path android:strokeWidth=".35344" />
android:pathData="m30.3156,23.9881c-0.496,0 -0.992,0.1893 -1.3705,0.5676l-2.7282,2.7288 3.4612,3.4606c0.8044,-0.2715 1.727,-0.0892 2.368,0.5517 0.6237,0.624 0.8361,1.5493 0.5471,2.3828l3.3357,3.3357c0.8076,-0.2777 1.738,-0.098 2.3828,0.5476 0.9008,0.9005 0.9008,2.361 0,3.2615 -1.7823,1.7848 -4.7253,-0.1767 -3.7641,-2.5087l-3.1111,-3.1106c-2.2315,0.5285 -3.8934,-1.2655 -3.149,-3.1674l-0.6863,-0.6863l0,15.9165l5.4913,0l0,-5.8608c-0.0315,-0.7566 1.1201,-0.7566 1.0886,0l0,6.4043c0.0005,0.3013 -0.2438,0.5457 -0.545,0.5455l-6.5804,0c-0.3015,0.0005 -0.546,-0.2441 -0.5456,-0.5455l0,-17.4333c-0.0005,-0.0363 0.0029,-0.0728 0.0097,-0.1085l-1.6444,-1.6444 -9.0106,9.0084c-0.7567,0.7573 -0.7567,1.9848 0,2.7421l13.1415,13.14c0.7572,0.7567 1.9844,0.7567 2.7416,0l13.0778,-13.0785c0.7572,-0.7572 0.7572,-1.9849 0,-2.7421l-13.14,-13.1393c-0.3785,-0.3783 -0.8746,-0.5676 -1.3705,-0.5676zM29.9512,39.1825c0.1001,0 0.1808,0.0381 0.2426,0.1146 0.0648,0.0736 0.1326,0.1975 0.2032,0.371 0.0705,0.1706 0.1089,0.2615 0.1146,0.2733 0.0059,-0.0119 0.0424,-0.1026 0.11,-0.2733 0.0707,-0.1705 0.1401,-0.2946 0.2078,-0.371 0.0677,-0.0765 0.1513,-0.1146 0.2513,-0.1146 0.1412,0 0.2646,0.047 0.3705,0.1412 0.1059,0.0941 0.1592,0.2103 0.1592,0.3485 0,0.0676 -0.0179,0.1368 -0.0532,0.2073 -0.0323,0.0707 -0.0777,0.1444 -0.1366,0.2211 -0.056,0.0734 -0.1164,0.1571 -0.1812,0.2513 0.0648,-0.0089 0.1589,-0.0251 0.2825,-0.0486 0.1265,-0.0236 0.2268,-0.0354 0.3004,-0.0354 0.0969,0 0.1762,0.0224 0.238,0.0666 0.0648,0.0442 0.1118,0.1042 0.1413,0.1807 0.0294,0.0734 0.044,0.1544 0.044,0.2426 0,0.1384 -0.0337,0.2544 -0.1013,0.3485 -0.0677,0.0912 -0.1749,0.1372 -0.3219,0.1372 -0.0472,0 -0.14,-0.0119 -0.2784,-0.0354 -0.1382,-0.0236 -0.2397,-0.0386 -0.3045,-0.0446 0.1531,0.2177 0.2534,0.3652 0.3004,0.4417 0.047,0.0736 0.0706,0.1544 0.0706,0.2426 0,0.1412 -0.0533,0.2556 -0.1592,0.3439 -0.1028,0.0853 -0.2264,0.1279 -0.3705,0.1279 -0.1028,0 -0.1882,-0.0366 -0.2559,-0.11 -0.0646,-0.0767 -0.1309,-0.1955 -0.1986,-0.3572 -0.0646,-0.1647 -0.1028,-0.259 -0.1146,-0.2825 -0.0117,0.0235 -0.0516,0.1178 -0.1192,0.2825 -0.0648,0.1646 -0.1281,0.2836 -0.1899,0.3572 -0.0617,0.0735 -0.1454,0.11 -0.2513,0.11 -0.15,0 -0.2779,-0.0427 -0.3838,-0.1279 -0.1059,-0.0883 -0.1587,-0.2027 -0.1587,-0.3439 0,-0.0618 0.0156,-0.1263 0.0481,-0.194 0.0323,-0.0707 0.0665,-0.1311 0.1018,-0.1812 0.0382,-0.0499 0.1118,-0.1532 0.2206,-0.3091 -0.0736,0.0059 -0.1751,0.021 -0.3045,0.0446 -0.1294,0.0236 -0.2176,0.0354 -0.2646,0.0354 -0.1472,0 -0.2577,-0.046 -0.3311,-0.1372 -0.0707,-0.0941 -0.1059,-0.2101 -0.1059,-0.3485 0,-0.1411 0.0352,-0.2573 0.1059,-0.3485 0.0734,-0.0942 0.184,-0.1412 0.3311,-0.1412 0.0736,0 0.1628,0.0107 0.2687,0.0312 0.1059,0.0206 0.2062,0.038 0.3004,0.0527 -0.0824,-0.1177 -0.1648,-0.2366 -0.2472,-0.3572 -0.0824,-0.1206 -0.1233,-0.2282 -0.1233,-0.3224 0,-0.1382 0.0543,-0.2544 0.1633,-0.3485 0.1088,-0.0942 0.2351,-0.1412 0.3792,-0.1412zM29.9512,43.2235c0.1001,0 0.1808,0.0383 0.2426,0.1146 0.0648,0.0735 0.1326,0.197 0.2032,0.3705 0.0705,0.1707 0.1089,0.262 0.1146,0.2738 0.0059,-0.0119 0.0424,-0.1031 0.11,-0.2738 0.0707,-0.1705 0.1401,-0.2941 0.2078,-0.3705 0.0677,-0.0765 0.1513,-0.1146 0.2513,-0.1146 0.1412,0 0.2646,0.047 0.3705,0.1412 0.1059,0.0941 0.1592,0.2102 0.1592,0.3485 0,0.0675 -0.0179,0.1366 -0.0532,0.2073 -0.0323,0.0705 -0.0777,0.1441 -0.1366,0.2206 -0.056,0.0735 -0.1164,0.1576 -0.1812,0.2518 0.0648,-0.0089 0.1589,-0.0251 0.2825,-0.0486 0.1265,-0.0236 0.2268,-0.0353 0.3004,-0.0353 0.0969,0 0.1762,0.0219 0.238,0.0661 0.0648,0.0442 0.1118,0.1047 0.1413,0.1812 0.0294,0.0736 0.044,0.1542 0.044,0.2426 0,0.1382 -0.0337,0.2544 -0.1013,0.3485 -0.0677,0.0912 -0.1749,0.1366 -0.3219,0.1366 -0.0472,0 -0.14,-0.0119 -0.2784,-0.0354 -0.1382,-0.0236 -0.2397,-0.038 -0.3045,-0.044 0.1531,0.2177 0.2534,0.3647 0.3004,0.4411 0.047,0.0736 0.0706,0.1549 0.0706,0.2431 0,0.1411 -0.0533,0.2558 -0.1592,0.3439 -0.1028,0.0854 -0.2264,0.1279 -0.3705,0.1279 -0.1028,0 -0.1882,-0.0371 -0.2559,-0.1105 -0.0646,-0.0765 -0.1309,-0.1955 -0.1986,-0.3572 -0.0646,-0.1647 -0.1028,-0.259 -0.1146,-0.2825 -0.0117,0.0236 -0.0516,0.1178 -0.1192,0.2825 -0.0648,0.1648 -0.1281,0.2836 -0.1899,0.3572 -0.0617,0.0735 -0.1454,0.1105 -0.2513,0.1105 -0.15,0 -0.2779,-0.0425 -0.3838,-0.1279 -0.1059,-0.0881 -0.1587,-0.2028 -0.1587,-0.3439 0,-0.0617 0.0156,-0.1268 0.0481,-0.1945 0.0323,-0.0705 0.0665,-0.1311 0.1018,-0.1812 0.0382,-0.0499 0.1118,-0.1527 0.2206,-0.3086 -0.0736,0.0059 -0.1751,0.0205 -0.3045,0.044 -0.1294,0.0236 -0.2176,0.0354 -0.2646,0.0354 -0.1472,0 -0.2577,-0.0454 -0.3311,-0.1366 -0.0707,-0.0941 -0.1059,-0.2103 -0.1059,-0.3485 0,-0.1412 0.0352,-0.2573 0.1059,-0.3485 0.0734,-0.0942 0.184,-0.1412 0.3311,-0.1412 0.0736,0 0.1628,0.0101 0.2687,0.0307 0.1059,0.0204 0.2062,0.0384 0.3004,0.0532 -0.0824,-0.1177 -0.1648,-0.2371 -0.2472,-0.3577 -0.0824,-0.1206 -0.1233,-0.2278 -0.1233,-0.3219 0,-0.1384 0.0543,-0.2544 0.1633,-0.3485 0.1088,-0.0942 0.2351,-0.1412 0.3792,-0.1412z" <path
android:strokeWidth="1.3358" android:fillColor="#f47a68"
android:fillColor="#f47a68"/> android:pathData="m30.3156,23.9881c-0.496,0 -0.992,0.1893 -1.3705,0.5676l-2.7282,2.7288 3.4612,3.4606c0.8044,-0.2715 1.727,-0.0892 2.368,0.5517 0.6237,0.624 0.8361,1.5493 0.5471,2.3828l3.3357,3.3357c0.8076,-0.2777 1.738,-0.098 2.3828,0.5476 0.9008,0.9005 0.9008,2.361 0,3.2615 -1.7823,1.7848 -4.7253,-0.1767 -3.7641,-2.5087l-3.1111,-3.1106c-2.2315,0.5285 -3.8934,-1.2655 -3.149,-3.1674l-0.6863,-0.6863l0,15.9165l5.4913,0l0,-5.8608c-0.0315,-0.7566 1.1201,-0.7566 1.0886,0l0,6.4043c0.0005,0.3013 -0.2438,0.5457 -0.545,0.5455l-6.5804,0c-0.3015,0.0005 -0.546,-0.2441 -0.5456,-0.5455l0,-17.4333c-0.0005,-0.0363 0.0029,-0.0728 0.0097,-0.1085l-1.6444,-1.6444 -9.0106,9.0084c-0.7567,0.7573 -0.7567,1.9848 0,2.7421l13.1415,13.14c0.7572,0.7567 1.9844,0.7567 2.7416,0l13.0778,-13.0785c0.7572,-0.7572 0.7572,-1.9849 0,-2.7421l-13.14,-13.1393c-0.3785,-0.3783 -0.8746,-0.5676 -1.3705,-0.5676zM29.9512,39.1825c0.1001,0 0.1808,0.0381 0.2426,0.1146 0.0648,0.0736 0.1326,0.1975 0.2032,0.371 0.0705,0.1706 0.1089,0.2615 0.1146,0.2733 0.0059,-0.0119 0.0424,-0.1026 0.11,-0.2733 0.0707,-0.1705 0.1401,-0.2946 0.2078,-0.371 0.0677,-0.0765 0.1513,-0.1146 0.2513,-0.1146 0.1412,0 0.2646,0.047 0.3705,0.1412 0.1059,0.0941 0.1592,0.2103 0.1592,0.3485 0,0.0676 -0.0179,0.1368 -0.0532,0.2073 -0.0323,0.0707 -0.0777,0.1444 -0.1366,0.2211 -0.056,0.0734 -0.1164,0.1571 -0.1812,0.2513 0.0648,-0.0089 0.1589,-0.0251 0.2825,-0.0486 0.1265,-0.0236 0.2268,-0.0354 0.3004,-0.0354 0.0969,0 0.1762,0.0224 0.238,0.0666 0.0648,0.0442 0.1118,0.1042 0.1413,0.1807 0.0294,0.0734 0.044,0.1544 0.044,0.2426 0,0.1384 -0.0337,0.2544 -0.1013,0.3485 -0.0677,0.0912 -0.1749,0.1372 -0.3219,0.1372 -0.0472,0 -0.14,-0.0119 -0.2784,-0.0354 -0.1382,-0.0236 -0.2397,-0.0386 -0.3045,-0.0446 0.1531,0.2177 0.2534,0.3652 0.3004,0.4417 0.047,0.0736 0.0706,0.1544 0.0706,0.2426 0,0.1412 -0.0533,0.2556 -0.1592,0.3439 -0.1028,0.0853 -0.2264,0.1279 -0.3705,0.1279 -0.1028,0 -0.1882,-0.0366 -0.2559,-0.11 -0.0646,-0.0767 -0.1309,-0.1955 -0.1986,-0.3572 -0.0646,-0.1647 -0.1028,-0.259 -0.1146,-0.2825 -0.0117,0.0235 -0.0516,0.1178 -0.1192,0.2825 -0.0648,0.1646 -0.1281,0.2836 -0.1899,0.3572 -0.0617,0.0735 -0.1454,0.11 -0.2513,0.11 -0.15,0 -0.2779,-0.0427 -0.3838,-0.1279 -0.1059,-0.0883 -0.1587,-0.2027 -0.1587,-0.3439 0,-0.0618 0.0156,-0.1263 0.0481,-0.194 0.0323,-0.0707 0.0665,-0.1311 0.1018,-0.1812 0.0382,-0.0499 0.1118,-0.1532 0.2206,-0.3091 -0.0736,0.0059 -0.1751,0.021 -0.3045,0.0446 -0.1294,0.0236 -0.2176,0.0354 -0.2646,0.0354 -0.1472,0 -0.2577,-0.046 -0.3311,-0.1372 -0.0707,-0.0941 -0.1059,-0.2101 -0.1059,-0.3485 0,-0.1411 0.0352,-0.2573 0.1059,-0.3485 0.0734,-0.0942 0.184,-0.1412 0.3311,-0.1412 0.0736,0 0.1628,0.0107 0.2687,0.0312 0.1059,0.0206 0.2062,0.038 0.3004,0.0527 -0.0824,-0.1177 -0.1648,-0.2366 -0.2472,-0.3572 -0.0824,-0.1206 -0.1233,-0.2282 -0.1233,-0.3224 0,-0.1382 0.0543,-0.2544 0.1633,-0.3485 0.1088,-0.0942 0.2351,-0.1412 0.3792,-0.1412zM29.9512,43.2235c0.1001,0 0.1808,0.0383 0.2426,0.1146 0.0648,0.0735 0.1326,0.197 0.2032,0.3705 0.0705,0.1707 0.1089,0.262 0.1146,0.2738 0.0059,-0.0119 0.0424,-0.1031 0.11,-0.2738 0.0707,-0.1705 0.1401,-0.2941 0.2078,-0.3705 0.0677,-0.0765 0.1513,-0.1146 0.2513,-0.1146 0.1412,0 0.2646,0.047 0.3705,0.1412 0.1059,0.0941 0.1592,0.2102 0.1592,0.3485 0,0.0675 -0.0179,0.1366 -0.0532,0.2073 -0.0323,0.0705 -0.0777,0.1441 -0.1366,0.2206 -0.056,0.0735 -0.1164,0.1576 -0.1812,0.2518 0.0648,-0.0089 0.1589,-0.0251 0.2825,-0.0486 0.1265,-0.0236 0.2268,-0.0353 0.3004,-0.0353 0.0969,0 0.1762,0.0219 0.238,0.0661 0.0648,0.0442 0.1118,0.1047 0.1413,0.1812 0.0294,0.0736 0.044,0.1542 0.044,0.2426 0,0.1382 -0.0337,0.2544 -0.1013,0.3485 -0.0677,0.0912 -0.1749,0.1366 -0.3219,0.1366 -0.0472,0 -0.14,-0.0119 -0.2784,-0.0354 -0.1382,-0.0236 -0.2397,-0.038 -0.3045,-0.044 0.1531,0.2177 0.2534,0.3647 0.3004,0.4411 0.047,0.0736 0.0706,0.1549 0.0706,0.2431 0,0.1411 -0.0533,0.2558 -0.1592,0.3439 -0.1028,0.0854 -0.2264,0.1279 -0.3705,0.1279 -0.1028,0 -0.1882,-0.0371 -0.2559,-0.1105 -0.0646,-0.0765 -0.1309,-0.1955 -0.1986,-0.3572 -0.0646,-0.1647 -0.1028,-0.259 -0.1146,-0.2825 -0.0117,0.0236 -0.0516,0.1178 -0.1192,0.2825 -0.0648,0.1648 -0.1281,0.2836 -0.1899,0.3572 -0.0617,0.0735 -0.1454,0.1105 -0.2513,0.1105 -0.15,0 -0.2779,-0.0425 -0.3838,-0.1279 -0.1059,-0.0881 -0.1587,-0.2028 -0.1587,-0.3439 0,-0.0617 0.0156,-0.1268 0.0481,-0.1945 0.0323,-0.0705 0.0665,-0.1311 0.1018,-0.1812 0.0382,-0.0499 0.1118,-0.1527 0.2206,-0.3086 -0.0736,0.0059 -0.1751,0.0205 -0.3045,0.044 -0.1294,0.0236 -0.2176,0.0354 -0.2646,0.0354 -0.1472,0 -0.2577,-0.0454 -0.3311,-0.1366 -0.0707,-0.0941 -0.1059,-0.2103 -0.1059,-0.3485 0,-0.1412 0.0352,-0.2573 0.1059,-0.3485 0.0734,-0.0942 0.184,-0.1412 0.3311,-0.1412 0.0736,0 0.1628,0.0101 0.2687,0.0307 0.1059,0.0204 0.2062,0.0384 0.3004,0.0532 -0.0824,-0.1177 -0.1648,-0.2371 -0.2472,-0.3577 -0.0824,-0.1206 -0.1233,-0.2278 -0.1233,-0.3219 0,-0.1384 0.0543,-0.2544 0.1633,-0.3485 0.1088,-0.0942 0.2351,-0.1412 0.3792,-0.1412z"
android:strokeWidth="1.3358" />
</group> </group>
</vector> </vector>

View file

@ -21,9 +21,9 @@
android:allowBackup="false" android:allowBackup="false"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:requestLegacyExternalStorage="true"
tools:ignore="GoogleAppIndexingWarning"> tools:ignore="GoogleAppIndexingWarning">
<activity <activity
@ -31,30 +31,35 @@
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:label="@string/app_name" /> android:label="@string/app_name" />
<activity android:name=".LaunchActivity" <activity
android:label="@string/app_name" android:name=".LaunchActivity"
android:configChanges="orientation|screenSize"> android:configChanges="orientation|screenSize"
android:label="@string/app_name">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".git.GitOperationActivity" <activity
android:name=".git.GitOperationActivity"
android:theme="@style/NoBackgroundTheme" /> android:theme="@style/NoBackgroundTheme" />
<activity android:name=".git.GitServerConfigActivity" <activity
android:windowSoftInputMode="adjustResize" android:name=".git.GitServerConfigActivity"
android:label="@string/title_activity_git_clone" /> android:label="@string/title_activity_git_clone"
android:windowSoftInputMode="adjustResize" />
<activity android:name=".git.GitConfigActivity" <activity
android:windowSoftInputMode="adjustResize" android:name=".git.GitConfigActivity"
android:label="@string/title_activity_git_config" /> android:label="@string/title_activity_git_config"
android:windowSoftInputMode="adjustResize" />
<activity <activity
android:name=".UserPreference" android:name=".UserPreference"
android:parentActivityName=".PasswordStore" android:label="@string/action_settings"
android:label="@string/action_settings" /> android:parentActivityName=".PasswordStore" />
<service <service
android:name=".autofill.AutofillService" android:name=".autofill.AutofillService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
@ -66,14 +71,17 @@
android:resource="@xml/autofill_config" /> android:resource="@xml/autofill_config" />
</service> </service>
<service <service
android:name=".ClipboardService" android:name=".ClipboardService"
android:process=":clipboard_service_process" /> android:process=":clipboard_service_process" />
<service android:name=".autofill.oreo.OreoAutofillService" <service
android:name=".autofill.oreo.OreoAutofillService"
android:permission="android.permission.BIND_AUTOFILL_SERVICE"> android:permission="android.permission.BIND_AUTOFILL_SERVICE">
<intent-filter> <intent-filter>
<action android:name="android.service.autofill.AutofillService" /> <action android:name="android.service.autofill.AutofillService" />
</intent-filter> </intent-filter>
<meta-data android:name="android.autofill" android:resource="@xml/oreo_autofill_service" /> <meta-data
android:name="android.autofill"
android:resource="@xml/oreo_autofill_service" />
</service> </service>
<activity <activity
@ -87,13 +95,13 @@
<activity <activity
android:name=".crypto.PgpActivity" android:name=".crypto.PgpActivity"
android:configChanges="orientation|screenSize"
android:parentActivityName=".PasswordStore" android:parentActivityName=".PasswordStore"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize" />
android:configChanges="orientation|screenSize" />
<activity android:name=".SelectFolderActivity" /> <activity android:name=".SelectFolderActivity" />
<activity <activity
android:label="@string/pref_ssh_keygen_title"
android:name=".sshkeygen.SshKeyGenActivity" android:name=".sshkeygen.SshKeyGenActivity"
android:label="@string/pref_ssh_keygen_title"
android:windowSoftInputMode="adjustResize" /> android:windowSoftInputMode="adjustResize" />
<activity <activity
android:name=".autofill.oreo.ui.AutofillDecryptActivity" android:name=".autofill.oreo.ui.AutofillDecryptActivity"
@ -101,16 +109,16 @@
<activity <activity
android:name=".autofill.oreo.ui.AutofillFilterView" android:name=".autofill.oreo.ui.AutofillFilterView"
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:windowSoftInputMode="adjustNothing" android:theme="@style/DialogLikeTheme"
android:theme="@style/DialogLikeTheme" /> android:windowSoftInputMode="adjustNothing" />
<activity <activity
android:name=".autofill.oreo.ui.AutofillSaveActivity" android:name=".autofill.oreo.ui.AutofillSaveActivity"
android:theme="@style/NoBackgroundTheme"/> android:theme="@style/NoBackgroundTheme" />
<activity <activity
android:name=".autofill.oreo.ui.AutofillPublisherChangedActivity" android:name=".autofill.oreo.ui.AutofillPublisherChangedActivity"
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:windowSoftInputMode="adjustNothing" android:theme="@style/DialogLikeTheme"
android:theme="@style/DialogLikeTheme" /> android:windowSoftInputMode="adjustNothing" />
</application> </application>
</manifest> </manifest>

View file

@ -17,6 +17,7 @@ import com.haroldadmin.whatthestack.WhatTheStack
@Suppress("Unused") @Suppress("Unused")
class Application : android.app.Application(), SharedPreferences.OnSharedPreferenceChangeListener { class Application : android.app.Application(), SharedPreferences.OnSharedPreferenceChangeListener {
private var prefs: SharedPreferences? = null private var prefs: SharedPreferences? = null
override fun onCreate() { override fun onCreate() {

View file

@ -122,13 +122,13 @@ class ClipboardService : Service() {
} }
val notification = NotificationCompat.Builder(this, CHANNEL_ID) val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(getString(R.string.app_name)) .setContentTitle(getString(R.string.app_name))
.setContentText(getString(R.string.tap_clear_clipboard)) .setContentText(getString(R.string.tap_clear_clipboard))
.setSmallIcon(R.drawable.ic_action_secure_24dp) .setSmallIcon(R.drawable.ic_action_secure_24dp)
.setContentIntent(pendingIntent) .setContentIntent(pendingIntent)
.setUsesChronometer(true) .setUsesChronometer(true)
.setPriority(NotificationCompat.PRIORITY_LOW) .setPriority(NotificationCompat.PRIORITY_LOW)
.build() .build()
startForeground(1, notification) startForeground(1, notification)
} }
@ -136,9 +136,9 @@ class ClipboardService : Service() {
private fun createNotificationChannel() { private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val serviceChannel = NotificationChannel( val serviceChannel = NotificationChannel(
CHANNEL_ID, CHANNEL_ID,
getString(R.string.app_name), getString(R.string.app_name),
NotificationManager.IMPORTANCE_LOW NotificationManager.IMPORTANCE_LOW
) )
val manager = getSystemService<NotificationManager>() val manager = getSystemService<NotificationManager>()
if (manager != null) { if (manager != null) {

View file

@ -14,6 +14,7 @@ import java.io.UnsupportedEncodingException
* A single entry in password store. * A single entry in password store.
*/ */
class PasswordEntry(private val content: String) { class PasswordEntry(private val content: String) {
val password: String val password: String
val username: String? val username: String?
val digits: String val digits: String
@ -109,8 +110,8 @@ class PasswordEntry(private val content: String) {
private fun findOtpDigits(decryptedContent: String): String { private fun findOtpDigits(decryptedContent: String): String {
decryptedContent.split("\n".toRegex()).forEach { line -> decryptedContent.split("\n".toRegex()).forEach { line ->
if ((line.startsWith("otpauth://totp/") || if ((line.startsWith("otpauth://totp/") ||
line.startsWith("otpauth://hotp/")) && line.startsWith("otpauth://hotp/")) &&
Uri.parse(line).getQueryParameter("digits") != null) { Uri.parse(line).getQueryParameter("digits") != null) {
return Uri.parse(line).getQueryParameter("digits")!! return Uri.parse(line).getQueryParameter("digits")!!
} }
} }
@ -120,7 +121,7 @@ class PasswordEntry(private val content: String) {
private fun findTotpPeriod(decryptedContent: String): Long { private fun findTotpPeriod(decryptedContent: String): Long {
decryptedContent.split("\n".toRegex()).forEach { line -> decryptedContent.split("\n".toRegex()).forEach { line ->
if (line.startsWith("otpauth://totp/") && if (line.startsWith("otpauth://totp/") &&
Uri.parse(line).getQueryParameter("period") != null) { Uri.parse(line).getQueryParameter("period") != null) {
return java.lang.Long.parseLong(Uri.parse(line).getQueryParameter("period")!!) return java.lang.Long.parseLong(Uri.parse(line).getQueryParameter("period")!!)
} }
} }
@ -130,7 +131,7 @@ class PasswordEntry(private val content: String) {
private fun findTotpAlgorithm(decryptedContent: String): String { private fun findTotpAlgorithm(decryptedContent: String): String {
decryptedContent.split("\n".toRegex()).forEach { line -> decryptedContent.split("\n".toRegex()).forEach { line ->
if (line.startsWith("otpauth://totp/") && if (line.startsWith("otpauth://totp/") &&
Uri.parse(line).getQueryParameter("algorithm") != null) { Uri.parse(line).getQueryParameter("algorithm") != null) {
return Uri.parse(line).getQueryParameter("algorithm")!! return Uri.parse(line).getQueryParameter("algorithm")!!
} }
} }
@ -166,15 +167,15 @@ class PasswordEntry(private val content: String) {
companion object { companion object {
@VisibleForTesting(otherwise = PRIVATE) @VisibleForTesting(otherwise = PRIVATE)
val USERNAME_FIELDS = arrayOf( val USERNAME_FIELDS = arrayOf(
"login:", "login:",
"username:", "username:",
"user:", "user:",
"account:", "account:",
"email:", "email:",
"name:", "name:",
"handle:", "handle:",
"id:", "id:",
"identity:" "identity:"
) )
} }
} }

View file

@ -33,9 +33,9 @@ import com.zeapo.pwdstore.ui.adapters.PasswordItemRecyclerAdapter
import com.zeapo.pwdstore.ui.dialogs.ItemCreationBottomSheet import com.zeapo.pwdstore.ui.dialogs.ItemCreationBottomSheet
import com.zeapo.pwdstore.utils.PasswordItem import com.zeapo.pwdstore.utils.PasswordItem
import com.zeapo.pwdstore.utils.PasswordRepository import com.zeapo.pwdstore.utils.PasswordRepository
import me.zhanghai.android.fastscroll.FastScrollerBuilder
import java.io.File import java.io.File
import java.util.Stack import java.util.Stack
import me.zhanghai.android.fastscroll.FastScrollerBuilder
class PasswordFragment : Fragment() { class PasswordFragment : Fragment() {
private lateinit var recyclerAdapter: PasswordItemRecyclerAdapter private lateinit var recyclerAdapter: PasswordItemRecyclerAdapter
@ -74,12 +74,12 @@ class PasswordFragment : Fragment() {
binding.swipeRefresher.setOnRefreshListener { binding.swipeRefresher.setOnRefreshListener {
if (!PasswordRepository.isGitRepo()) { if (!PasswordRepository.isGitRepo()) {
Snackbar.make(binding.root, getString(R.string.clone_git_repo), Snackbar.LENGTH_INDEFINITE) Snackbar.make(binding.root, getString(R.string.clone_git_repo), Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.clone_button) { .setAction(R.string.clone_button) {
val intent = Intent(context, GitServerConfigActivity::class.java) val intent = Intent(context, GitServerConfigActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_CLONE) intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_CLONE)
startActivityForResult(intent, BaseGitActivity.REQUEST_CLONE) startActivityForResult(intent, BaseGitActivity.REQUEST_CLONE)
} }
.show() .show()
binding.swipeRefresher.isRefreshing = false binding.swipeRefresher.isRefreshing = false
} else { } else {
// When authentication is set to ConnectionMode.None then the only git operation we // When authentication is set to ConnectionMode.None then the only git operation we
@ -211,7 +211,7 @@ class PasswordFragment : Fragment() {
private fun animateFab(show: Boolean) = with(binding.fab) { private fun animateFab(show: Boolean) = with(binding.fab) {
val animation = AnimationUtils.loadAnimation( val animation = AnimationUtils.loadAnimation(
context, if (show) R.anim.scale_up else R.anim.scale_down context, if (show) R.anim.scale_up else R.anim.scale_down
) )
animation.setAnimationListener(object : Animation.AnimationListener { animation.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationRepeat(animation: Animation?) { override fun onAnimationRepeat(animation: Animation?) {
@ -226,9 +226,9 @@ class PasswordFragment : Fragment() {
} }
}) })
animate().rotationBy(if (show) -90f else 90f) animate().rotationBy(if (show) -90f else 90f)
.setStartDelay(if (show) 100 else 0) .setStartDelay(if (show) 100 else 0)
.setDuration(100) .setDuration(100)
.start() .start()
startAnimation(animation) startAnimation(animation)
} }
} }

View file

@ -65,14 +65,14 @@ import com.zeapo.pwdstore.utils.PasswordRepository.Companion.getRepositoryDirect
import com.zeapo.pwdstore.utils.PasswordRepository.Companion.initialize import com.zeapo.pwdstore.utils.PasswordRepository.Companion.initialize
import com.zeapo.pwdstore.utils.PasswordRepository.Companion.isInitialized import com.zeapo.pwdstore.utils.PasswordRepository.Companion.isInitialized
import com.zeapo.pwdstore.utils.PasswordRepository.PasswordSortOrder.Companion.getSortOrder import com.zeapo.pwdstore.utils.PasswordRepository.PasswordSortOrder.Companion.getSortOrder
import java.io.File
import java.lang.Character.UnicodeBlock
import java.util.Stack
import org.apache.commons.io.FileUtils import org.apache.commons.io.FileUtils
import org.apache.commons.io.FilenameUtils import org.apache.commons.io.FilenameUtils
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.errors.GitAPIException import org.eclipse.jgit.api.errors.GitAPIException
import org.eclipse.jgit.revwalk.RevCommit import org.eclipse.jgit.revwalk.RevCommit
import java.io.File
import java.lang.Character.UnicodeBlock
import java.util.Stack
class PasswordStore : AppCompatActivity() { class PasswordStore : AppCompatActivity() {
@ -90,7 +90,7 @@ class PasswordStore : AppCompatActivity() {
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
// open search view on search key, or Ctr+F // open search view on search key, or Ctr+F
if ((keyCode == KeyEvent.KEYCODE_SEARCH || keyCode == KeyEvent.KEYCODE_F && event.isCtrlPressed) && if ((keyCode == KeyEvent.KEYCODE_SEARCH || keyCode == KeyEvent.KEYCODE_F && event.isCtrlPressed) &&
!searchItem.isActionViewExpanded) { !searchItem.isActionViewExpanded) {
searchItem.expandActionView() searchItem.expandActionView()
return true return true
} }
@ -118,9 +118,9 @@ class PasswordStore : AppCompatActivity() {
// prevent attempt to create password list fragment // prevent attempt to create password list fragment
var savedInstance = savedInstanceState var savedInstance = savedInstanceState
if (savedInstanceState != null && (!settings.getBoolean("git_external", false) || if (savedInstanceState != null && (!settings.getBoolean("git_external", false) ||
ContextCompat.checkSelfPermission( ContextCompat.checkSelfPermission(
activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED)) { != PackageManager.PERMISSION_GRANTED)) {
savedInstance = null savedInstance = null
} }
super.onCreate(savedInstance) super.onCreate(savedInstance)
@ -128,28 +128,28 @@ class PasswordStore : AppCompatActivity() {
// If user is eligible for Oreo autofill, prompt them to switch. // If user is eligible for Oreo autofill, prompt them to switch.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
!settings.getBoolean(PREFERENCE_SEEN_AUTOFILL_ONBOARDING, false)) { !settings.getBoolean(PREFERENCE_SEEN_AUTOFILL_ONBOARDING, false)) {
MaterialAlertDialogBuilder(this).run { MaterialAlertDialogBuilder(this).run {
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
val layout = val layout =
layoutInflater.inflate(R.layout.oreo_autofill_instructions, null) layoutInflater.inflate(R.layout.oreo_autofill_instructions, null)
layout.findViewById<AppCompatTextView>(R.id.intro_text).setText(R.string.autofill_onboarding_dialog_message) layout.findViewById<AppCompatTextView>(R.id.intro_text).setText(R.string.autofill_onboarding_dialog_message)
val supportedBrowsersTextView = val supportedBrowsersTextView =
layout.findViewById<AppCompatTextView>(R.id.supportedBrowsers) layout.findViewById<AppCompatTextView>(R.id.supportedBrowsers)
supportedBrowsersTextView.text = supportedBrowsersTextView.text =
getInstalledBrowsersWithAutofillSupportLevel(context).joinToString( getInstalledBrowsersWithAutofillSupportLevel(context).joinToString(
separator = "\n" separator = "\n"
) { ) {
val appLabel = it.first val appLabel = it.first
val supportDescription = when (it.second) { val supportDescription = when (it.second) {
BrowserAutofillSupportLevel.None -> getString(R.string.oreo_autofill_no_support) BrowserAutofillSupportLevel.None -> getString(R.string.oreo_autofill_no_support)
BrowserAutofillSupportLevel.FlakyFill -> getString(R.string.oreo_autofill_flaky_fill_support) BrowserAutofillSupportLevel.FlakyFill -> getString(R.string.oreo_autofill_flaky_fill_support)
BrowserAutofillSupportLevel.PasswordFill -> getString(R.string.oreo_autofill_password_fill_support) BrowserAutofillSupportLevel.PasswordFill -> getString(R.string.oreo_autofill_password_fill_support)
BrowserAutofillSupportLevel.GeneralFill -> getString(R.string.oreo_autofill_general_fill_support) BrowserAutofillSupportLevel.GeneralFill -> getString(R.string.oreo_autofill_general_fill_support)
BrowserAutofillSupportLevel.GeneralFillAndSave -> getString(R.string.oreo_autofill_general_fill_and_save_support) BrowserAutofillSupportLevel.GeneralFillAndSave -> getString(R.string.oreo_autofill_general_fill_and_save_support)
}
"$appLabel: $supportDescription"
} }
"$appLabel: $supportDescription"
}
setView(layout) setView(layout)
setTitle(R.string.autofill_onboarding_dialog_title) setTitle(R.string.autofill_onboarding_dialog_title)
setPositiveButton(R.string.dialog_ok) { _, _ -> setPositiveButton(R.string.dialog_ok) { _, _ ->
@ -204,7 +204,7 @@ class PasswordStore : AppCompatActivity() {
override fun onCreateOptionsMenu(menu: Menu?): Boolean { override fun onCreateOptionsMenu(menu: Menu?): Boolean {
val menuRes = when { val menuRes = when {
ConnectionMode.fromString(settings.getString("git_remote_auth", null)) ConnectionMode.fromString(settings.getString("git_remote_auth", null))
== ConnectionMode.None -> R.menu.main_menu_no_auth == ConnectionMode.None -> R.menu.main_menu_no_auth
PasswordRepository.isGitRepo() -> R.menu.main_menu_git PasswordRepository.isGitRepo() -> R.menu.main_menu_git
else -> R.menu.main_menu_non_git else -> R.menu.main_menu_non_git
} }
@ -219,40 +219,40 @@ class PasswordStore : AppCompatActivity() {
searchItem = menu.findItem(R.id.action_search) searchItem = menu.findItem(R.id.action_search)
searchView = searchItem.actionView as SearchView searchView = searchItem.actionView as SearchView
searchView.setOnQueryTextListener( searchView.setOnQueryTextListener(
object : OnQueryTextListener { object : OnQueryTextListener {
override fun onQueryTextSubmit(s: String): Boolean { override fun onQueryTextSubmit(s: String): Boolean {
searchView.clearFocus() searchView.clearFocus()
return true return true
} }
override fun onQueryTextChange(s: String): Boolean { override fun onQueryTextChange(s: String): Boolean {
val filter = s.trim() val filter = s.trim()
// List the contents of the current directory if the user enters a blank // List the contents of the current directory if the user enters a blank
// search term. // search term.
if (filter.isEmpty()) if (filter.isEmpty())
model.navigateTo( model.navigateTo(
newDirectory = model.currentDir.value!!, newDirectory = model.currentDir.value!!,
pushPreviousLocation = false pushPreviousLocation = false
) )
else else
model.search(filter) model.search(filter)
return true return true
} }
}) })
// When using the support library, the setOnActionExpandListener() method is // When using the support library, the setOnActionExpandListener() method is
// static and accepts the MenuItem object as an argument // static and accepts the MenuItem object as an argument
searchItem.setOnActionExpandListener( searchItem.setOnActionExpandListener(
object : OnActionExpandListener { object : OnActionExpandListener {
override fun onMenuItemActionCollapse(item: MenuItem): Boolean { override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
refreshPasswordList() refreshPasswordList()
return true return true
} }
override fun onMenuItemActionExpand(item: MenuItem): Boolean { override fun onMenuItemActionExpand(item: MenuItem): Boolean {
return true return true
} }
}) })
if (settings.getBoolean("search_on_start", false)) { if (settings.getBoolean("search_on_start", false)) {
searchItem.expandActionView() searchItem.expandActionView()
} }
@ -266,8 +266,8 @@ class PasswordStore : AppCompatActivity() {
val id = item.itemId val id = item.itemId
val intent: Intent val intent: Intent
val initBefore = MaterialAlertDialogBuilder(this) val initBefore = MaterialAlertDialogBuilder(this)
.setMessage(resources.getString(R.string.creation_dialog_text)) .setMessage(resources.getString(R.string.creation_dialog_text))
.setPositiveButton(resources.getString(R.string.dialog_ok), null) .setPositiveButton(resources.getString(R.string.dialog_ok), null)
when (id) { when (id) {
R.id.user_pref -> { R.id.user_pref -> {
try { try {
@ -379,7 +379,7 @@ class PasswordStore : AppCompatActivity() {
if (externalRepo && externalRepoPath != null) { if (externalRepo && externalRepoPath != null) {
val dir = File(externalRepoPath) val dir = File(externalRepoPath)
if (dir.exists() && dir.isDirectory && if (dir.exists() && dir.isDirectory &&
getPasswords(dir, getRepositoryDirectory(this), sortOrder).isNotEmpty()) { getPasswords(dir, getRepositoryDirectory(this), sortOrder).isNotEmpty()) {
closeRepository() closeRepository()
checkLocalRepository() checkLocalRepository()
return // if not empty, just show me the passwords! return // if not empty, just show me the passwords!
@ -388,13 +388,13 @@ class PasswordStore : AppCompatActivity() {
val keyIds = settings.getStringSet("openpgp_key_ids_set", HashSet()) val keyIds = settings.getStringSet("openpgp_key_ids_set", HashSet())
if (keyIds != null && keyIds.isEmpty()) { if (keyIds != null && keyIds.isEmpty()) {
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setMessage(resources.getString(R.string.key_dialog_text)) .setMessage(resources.getString(R.string.key_dialog_text))
.setPositiveButton(resources.getString(R.string.dialog_positive)) { _, _ -> .setPositiveButton(resources.getString(R.string.dialog_positive)) { _, _ ->
val intent = Intent(activity, UserPreference::class.java) val intent = Intent(activity, UserPreference::class.java)
startActivityForResult(intent, BaseGitActivity.REQUEST_INIT) startActivityForResult(intent, BaseGitActivity.REQUEST_INIT)
} }
.setNegativeButton(resources.getString(R.string.dialog_negative), null) .setNegativeButton(resources.getString(R.string.dialog_negative), null)
.show() .show()
} }
createRepository() createRepository()
} }
@ -407,15 +407,15 @@ class PasswordStore : AppCompatActivity() {
return if (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) return if (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) { != PackageManager.PERMISSION_GRANTED) {
Snackbar.make( Snackbar.make(
findViewById(R.id.main_layout), findViewById(R.id.main_layout),
getString(R.string.access_sdcard_text), getString(R.string.access_sdcard_text),
Snackbar.LENGTH_INDEFINITE Snackbar.LENGTH_INDEFINITE
).run { ).run {
setAction(getString(R.string.snackbar_action_grant)) { setAction(getString(R.string.snackbar_action_grant)) {
ActivityCompat.requestPermissions( ActivityCompat.requestPermissions(
activity, activity,
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
REQUEST_EXTERNAL_STORAGE REQUEST_EXTERNAL_STORAGE
) )
dismiss() dismiss()
} }
@ -447,7 +447,7 @@ class PasswordStore : AppCompatActivity() {
tag(TAG).d { "Check, dir: ${localDir.absolutePath}" } tag(TAG).d { "Check, dir: ${localDir.absolutePath}" }
// do not push the fragment if we already have it // do not push the fragment if we already have it
if (fragmentManager.findFragmentByTag("PasswordsList") == null || if (fragmentManager.findFragmentByTag("PasswordsList") == null ||
settings.getBoolean("repo_changed", false)) { settings.getBoolean("repo_changed", false)) {
settings.edit { putBoolean("repo_changed", false) } settings.edit { putBoolean("repo_changed", false) }
plist = PasswordFragment() plist = PasswordFragment()
val args = Bundle() val args = Bundle()
@ -520,11 +520,11 @@ class PasswordStore : AppCompatActivity() {
// Adds shortcut // Adds shortcut
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
val shortcut = Builder(this, item.fullPathToParent) val shortcut = Builder(this, item.fullPathToParent)
.setShortLabel(item.toString()) .setShortLabel(item.toString())
.setLongLabel(item.fullPathToParent + item.toString()) .setLongLabel(item.fullPathToParent + item.toString())
.setIcon(Icon.createWithResource(this, R.mipmap.ic_launcher)) .setIcon(Icon.createWithResource(this, R.mipmap.ic_launcher))
.setIntent(authDecryptIntent.setAction("DECRYPT_PASS")) // Needs action .setIntent(authDecryptIntent.setAction("DECRYPT_PASS")) // Needs action
.build() .build()
val shortcuts = shortcutManager!!.dynamicShortcuts val shortcuts = shortcutManager!!.dynamicShortcuts
if (shortcuts.size >= shortcutManager!!.maxShortcutCountPerActivity && shortcuts.size > 0) { if (shortcuts.size >= shortcutManager!!.maxShortcutCountPerActivity && shortcuts.size > 0) {
shortcuts.removeAt(shortcuts.size - 1) shortcuts.removeAt(shortcuts.size - 1)
@ -550,20 +550,20 @@ class PasswordStore : AppCompatActivity() {
private fun validateState(): Boolean { private fun validateState(): Boolean {
if (!isInitialized) { if (!isInitialized) {
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setMessage(resources.getString(R.string.creation_dialog_text)) .setMessage(resources.getString(R.string.creation_dialog_text))
.setPositiveButton(resources.getString(R.string.dialog_ok), null) .setPositiveButton(resources.getString(R.string.dialog_ok), null)
.show() .show()
return false return false
} }
if (settings.getStringSet("openpgp_key_ids_set", HashSet()).isNullOrEmpty()) { if (settings.getStringSet("openpgp_key_ids_set", HashSet()).isNullOrEmpty()) {
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setTitle(resources.getString(R.string.no_key_selected_dialog_title)) .setTitle(resources.getString(R.string.no_key_selected_dialog_title))
.setMessage(resources.getString(R.string.no_key_selected_dialog_text)) .setMessage(resources.getString(R.string.no_key_selected_dialog_text))
.setPositiveButton(resources.getString(R.string.dialog_ok)) { _, _ -> .setPositiveButton(resources.getString(R.string.dialog_ok)) { _, _ ->
val intent = Intent(activity, UserPreference::class.java) val intent = Intent(activity, UserPreference::class.java)
startActivity(intent) startActivity(intent)
} }
.show() .show()
return false return false
} }
return true return true
@ -593,22 +593,22 @@ class PasswordStore : AppCompatActivity() {
} }
val item = selectedItems.pop() val item = selectedItems.pop()
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setMessage(resources.getString(R.string.delete_dialog_text, item.longName)) .setMessage(resources.getString(R.string.delete_dialog_text, item.longName))
.setPositiveButton(resources.getString(R.string.dialog_yes)) { _, _ -> .setPositiveButton(resources.getString(R.string.dialog_yes)) { _, _ ->
val filesToDelete = if (item.file.isDirectory) { val filesToDelete = if (item.file.isDirectory) {
FileUtils.listFiles(item.file, null, true) FileUtils.listFiles(item.file, null, true)
} else { } else {
listOf(item.file) listOf(item.file)
}
AutofillMatcher.updateMatches(applicationContext, delete = filesToDelete)
item.file.deleteRecursively()
commitChange(resources.getString(R.string.git_commit_remove_text, item.longName))
deletePasswords(selectedItems)
} }
.setNegativeButton(resources.getString(R.string.dialog_no)) { _, _ -> AutofillMatcher.updateMatches(applicationContext, delete = filesToDelete)
deletePasswords(selectedItems) item.file.deleteRecursively()
} commitChange(resources.getString(R.string.git_commit_remove_text, item.longName))
.show() deletePasswords(selectedItems)
}
.setNegativeButton(resources.getString(R.string.dialog_no)) { _, _ ->
deletePasswords(selectedItems)
}
.show()
} }
fun movePasswords(values: List<PasswordItem>) { fun movePasswords(values: List<PasswordItem>) {
@ -661,22 +661,22 @@ class PasswordStore : AppCompatActivity() {
if (data != null && data.getBooleanExtra("needCommit", false)) { if (data != null && data.getBooleanExtra("needCommit", false)) {
if (data.getStringExtra("OPERATION") == "EDIT") { if (data.getStringExtra("OPERATION") == "EDIT") {
commitChange(resources.getString(R.string.git_commit_edit_text, commitChange(resources.getString(R.string.git_commit_edit_text,
data.extras!!.getString("LONG_NAME"))) data.extras!!.getString("LONG_NAME")))
} else { } else {
commitChange(resources.getString(R.string.git_commit_increment_text, commitChange(resources.getString(R.string.git_commit_increment_text,
data.extras!!.getString("LONG_NAME"))) data.extras!!.getString("LONG_NAME")))
} }
} }
refreshPasswordList() refreshPasswordList()
} }
REQUEST_CODE_ENCRYPT -> { REQUEST_CODE_ENCRYPT -> {
commitChange(resources.getString(R.string.git_commit_add_text, commitChange(resources.getString(R.string.git_commit_add_text,
data!!.extras!!.getString("LONG_NAME"))) data!!.extras!!.getString("LONG_NAME")))
refreshPasswordList() refreshPasswordList()
} }
REQUEST_CODE_EDIT -> { REQUEST_CODE_EDIT -> {
commitChange(resources.getString(R.string.git_commit_edit_text, commitChange(resources.getString(R.string.git_commit_edit_text,
data!!.extras!!.getString("LONG_NAME"))) data!!.extras!!.getString("LONG_NAME")))
refreshPasswordList() refreshPasswordList()
} }
BaseGitActivity.REQUEST_INIT, NEW_REPO_BUTTON -> initializeRepositoryInfo() BaseGitActivity.REQUEST_INIT, NEW_REPO_BUTTON -> initializeRepositoryInfo()
@ -685,14 +685,14 @@ class PasswordStore : AppCompatActivity() {
// duplicate code // duplicate code
CLONE_REPO_BUTTON -> { CLONE_REPO_BUTTON -> {
if (settings.getBoolean("git_external", false) && if (settings.getBoolean("git_external", false) &&
settings.getString("git_external_repo", null) != null) { settings.getString("git_external_repo", null) != null) {
val externalRepoPath = settings.getString("git_external_repo", null) val externalRepoPath = settings.getString("git_external_repo", null)
val dir = externalRepoPath?.let { File(it) } val dir = externalRepoPath?.let { File(it) }
if (dir != null && if (dir != null &&
dir.exists() && dir.exists() &&
dir.isDirectory && dir.isDirectory &&
!FileUtils.listFiles(dir, null, true).isEmpty() && !FileUtils.listFiles(dir, null, true).isEmpty() &&
getPasswords(dir, getRepositoryDirectory(this), sortOrder).isNotEmpty()) { getPasswords(dir, getRepositoryDirectory(this), sortOrder).isNotEmpty()) {
closeRepository() closeRepository()
checkLocalRepository() checkLocalRepository()
return // if not empty, just show me the passwords! return // if not empty, just show me the passwords!
@ -732,17 +732,17 @@ class PasswordStore : AppCompatActivity() {
if (destinationFile.exists()) { if (destinationFile.exists()) {
e { "Trying to move a file that already exists." } e { "Trying to move a file that already exists." }
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setTitle(resources.getString(R.string.password_exists_title)) .setTitle(resources.getString(R.string.password_exists_title))
.setMessage(resources.getString( .setMessage(resources.getString(
R.string.password_exists_message, R.string.password_exists_message,
destinationLongName, destinationLongName,
sourceLongName) sourceLongName)
) )
.setPositiveButton(R.string.dialog_ok) { _, _ -> .setPositiveButton(R.string.dialog_ok) { _, _ ->
movePasswords(source, destinationFile, sourceLongName, destinationLongName) movePasswords(source, destinationFile, sourceLongName, destinationLongName)
} }
.setNegativeButton(R.string.dialog_cancel, null) .setNegativeButton(R.string.dialog_cancel, null)
.show() .show()
} else { } else {
movePasswords(source, destinationFile, sourceLongName, destinationLongName) movePasswords(source, destinationFile, sourceLongName, destinationLongName)
} }
@ -773,66 +773,66 @@ class PasswordStore : AppCompatActivity() {
} else { } else {
AutofillMatcher.updateMatches(this, sourceDestinationMap) AutofillMatcher.updateMatches(this, sourceDestinationMap)
commitChange(resources commitChange(resources
.getString( .getString(
R.string.git_commit_move_text, R.string.git_commit_move_text,
sourceLongName, sourceLongName,
destinationLongName)) destinationLongName))
} }
} }
private fun initRepository(operation: Int) { private fun initRepository(operation: Int) {
closeRepository() closeRepository()
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setTitle(resources.getString(R.string.location_dialog_title)) .setTitle(resources.getString(R.string.location_dialog_title))
.setMessage(resources.getString(R.string.location_dialog_text)) .setMessage(resources.getString(R.string.location_dialog_text))
.setPositiveButton(resources.getString(R.string.location_hidden)) { _, _ -> .setPositiveButton(resources.getString(R.string.location_hidden)) { _, _ ->
settings.edit { putBoolean("git_external", false) } settings.edit { putBoolean("git_external", false) }
when (operation) { when (operation) {
NEW_REPO_BUTTON -> initializeRepositoryInfo() NEW_REPO_BUTTON -> initializeRepositoryInfo()
CLONE_REPO_BUTTON -> { CLONE_REPO_BUTTON -> {
val intent = Intent(activity, GitServerConfigActivity::class.java) val intent = Intent(activity, GitServerConfigActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_CLONE) intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_CLONE)
startActivityForResult(intent, BaseGitActivity.REQUEST_CLONE) startActivityForResult(intent, BaseGitActivity.REQUEST_CLONE)
}
}
}
.setNegativeButton(resources.getString(R.string.location_sdcard)) { _, _ ->
settings.edit { putBoolean("git_external", true) }
val externalRepo = settings.getString("git_external_repo", null)
if (externalRepo == null) {
val intent = Intent(activity, UserPreference::class.java)
intent.putExtra("operation", "git_external")
startActivityForResult(intent, operation)
} else {
MaterialAlertDialogBuilder(activity)
.setTitle(resources.getString(R.string.directory_selected_title))
.setMessage(resources.getString(R.string.directory_selected_message, externalRepo))
.setPositiveButton(resources.getString(R.string.use)) { _, _ ->
when (operation) {
NEW_REPO_BUTTON -> initializeRepositoryInfo()
CLONE_REPO_BUTTON -> {
val intent = Intent(activity, GitServerConfigActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_CLONE)
startActivityForResult(intent, BaseGitActivity.REQUEST_CLONE)
}
}
} }
} .setNegativeButton(resources.getString(R.string.change)) { _, _ ->
val intent = Intent(activity, UserPreference::class.java)
intent.putExtra("operation", "git_external")
startActivityForResult(intent, operation)
}
.show()
} }
.setNegativeButton(resources.getString(R.string.location_sdcard)) { _, _ -> }
settings.edit { putBoolean("git_external", true) } .show()
val externalRepo = settings.getString("git_external_repo", null)
if (externalRepo == null) {
val intent = Intent(activity, UserPreference::class.java)
intent.putExtra("operation", "git_external")
startActivityForResult(intent, operation)
} else {
MaterialAlertDialogBuilder(activity)
.setTitle(resources.getString(R.string.directory_selected_title))
.setMessage(resources.getString(R.string.directory_selected_message, externalRepo))
.setPositiveButton(resources.getString(R.string.use)) { _, _ ->
when (operation) {
NEW_REPO_BUTTON -> initializeRepositoryInfo()
CLONE_REPO_BUTTON -> {
val intent = Intent(activity, GitServerConfigActivity::class.java)
intent.putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_CLONE)
startActivityForResult(intent, BaseGitActivity.REQUEST_CLONE)
}
}
}
.setNegativeButton(resources.getString(R.string.change)) { _, _ ->
val intent = Intent(activity, UserPreference::class.java)
intent.putExtra("operation", "git_external")
startActivityForResult(intent, operation)
}
.show()
}
}
.show()
} }
fun matchPasswordWithApp(item: PasswordItem) { fun matchPasswordWithApp(item: PasswordItem) {
val path = item.file val path = item.file
.absolutePath .absolutePath
.replace(getRepositoryDirectory(applicationContext).toString() + "/", "") .replace(getRepositoryDirectory(applicationContext).toString() + "/", "")
.replace(".gpg", "") .replace(".gpg", "")
val data = Intent() val data = Intent()
data.putExtra("path", path) data.putExtra("path", path)
setResult(Activity.RESULT_OK, data) setResult(Activity.RESULT_OK, data)
@ -860,7 +860,7 @@ class PasswordStore : AppCompatActivity() {
private fun isPrintable(c: Char): Boolean { private fun isPrintable(c: Char): Boolean {
val block = UnicodeBlock.of(c) val block = UnicodeBlock.of(c)
return (!Character.isISOControl(c) && return (!Character.isISOControl(c) &&
block != null && block !== UnicodeBlock.SPECIALS) block != null && block !== UnicodeBlock.SPECIALS)
} }
private const val PREFERENCE_SEEN_AUTOFILL_ONBOARDING = "seen_autofill_onboarding" private const val PREFERENCE_SEEN_AUTOFILL_ONBOARDING = "seen_autofill_onboarding"

View file

@ -30,10 +30,6 @@ import com.zeapo.pwdstore.autofill.oreo.AutofillPreferences
import com.zeapo.pwdstore.autofill.oreo.DirectoryStructure import com.zeapo.pwdstore.autofill.oreo.DirectoryStructure
import com.zeapo.pwdstore.utils.PasswordItem import com.zeapo.pwdstore.utils.PasswordItem
import com.zeapo.pwdstore.utils.PasswordRepository import com.zeapo.pwdstore.utils.PasswordRepository
import java.io.File
import java.text.Collator
import java.util.Locale
import java.util.Stack
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
@ -48,6 +44,10 @@ import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.toList import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.yield import kotlinx.coroutines.yield
import me.zhanghai.android.fastscroll.PopupTextProvider import me.zhanghai.android.fastscroll.PopupTextProvider
import java.io.File
import java.text.Collator
import java.util.Locale
import java.util.Stack
private fun File.toPasswordItem(root: File) = if (isFile) private fun File.toPasswordItem(root: File) = if (isFile)
PasswordItem.newPassword(name, this, root) PasswordItem.newPassword(name, this, root)

View file

@ -18,8 +18,8 @@ import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.zeapo.pwdstore.ui.adapters.PasswordItemRecyclerAdapter import com.zeapo.pwdstore.ui.adapters.PasswordItemRecyclerAdapter
import com.zeapo.pwdstore.utils.PasswordItem import com.zeapo.pwdstore.utils.PasswordItem
import java.io.File
import me.zhanghai.android.fastscroll.FastScrollerBuilder import me.zhanghai.android.fastscroll.FastScrollerBuilder
import java.io.File
class SelectFolderFragment : Fragment() { class SelectFolderFragment : Fragment() {
private lateinit var recyclerAdapter: PasswordItemRecyclerAdapter private lateinit var recyclerAdapter: PasswordItemRecyclerAdapter
@ -75,7 +75,7 @@ class SelectFolderFragment : Fragment() {
} }
} catch (e: ClassCastException) { } catch (e: ClassCastException) {
throw ClassCastException( throw ClassCastException(
"$context must implement OnFragmentInteractionListener") "$context must implement OnFragmentInteractionListener")
} }
} }

View file

@ -50,6 +50,8 @@ import com.zeapo.pwdstore.utils.BiometricAuthenticator
import com.zeapo.pwdstore.utils.PasswordRepository import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.autofillManager import com.zeapo.pwdstore.utils.autofillManager
import com.zeapo.pwdstore.utils.getEncryptedPrefs import com.zeapo.pwdstore.utils.getEncryptedPrefs
import me.msfjarvis.openpgpktx.util.OpenPgpUtils
import org.apache.commons.io.FileUtils
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.time.LocalDateTime import java.time.LocalDateTime
@ -57,8 +59,6 @@ import java.time.format.DateTimeFormatter
import java.util.Calendar import java.util.Calendar
import java.util.HashSet import java.util.HashSet
import java.util.TimeZone import java.util.TimeZone
import me.msfjarvis.openpgpktx.util.OpenPgpUtils
import org.apache.commons.io.FileUtils
typealias ClickListener = Preference.OnPreferenceClickListener typealias ClickListener = Preference.OnPreferenceClickListener
typealias ChangeListener = Preference.OnPreferenceChangeListener typealias ChangeListener = Preference.OnPreferenceChangeListener
@ -98,8 +98,8 @@ class UserPreference : AppCompatActivity() {
if (!PasswordRepository.isGitRepo()) { if (!PasswordRepository.isGitRepo()) {
listOfNotNull( listOfNotNull(
gitServerPreference, gitConfigPreference, sshKeyPreference, gitServerPreference, gitConfigPreference, sshKeyPreference,
sshKeygenPreference, viewSshKeyPreference, clearSavedPassPreference sshKeygenPreference, viewSshKeyPreference, clearSavedPassPreference
).forEach { ).forEach {
it.parent?.removePreference(it) it.parent?.removePreference(it)
} }
@ -121,10 +121,10 @@ class UserPreference : AppCompatActivity() {
val autoFillShowFullNamePreference = val autoFillShowFullNamePreference =
findPreference<CheckBoxPreference>("autofill_full_path")!! findPreference<CheckBoxPreference>("autofill_full_path")!!
autofillDependencies = listOf( autofillDependencies = listOf(
autoFillAppsPreference, autoFillAppsPreference,
autoFillDefaultPreference, autoFillDefaultPreference,
autoFillAlwaysShowDialogPreference, autoFillAlwaysShowDialogPreference,
autoFillShowFullNamePreference autoFillShowFullNamePreference
) )
val oreoAutofillDirectoryStructurePreference = val oreoAutofillDirectoryStructurePreference =
findPreference<ListPreference>("oreo_autofill_directory_structure")!! findPreference<ListPreference>("oreo_autofill_directory_structure")!!
@ -139,7 +139,7 @@ class UserPreference : AppCompatActivity() {
clearHotpIncrementPreference?.isVisible = sharedPreferences.getBoolean("hotp_remember_check", false) clearHotpIncrementPreference?.isVisible = sharedPreferences.getBoolean("hotp_remember_check", false)
clearClipboard20xPreference?.isVisible = sharedPreferences.getString("general_show_time", "45")?.toInt() != 0 clearClipboard20xPreference?.isVisible = sharedPreferences.getString("general_show_time", "45")?.toInt() != 0
val selectedKeys = (sharedPreferences.getStringSet("openpgp_key_ids_set", null) val selectedKeys = (sharedPreferences.getStringSet("openpgp_key_ids_set", null)
?: HashSet()).toTypedArray() ?: HashSet()).toTypedArray()
keyPreference?.summary = if (selectedKeys.isEmpty()) { keyPreference?.summary = if (selectedKeys.isEmpty()) {
this.resources.getString(R.string.pref_no_key_selected) this.resources.getString(R.string.pref_no_key_selected)
} else { } else {
@ -148,7 +148,7 @@ class UserPreference : AppCompatActivity() {
} }
} }
openkeystoreIdPreference?.isVisible = sharedPreferences.getString("ssh_openkeystore_keyid", null)?.isNotEmpty() openkeystoreIdPreference?.isVisible = sharedPreferences.getString("ssh_openkeystore_keyid", null)?.isNotEmpty()
?: false ?: false
updateAutofillSettings() updateAutofillSettings()
updateClearSavedPassphrasePrefs() updateClearSavedPassphrasePrefs()
@ -220,29 +220,29 @@ class UserPreference : AppCompatActivity() {
deleteRepoPreference?.onPreferenceClickListener = ClickListener { deleteRepoPreference?.onPreferenceClickListener = ClickListener {
val repoDir = PasswordRepository.getRepositoryDirectory(callingActivity.applicationContext) val repoDir = PasswordRepository.getRepositoryDirectory(callingActivity.applicationContext)
MaterialAlertDialogBuilder(callingActivity) MaterialAlertDialogBuilder(callingActivity)
.setTitle(R.string.pref_dialog_delete_title) .setTitle(R.string.pref_dialog_delete_title)
.setMessage(resources.getString(R.string.dialog_delete_msg, repoDir)) .setMessage(resources.getString(R.string.dialog_delete_msg, repoDir))
.setCancelable(false) .setCancelable(false)
.setPositiveButton(R.string.dialog_delete) { dialogInterface, _ -> .setPositiveButton(R.string.dialog_delete) { dialogInterface, _ ->
try { try {
FileUtils.cleanDirectory(PasswordRepository.getRepositoryDirectory(callingActivity.applicationContext)) FileUtils.cleanDirectory(PasswordRepository.getRepositoryDirectory(callingActivity.applicationContext))
PasswordRepository.closeRepository() PasswordRepository.closeRepository()
} catch (ignored: Exception) { } catch (ignored: Exception) {
// TODO Handle the different cases of exceptions // TODO Handle the different cases of exceptions
}
sharedPreferences.edit { putBoolean("repository_initialized", false) }
dialogInterface.cancel()
callingActivity.finish()
} }
.setNegativeButton(R.string.dialog_do_not_delete) { dialogInterface, _ -> run { dialogInterface.cancel() } }
.show() sharedPreferences.edit { putBoolean("repository_initialized", false) }
dialogInterface.cancel()
callingActivity.finish()
}
.setNegativeButton(R.string.dialog_do_not_delete) { dialogInterface, _ -> run { dialogInterface.cancel() } }
.show()
true true
} }
selectExternalGitRepositoryPreference?.summary = selectExternalGitRepositoryPreference?.summary =
sharedPreferences.getString("git_external_repo", context.getString(R.string.no_repo_selected)) sharedPreferences.getString("git_external_repo", context.getString(R.string.no_repo_selected))
selectExternalGitRepositoryPreference?.onPreferenceClickListener = ClickListener { selectExternalGitRepositoryPreference?.onPreferenceClickListener = ClickListener {
callingActivity.selectExternalGitRepository() callingActivity.selectExternalGitRepository()
true true
@ -484,23 +484,23 @@ class UserPreference : AppCompatActivity() {
prefsFragment = PrefsFragment() prefsFragment = PrefsFragment()
supportFragmentManager supportFragmentManager
.beginTransaction() .beginTransaction()
.replace(android.R.id.content, prefsFragment) .replace(android.R.id.content, prefsFragment)
.commit() .commit()
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
} }
fun selectExternalGitRepository() { fun selectExternalGitRepository() {
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setTitle(this.resources.getString(R.string.external_repository_dialog_title)) .setTitle(this.resources.getString(R.string.external_repository_dialog_title))
.setMessage(this.resources.getString(R.string.external_repository_dialog_text)) .setMessage(this.resources.getString(R.string.external_repository_dialog_text))
.setPositiveButton(R.string.dialog_ok) { _, _ -> .setPositiveButton(R.string.dialog_ok) { _, _ ->
val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
startActivityForResult(Intent.createChooser(i, "Choose Directory"), SELECT_GIT_DIRECTORY) startActivityForResult(Intent.createChooser(i, "Choose Directory"), SELECT_GIT_DIRECTORY)
} }
.setNegativeButton(R.string.dialog_cancel, null) .setNegativeButton(R.string.dialog_cancel, null)
.show() .show()
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -630,9 +630,9 @@ class UserPreference : AppCompatActivity() {
copySshKey(uri) copySshKey(uri)
Toast.makeText( Toast.makeText(
this, this,
this.resources.getString(R.string.ssh_key_success_dialog_title), this.resources.getString(R.string.ssh_key_success_dialog_title),
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext) val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
@ -668,13 +668,13 @@ class UserPreference : AppCompatActivity() {
if (Environment.getExternalStorageDirectory().path == repoPath) { if (Environment.getExternalStorageDirectory().path == repoPath) {
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.sdcard_root_warning_title)) .setTitle(getString(R.string.sdcard_root_warning_title))
.setMessage(getString(R.string.sdcard_root_warning_message)) .setMessage(getString(R.string.sdcard_root_warning_message))
.setPositiveButton("Remove everything") { _, _ -> .setPositiveButton("Remove everything") { _, _ ->
prefs.edit { putString("git_external_repo", uri?.path) } prefs.edit { putString("git_external_repo", uri?.path) }
} }
.setNegativeButton(R.string.dialog_cancel, null) .setNegativeButton(R.string.dialog_cancel, null)
.show() .show()
} }
prefs.edit { putString("git_external_repo", repoPath) } prefs.edit { putString("git_external_repo", repoPath) }
} }
@ -693,9 +693,9 @@ class UserPreference : AppCompatActivity() {
val uri: Uri = data.data ?: throw IOException("Unable to open file") val uri: Uri = data.data ?: throw IOException("Unable to open file")
Toast.makeText( Toast.makeText(
this, this,
this.resources.getString(R.string.xkpwgen_custom_dict_imported, uri.path), this.resources.getString(R.string.xkpwgen_custom_dict_imported, uri.path),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext) val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
@ -731,8 +731,8 @@ class UserPreference : AppCompatActivity() {
val dateString = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val dateString = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
LocalDateTime LocalDateTime
.now() .now()
.format(DateTimeFormatter.ISO_DATE_TIME) .format(DateTimeFormatter.ISO_DATE_TIME)
} else { } else {
String.format("%tFT%<tRZ", Calendar.getInstance(TimeZone.getTimeZone("Z"))) String.format("%tFT%<tRZ", Calendar.getInstance(TimeZone.getTimeZone("Z")))
} }

View file

@ -60,11 +60,11 @@ class AutofillFragment : DialogFragment() {
} else { } else {
val browserIntent = Intent("android.intent.action.VIEW", Uri.parse("http://")) val browserIntent = Intent("android.intent.action.VIEW", Uri.parse("http://"))
val resolveInfo = requireContext().packageManager val resolveInfo = requireContext().packageManager
.resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY) .resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY)
iconPackageName = resolveInfo?.activityInfo?.packageName iconPackageName = resolveInfo?.activityInfo?.packageName
builder.setTitle("Website") builder.setTitle("Website")
(view.findViewById<View>(R.id.webURL) as EditText).setText(packageName (view.findViewById<View>(R.id.webURL) as EditText).setText(packageName
?: "com.android.browser") ?: "com.android.browser")
} }
try { try {
if (iconPackageName != null) { if (iconPackageName != null) {
@ -86,9 +86,9 @@ class AutofillFragment : DialogFragment() {
(view.findViewById<View>(R.id.matched) as ListView).adapter = adapter (view.findViewById<View>(R.id.matched) as ListView).adapter = adapter
// delete items by clicking them // delete items by clicking them
(view.findViewById<View>(R.id.matched) as ListView).onItemClickListener = (view.findViewById<View>(R.id.matched) as ListView).onItemClickListener =
AdapterView.OnItemClickListener { _, _, position, _ -> AdapterView.OnItemClickListener { _, _, position, _ ->
adapter!!.remove(adapter!!.getItem(position)) adapter!!.remove(adapter!!.getItem(position))
} }
// set the existing preference, if any // set the existing preference, if any
val prefs: SharedPreferences = if (!isWeb) { val prefs: SharedPreferences = if (!isWeb) {
@ -122,7 +122,7 @@ class AutofillFragment : DialogFragment() {
if (isWeb) { if (isWeb) {
builder.setNeutralButton(R.string.autofill_apps_delete) { _, _ -> builder.setNeutralButton(R.string.autofill_apps_delete) { _, _ ->
if (callingActivity.recyclerAdapter != null && if (callingActivity.recyclerAdapter != null &&
packageName != null && packageName != "") { packageName != null && packageName != "") {
prefs.edit { prefs.edit {
remove(packageName) remove(packageName)
callingActivity.recyclerAdapter?.removeWebsite(packageName) callingActivity.recyclerAdapter?.removeWebsite(packageName)

View file

@ -19,9 +19,9 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import me.zhanghai.android.fastscroll.FastScrollerBuilder
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.ArrayList import java.util.ArrayList
import me.zhanghai.android.fastscroll.FastScrollerBuilder
class AutofillPreferenceActivity : AppCompatActivity() { class AutofillPreferenceActivity : AppCompatActivity() {

View file

@ -18,9 +18,9 @@ import androidx.recyclerview.widget.SortedList
import androidx.recyclerview.widget.SortedListAdapterCallback import androidx.recyclerview.widget.SortedListAdapterCallback
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.utils.splitLines import com.zeapo.pwdstore.utils.splitLines
import me.zhanghai.android.fastscroll.PopupTextProvider
import java.util.ArrayList import java.util.ArrayList
import java.util.Locale import java.util.Locale
import me.zhanghai.android.fastscroll.PopupTextProvider
internal class AutofillRecyclerAdapter( internal class AutofillRecyclerAdapter(
allApps: List<AppInfo>, allApps: List<AppInfo>,
@ -63,7 +63,7 @@ internal class AutofillRecyclerAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context) val v = LayoutInflater.from(parent.context)
.inflate(R.layout.autofill_row_layout, parent, false) .inflate(R.layout.autofill_row_layout, parent, false)
return ViewHolder(v) return ViewHolder(v)
} }
@ -96,7 +96,7 @@ internal class AutofillRecyclerAdapter(
holder.secondary.append(" " + preference!!.splitLines()[0]) holder.secondary.append(" " + preference!!.splitLines()[0])
if (preference.trim { it <= ' ' }.splitLines().size - 1 > 0) { if (preference.trim { it <= ' ' }.splitLines().size - 1 > 0) {
holder.secondary.append(" and " + holder.secondary.append(" and " +
(preference.trim { it <= ' ' }.splitLines().size - 1) + " more") (preference.trim { it <= ' ' }.splitLines().size - 1) + " more")
} }
} }
} }

View file

@ -32,15 +32,6 @@ import com.zeapo.pwdstore.PasswordEntry
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.utils.PasswordRepository import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.splitLines import com.zeapo.pwdstore.utils.splitLines
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.io.UnsupportedEncodingException
import java.net.MalformedURLException
import java.net.URL
import java.util.ArrayList
import java.util.Locale
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
@ -51,6 +42,15 @@ import me.msfjarvis.openpgpktx.util.OpenPgpServiceConnection
import org.apache.commons.io.FileUtils import org.apache.commons.io.FileUtils
import org.openintents.openpgp.IOpenPgpService2 import org.openintents.openpgp.IOpenPgpService2
import org.openintents.openpgp.OpenPgpError import org.openintents.openpgp.OpenPgpError
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.io.UnsupportedEncodingException
import java.net.MalformedURLException
import java.net.URL
import java.util.ArrayList
import java.util.Locale
class AutofillService : AccessibilityService(), CoroutineScope by CoroutineScope(Dispatchers.Default) { class AutofillService : AccessibilityService(), CoroutineScope by CoroutineScope(Dispatchers.Default) {
private var serviceConnection: OpenPgpServiceConnection? = null private var serviceConnection: OpenPgpServiceConnection? = null
@ -103,16 +103,16 @@ class AutofillService : AccessibilityService(), CoroutineScope by CoroutineScope
// if returning to the source app from a successful AutofillActivity // if returning to the source app from a successful AutofillActivity
if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED &&
event.packageName != null && event.packageName == packageName && event.packageName != null && event.packageName == packageName &&
resultData != null) { resultData != null) {
bindDecryptAndVerify() bindDecryptAndVerify()
} }
// look for webView and trigger accessibility events if window changes // look for webView and trigger accessibility events if window changes
// or if page changes in chrome // or if page changes in chrome
if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED || (event.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED && if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED || (event.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED &&
event.packageName != null && event.packageName != null &&
(event.packageName == "com.android.chrome" || event.packageName == "com.android.browser"))) { (event.packageName == "com.android.chrome" || event.packageName == "com.android.browser"))) {
// there is a chance for getRootInActiveWindow() to return null at any time. save it. // there is a chance for getRootInActiveWindow() to return null at any time. save it.
try { try {
val root = rootInActiveWindow val root = rootInActiveWindow
@ -145,8 +145,8 @@ class AutofillService : AccessibilityService(), CoroutineScope by CoroutineScope
// nothing to do if field is keychain app or system ui // nothing to do if field is keychain app or system ui
if (event.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED || if (event.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED ||
event.packageName != null && event.packageName == "org.sufficientlysecure.keychain" || event.packageName != null && event.packageName == "org.sufficientlysecure.keychain" ||
event.packageName != null && event.packageName == "com.android.systemui") { event.packageName != null && event.packageName == "com.android.systemui") {
dismissDialog() dismissDialog()
return return
} }
@ -182,7 +182,7 @@ class AutofillService : AccessibilityService(), CoroutineScope by CoroutineScope
// need to request permission before attempting to draw dialog // need to request permission before attempting to draw dialog
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName())) Uri.parse("package:" + getPackageName()))
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent) startActivity(intent)
return return
@ -283,7 +283,7 @@ class AutofillService : AccessibilityService(), CoroutineScope by CoroutineScope
val value = prefs.getString(key, null) val value = prefs.getString(key, null)
val keyLowerCase = key.toLowerCase(Locale.ROOT) val keyLowerCase = key.toLowerCase(Locale.ROOT)
if (value != null && value != "" && if (value != null && value != "" &&
(webViewUrlLowerCase.contains(keyLowerCase) || keyLowerCase.contains(webViewUrlLowerCase))) { (webViewUrlLowerCase.contains(keyLowerCase) || keyLowerCase.contains(webViewUrlLowerCase))) {
preference = value preference = value
settingsURL = key settingsURL = key
} }
@ -419,7 +419,7 @@ class AutofillService : AccessibilityService(), CoroutineScope by CoroutineScope
for (i in items.indices) { for (i in items.indices) {
if (autofillFullPath) { if (autofillFullPath) {
itemNames[i] = items[i].path.replace(".gpg", "") itemNames[i] = items[i].path.replace(".gpg", "")
.replace("$passwordDirectory/", "") .replace("$passwordDirectory/", "")
} else { } else {
itemNames[i] = items[i].name.replace(".gpg", "") itemNames[i] = items[i].name.replace(".gpg", "")
} }

View file

@ -31,6 +31,7 @@ private fun Context.matchPreferences(formOrigin: FormOrigin): SharedPreferences
class AutofillPublisherChangedException(val formOrigin: FormOrigin) : class AutofillPublisherChangedException(val formOrigin: FormOrigin) :
Exception("The publisher of '${formOrigin.identifier}' changed since an entry was first matched with this app") { Exception("The publisher of '${formOrigin.identifier}' changed since an entry was first matched with this app") {
init { init {
require(formOrigin is FormOrigin.App) require(formOrigin is FormOrigin.App)
} }
@ -40,6 +41,7 @@ class AutofillPublisherChangedException(val formOrigin: FormOrigin) :
* Manages "matches", i.e., associations between apps or websites and Password Store entries. * Manages "matches", i.e., associations between apps or websites and Password Store entries.
*/ */
class AutofillMatcher { class AutofillMatcher {
companion object { companion object {
private const val MAX_NUM_MATCHES = 10 private const val MAX_NUM_MATCHES = 10
@ -172,10 +174,10 @@ class AutofillMatcher {
.minus(deletePathList) .minus(deletePathList)
.minus(oldNewPathMap.values) .minus(oldNewPathMap.values)
.map { match -> .map { match ->
val newPath = oldNewPathMap[match] ?: return@map match val newPath = oldNewPathMap[match] ?: return@map match
d { "Updating match for $key: $match --> $newPath" } d { "Updating match for $key: $match --> $newPath" }
newPath newPath
}.toSet() }.toSet()
if (newMatches != oldMatches) if (newMatches != oldMatches)
prefs.edit { putStringSet(key, newMatches) } prefs.edit { putStringSet(key, newMatches) }
} }

View file

@ -130,6 +130,7 @@ data class ClassifiedAutofillScenario<T : Any>(
val currentPassword: List<T>, val currentPassword: List<T>,
val newPassword: List<T> val newPassword: List<T>
) : AutofillScenario<T>() { ) : AutofillScenario<T>() {
override val allPasswordFields override val allPasswordFields
get() = currentPassword + newPassword get() = currentPassword + newPassword
override val passwordFieldsToFillOnMatch override val passwordFieldsToFillOnMatch
@ -148,6 +149,7 @@ data class GenericAutofillScenario<T : Any>(
override val fillUsername: Boolean, override val fillUsername: Boolean,
val genericPassword: List<T> val genericPassword: List<T>
) : AutofillScenario<T>() { ) : AutofillScenario<T>() {
override val allPasswordFields override val allPasswordFields
get() = genericPassword get() = genericPassword
override val passwordFieldsToFillOnMatch override val passwordFieldsToFillOnMatch

View file

@ -14,10 +14,12 @@ annotation class AutofillDsl
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
interface FieldMatcher { interface FieldMatcher {
fun match(fields: List<FormField>, alreadyMatched: List<FormField>): List<FormField>? fun match(fields: List<FormField>, alreadyMatched: List<FormField>): List<FormField>?
@AutofillDsl @AutofillDsl
class Builder { class Builder {
private var takeSingle: (FormField.(List<FormField>) -> Boolean)? = null private var takeSingle: (FormField.(List<FormField>) -> Boolean)? = null
private val tieBreakersSingle: MutableList<FormField.(List<FormField>) -> Boolean> = private val tieBreakersSingle: MutableList<FormField.(List<FormField>) -> Boolean> =
mutableListOf() mutableListOf()
@ -68,6 +70,7 @@ class SingleFieldMatcher(
@AutofillDsl @AutofillDsl
class Builder { class Builder {
private var takeSingle: (FormField.(List<FormField>) -> Boolean)? = null private var takeSingle: (FormField.(List<FormField>) -> Boolean)? = null
private val tieBreakersSingle: MutableList<FormField.(List<FormField>) -> Boolean> = private val tieBreakersSingle: MutableList<FormField.(List<FormField>) -> Boolean> =
mutableListOf() mutableListOf()
@ -169,6 +172,7 @@ class AutofillRule private constructor(
private val applyInSingleOriginMode: Boolean, private val applyInSingleOriginMode: Boolean,
private val applyOnManualRequestOnly: Boolean private val applyOnManualRequestOnly: Boolean
) { ) {
companion object { companion object {
private var ruleId = 1 private var ruleId = 1
} }
@ -304,6 +308,7 @@ class AutofillStrategy private constructor(private val rules: List<AutofillRule>
@AutofillDsl @AutofillDsl
class Builder { class Builder {
private val rules: MutableList<AutofillRule> = mutableListOf() private val rules: MutableList<AutofillRule> = mutableListOf()
fun rule( fun rule(

View file

@ -31,6 +31,7 @@ import java.io.File
* A unique identifier for either an Android app (package name) or a website (origin minus port). * A unique identifier for either an Android app (package name) or a website (origin minus port).
*/ */
sealed class FormOrigin(open val identifier: String) { sealed class FormOrigin(open val identifier: String) {
data class Web(override val identifier: String) : FormOrigin(identifier) data class Web(override val identifier: String) : FormOrigin(identifier)
data class App(override val identifier: String) : FormOrigin(identifier) data class App(override val identifier: String) : FormOrigin(identifier)
@ -198,6 +199,7 @@ class FillableForm private constructor(
private val ignoredIds: List<AutofillId>, private val ignoredIds: List<AutofillId>,
private val saveFlags: Int? private val saveFlags: Int?
) { ) {
companion object { companion object {
fun makeFillInDataset( fun makeFillInDataset(
context: Context, context: Context,

View file

@ -22,16 +22,6 @@ import com.zeapo.pwdstore.autofill.oreo.AutofillPreferences
import com.zeapo.pwdstore.autofill.oreo.Credentials import com.zeapo.pwdstore.autofill.oreo.Credentials
import com.zeapo.pwdstore.autofill.oreo.DirectoryStructure import com.zeapo.pwdstore.autofill.oreo.DirectoryStructure
import com.zeapo.pwdstore.autofill.oreo.FillableForm import com.zeapo.pwdstore.autofill.oreo.FillableForm
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileNotFoundException
import java.io.InputStream
import java.io.OutputStream
import java.io.UnsupportedEncodingException
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
@ -42,6 +32,16 @@ import me.msfjarvis.openpgpktx.util.OpenPgpApi
import me.msfjarvis.openpgpktx.util.OpenPgpServiceConnection import me.msfjarvis.openpgpktx.util.OpenPgpServiceConnection
import org.openintents.openpgp.IOpenPgpService2 import org.openintents.openpgp.IOpenPgpService2
import org.openintents.openpgp.OpenPgpError import org.openintents.openpgp.OpenPgpError
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileNotFoundException
import java.io.InputStream
import java.io.OutputStream
import java.io.UnsupportedEncodingException
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
class AutofillDecryptActivity : Activity(), CoroutineScope { class AutofillDecryptActivity : Activity(), CoroutineScope {
@ -214,9 +214,9 @@ class AutofillDecryptActivity : Activity(), CoroutineScope {
if (error != null) { if (error != null) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
Toast.makeText( Toast.makeText(
applicationContext, applicationContext,
"Error from OpenKeyChain: ${error.message}", "Error from OpenKeyChain: ${error.message}",
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
} }
e { "OpenPgpApi ACTION_DECRYPT_VERIFY failed (${error.errorId}): ${error.message}" } e { "OpenPgpApi ACTION_DECRYPT_VERIFY failed (${error.errorId}): ${error.message}" }

View file

@ -78,19 +78,19 @@ class AutofillPublisherChangedActivity : AppCompatActivity() {
try { try {
with(binding) { with(binding) {
val packageInfo = val packageInfo =
packageManager.getPackageInfo(appPackage, PackageManager.GET_META_DATA) packageManager.getPackageInfo(appPackage, PackageManager.GET_META_DATA)
val installTime = DateUtils.getRelativeTimeSpanString(packageInfo.firstInstallTime) val installTime = DateUtils.getRelativeTimeSpanString(packageInfo.firstInstallTime)
warningAppInstallDate.text = warningAppInstallDate.text =
getString(R.string.oreo_autofill_warning_publisher_install_time, installTime) getString(R.string.oreo_autofill_warning_publisher_install_time, installTime)
val appInfo = val appInfo =
packageManager.getApplicationInfo(appPackage, PackageManager.GET_META_DATA) packageManager.getApplicationInfo(appPackage, PackageManager.GET_META_DATA)
warningAppName.text = "${packageManager.getApplicationLabel(appInfo)}" warningAppName.text = "${packageManager.getApplicationLabel(appInfo)}"
val currentHash = computeCertificatesHash(this@AutofillPublisherChangedActivity, appPackage) val currentHash = computeCertificatesHash(this@AutofillPublisherChangedActivity, appPackage)
warningAppAdvancedInfo.text = getString( warningAppAdvancedInfo.text = getString(
R.string.oreo_autofill_warning_publisher_advanced_info_template, R.string.oreo_autofill_warning_publisher_advanced_info_template,
appPackage, appPackage,
currentHash currentHash
) )
} }
} catch (exception: Exception) { } catch (exception: Exception) {

View file

@ -52,13 +52,28 @@ import com.zeapo.pwdstore.autofill.oreo.DirectoryStructure
import com.zeapo.pwdstore.ui.dialogs.PasswordGeneratorDialogFragment import com.zeapo.pwdstore.ui.dialogs.PasswordGeneratorDialogFragment
import com.zeapo.pwdstore.ui.dialogs.XkPasswordGeneratorDialogFragment import com.zeapo.pwdstore.ui.dialogs.XkPasswordGeneratorDialogFragment
import com.zeapo.pwdstore.utils.Otp import com.zeapo.pwdstore.utils.Otp
import java.io.ByteArrayInputStream import kotlinx.android.synthetic.main.decrypt_layout.crypto_container_decrypt
import java.io.ByteArrayOutputStream import kotlinx.android.synthetic.main.decrypt_layout.crypto_copy_otp
import java.io.File import kotlinx.android.synthetic.main.decrypt_layout.crypto_copy_username
import java.nio.charset.Charset import kotlinx.android.synthetic.main.decrypt_layout.crypto_extra_show
import java.util.Date import kotlinx.android.synthetic.main.decrypt_layout.crypto_extra_show_layout
import kotlinx.android.synthetic.main.decrypt_layout.* import kotlinx.android.synthetic.main.decrypt_layout.crypto_extra_toggle_show
import kotlinx.android.synthetic.main.encrypt_layout.* import kotlinx.android.synthetic.main.decrypt_layout.crypto_otp_show
import kotlinx.android.synthetic.main.decrypt_layout.crypto_otp_show_label
import kotlinx.android.synthetic.main.decrypt_layout.crypto_password_category_decrypt
import kotlinx.android.synthetic.main.decrypt_layout.crypto_password_file
import kotlinx.android.synthetic.main.decrypt_layout.crypto_password_last_changed
import kotlinx.android.synthetic.main.decrypt_layout.crypto_password_show
import kotlinx.android.synthetic.main.decrypt_layout.crypto_password_show_label
import kotlinx.android.synthetic.main.decrypt_layout.crypto_password_toggle_show
import kotlinx.android.synthetic.main.decrypt_layout.crypto_username_show
import kotlinx.android.synthetic.main.decrypt_layout.crypto_username_show_label
import kotlinx.android.synthetic.main.encrypt_layout.crypto_extra_edit
import kotlinx.android.synthetic.main.encrypt_layout.crypto_password_category
import kotlinx.android.synthetic.main.encrypt_layout.crypto_password_edit
import kotlinx.android.synthetic.main.encrypt_layout.crypto_password_file_edit
import kotlinx.android.synthetic.main.encrypt_layout.encrypt_username
import kotlinx.android.synthetic.main.encrypt_layout.generate_password
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.msfjarvis.openpgpktx.util.OpenPgpApi import me.msfjarvis.openpgpktx.util.OpenPgpApi
@ -74,6 +89,11 @@ import org.apache.commons.io.FileUtils
import org.apache.commons.io.FilenameUtils import org.apache.commons.io.FilenameUtils
import org.openintents.openpgp.IOpenPgpService2 import org.openintents.openpgp.IOpenPgpService2
import org.openintents.openpgp.OpenPgpError import org.openintents.openpgp.OpenPgpError
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.nio.charset.Charset
import java.util.Date
class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound { class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
private val clipboard by lazy { getSystemService<ClipboardManager>() } private val clipboard by lazy { getSystemService<ClipboardManager>() }
@ -96,10 +116,10 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
private val name: String by lazy { getName(fullPath) } private val name: String by lazy { getName(fullPath) }
private val lastChangedString: CharSequence by lazy { private val lastChangedString: CharSequence by lazy {
getLastChangedString( getLastChangedString(
intent.getLongExtra( intent.getLongExtra(
"LAST_CHANGED_TIMESTAMP", "LAST_CHANGED_TIMESTAMP",
-1L -1L
) )
) )
} }
private val relativeParentPath: String by lazy { getParentPath(fullPath, repoPath) } private val relativeParentPath: String by lazy { getParentPath(fullPath, repoPath) }
@ -325,8 +345,8 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
val pi: PendingIntent? = result.getParcelableExtra(RESULT_INTENT) val pi: PendingIntent? = result.getParcelableExtra(RESULT_INTENT)
try { try {
this@PgpActivity.startIntentSenderFromChild( this@PgpActivity.startIntentSenderFromChild(
this@PgpActivity, pi?.intentSender, requestCode, this@PgpActivity, pi?.intentSender, requestCode,
null, 0, 0, 0 null, 0, 0, 0
) )
} catch (e: IntentSender.SendIntentException) { } catch (e: IntentSender.SendIntentException) {
e(e) { "SendIntentException" } e(e) { "SendIntentException" }
@ -401,8 +421,8 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
null null
} else { } else {
HoldToShowPasswordTransformation( HoldToShowPasswordTransformation(
crypto_password_toggle_show, crypto_password_toggle_show,
Runnable { crypto_password_show.text = entry.password } Runnable { crypto_password_show.text = entry.password }
) )
} }
@ -455,19 +475,19 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
if (entry.hasTotp()) { if (entry.hasTotp()) {
crypto_copy_otp.setOnClickListener { crypto_copy_otp.setOnClickListener {
copyOtpToClipBoard( copyOtpToClipBoard(
Otp.calculateCode( Otp.calculateCode(
entry.totpSecret, entry.totpSecret,
Date().time / (1000 * entry.totpPeriod), Date().time / (1000 * entry.totpPeriod),
entry.totpAlgorithm, entry.totpAlgorithm,
entry.digits) entry.digits)
) )
} }
crypto_otp_show.text = crypto_otp_show.text =
Otp.calculateCode( Otp.calculateCode(
entry.totpSecret, entry.totpSecret,
Date().time / (1000 * entry.totpPeriod), Date().time / (1000 * entry.totpPeriod),
entry.totpAlgorithm, entry.totpAlgorithm,
entry.digits) entry.digits)
} else { } else {
// we only want to calculate and show HOTP if the user requests it // we only want to calculate and show HOTP if the user requests it
crypto_copy_otp.setOnClickListener { crypto_copy_otp.setOnClickListener {
@ -482,31 +502,31 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
val checkInflater = LayoutInflater.from(this@PgpActivity) val checkInflater = LayoutInflater.from(this@PgpActivity)
val checkLayout = checkInflater.inflate(R.layout.otp_confirm_layout, null) val checkLayout = checkInflater.inflate(R.layout.otp_confirm_layout, null)
val rememberCheck: CheckBox = val rememberCheck: CheckBox =
checkLayout.findViewById(R.id.hotp_remember_checkbox) checkLayout.findViewById(R.id.hotp_remember_checkbox)
val dialogBuilder = MaterialAlertDialogBuilder(this@PgpActivity) val dialogBuilder = MaterialAlertDialogBuilder(this@PgpActivity)
dialogBuilder.setView(checkLayout) dialogBuilder.setView(checkLayout)
dialogBuilder.setMessage(R.string.dialog_update_body) dialogBuilder.setMessage(R.string.dialog_update_body)
.setCancelable(false) .setCancelable(false)
.setPositiveButton(R.string.dialog_update_positive) { _, _ -> .setPositiveButton(R.string.dialog_update_positive) { _, _ ->
run { run {
calculateAndCommitHotp(entry) calculateAndCommitHotp(entry)
if (rememberCheck.isChecked) { if (rememberCheck.isChecked) {
settings.edit {
putBoolean("hotp_remember_check", true)
putBoolean("hotp_remember_choice", true)
}
}
}
}
.setNegativeButton(R.string.dialog_update_negative) { _, _ ->
run {
calculateHotp(entry)
settings.edit { settings.edit {
putBoolean("hotp_remember_check", true) putBoolean("hotp_remember_check", true)
putBoolean("hotp_remember_choice", false) putBoolean("hotp_remember_choice", true)
} }
} }
} }
}
.setNegativeButton(R.string.dialog_update_negative) { _, _ ->
run {
calculateHotp(entry)
settings.edit {
putBoolean("hotp_remember_check", true)
putBoolean("hotp_remember_choice", false)
}
}
}
val updateDialog = dialogBuilder.create() val updateDialog = dialogBuilder.create()
updateDialog.setTitle(R.string.dialog_update_title) updateDialog.setTitle(R.string.dialog_update_title)
updateDialog.show() updateDialog.show()
@ -612,11 +632,11 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
if (shouldGeneratePassword) { if (shouldGeneratePassword) {
val directoryStructure = val directoryStructure =
AutofillPreferences.directoryStructure(applicationContext) AutofillPreferences.directoryStructure(applicationContext)
val entry = PasswordEntry(content) val entry = PasswordEntry(content)
returnIntent.putExtra("PASSWORD", entry.password) returnIntent.putExtra("PASSWORD", entry.password)
val username = PasswordEntry(content).username val username = PasswordEntry(content).username
?: directoryStructure.getUsernameFor(file) ?: directoryStructure.getUsernameFor(file)
returnIntent.putExtra("USERNAME", username) returnIntent.putExtra("USERNAME", username)
} }
@ -640,9 +660,9 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
generate_password?.setOnClickListener { generate_password?.setOnClickListener {
when (settings.getString("pref_key_pwgen_type", KEY_PWGEN_TYPE_CLASSIC)) { when (settings.getString("pref_key_pwgen_type", KEY_PWGEN_TYPE_CLASSIC)) {
KEY_PWGEN_TYPE_CLASSIC -> PasswordGeneratorDialogFragment() KEY_PWGEN_TYPE_CLASSIC -> PasswordGeneratorDialogFragment()
.show(supportFragmentManager, "generator") .show(supportFragmentManager, "generator")
KEY_PWGEN_TYPE_XKPASSWD -> XkPasswordGeneratorDialogFragment() KEY_PWGEN_TYPE_XKPASSWD -> XkPasswordGeneratorDialogFragment()
.show(supportFragmentManager, "xkpwgenerator") .show(supportFragmentManager, "xkpwgenerator")
} }
} }
@ -716,7 +736,7 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
RESULT_CODE_SUCCESS -> { RESULT_CODE_SUCCESS -> {
try { try {
val ids = result.getLongArrayExtra(OpenPgpApi.RESULT_KEY_IDS) val ids = result.getLongArrayExtra(OpenPgpApi.RESULT_KEY_IDS)
?: LongArray(0) ?: LongArray(0)
val keys = ids.map { it.toString() }.toSet() val keys = ids.map { it.toString() }.toSet()
// use Long // use Long
@ -777,7 +797,8 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
private inner class HoldToShowPasswordTransformation constructor(button: Button, private val onToggle: Runnable) : private inner class HoldToShowPasswordTransformation constructor(button: Button, private val onToggle: Runnable) :
PasswordTransformationMethod(), View.OnTouchListener { PasswordTransformationMethod(), View.OnTouchListener {
private var shown = false private var shown = false
init { init {
@ -856,10 +877,10 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
sendIntent.putExtra(Intent.EXTRA_TEXT, passwordEntry?.password) sendIntent.putExtra(Intent.EXTRA_TEXT, passwordEntry?.password)
sendIntent.type = "text/plain" sendIntent.type = "text/plain"
startActivity( startActivity(
Intent.createChooser( Intent.createChooser(
sendIntent, sendIntent,
resources.getText(R.string.send_plaintext_password_to) resources.getText(R.string.send_plaintext_password_to)
) )
) // Always show a picker to give the user a chance to cancel ) // Always show a picker to give the user a chance to cancel
} }
@ -970,7 +991,7 @@ class PgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBound {
* Gets the relative path to the repository * Gets the relative path to the repository
*/ */
fun getRelativePath(fullPath: String, repositoryPath: String): String = fun getRelativePath(fullPath: String, repositoryPath: String): String =
fullPath.replace(repositoryPath, "").replace("/+".toRegex(), "/") fullPath.replace(repositoryPath, "").replace("/+".toRegex(), "/")
/** /**
* Gets the Parent path, relative to the repository * Gets the Parent path, relative to the repository

View file

@ -31,6 +31,7 @@ import java.net.URI
* tasks and makes sense to be held here. * tasks and makes sense to be held here.
*/ */
abstract class BaseGitActivity : AppCompatActivity() { abstract class BaseGitActivity : AppCompatActivity() {
lateinit var protocol: Protocol lateinit var protocol: Protocol
lateinit var connectionMode: ConnectionMode lateinit var connectionMode: ConnectionMode
var url: String? = null var url: String? = null
@ -180,7 +181,7 @@ abstract class BaseGitActivity : AppCompatActivity() {
} }
} }
op.executeAfterAuthentication(connectionMode, serverUser, op.executeAfterAuthentication(connectionMode, serverUser,
File("$filesDir/.ssh_key"), identity) File("$filesDir/.ssh_key"), identity)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
MaterialAlertDialogBuilder(this).setMessage(e.message).show() MaterialAlertDialogBuilder(this).setMessage(e.message).show()

View file

@ -7,11 +7,11 @@ package com.zeapo.pwdstore.git
import android.app.Activity import android.app.Activity
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import java.io.File
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.GitCommand import org.eclipse.jgit.api.GitCommand
import org.eclipse.jgit.api.PushCommand import org.eclipse.jgit.api.PushCommand
import org.eclipse.jgit.api.RebaseCommand import org.eclipse.jgit.api.RebaseCommand
import java.io.File
class BreakOutOfDetached(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) { class BreakOutOfDetached(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) {
private lateinit var commands: List<GitCommand<out Any>> private lateinit var commands: List<GitCommand<out Any>>
@ -26,14 +26,14 @@ class BreakOutOfDetached(fileDir: File, callingActivity: Activity) : GitOperatio
val branchName = "conflicting-master-${System.currentTimeMillis()}" val branchName = "conflicting-master-${System.currentTimeMillis()}"
this.commands = listOf( this.commands = listOf(
// abort the rebase // abort the rebase
git.rebase().setOperation(RebaseCommand.Operation.ABORT), git.rebase().setOperation(RebaseCommand.Operation.ABORT),
// git checkout -b conflict-branch // git checkout -b conflict-branch
git.checkout().setCreateBranch(true).setName(branchName), git.checkout().setCreateBranch(true).setName(branchName),
// push the changes // push the changes
git.push().setRemote("origin"), git.push().setRemote("origin"),
// switch back to master // switch back to master
git.checkout().setName("master") git.checkout().setName("master")
) )
return this return this
} }
@ -42,11 +42,11 @@ class BreakOutOfDetached(fileDir: File, callingActivity: Activity) : GitOperatio
val git = Git(repository) val git = Git(repository)
if (!git.repository.repositoryState.isRebasing) { if (!git.repository.repositoryState.isRebasing) {
MaterialAlertDialogBuilder(callingActivity) MaterialAlertDialogBuilder(callingActivity)
.setTitle(callingActivity.resources.getString(R.string.git_abort_and_push_title)) .setTitle(callingActivity.resources.getString(R.string.git_abort_and_push_title))
.setMessage("The repository is not rebasing, no need to push to another branch") .setMessage("The repository is not rebasing, no need to push to another branch")
.setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> .setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ ->
callingActivity.finish() callingActivity.finish()
}.show() }.show()
return return
} }
@ -59,26 +59,26 @@ class BreakOutOfDetached(fileDir: File, callingActivity: Activity) : GitOperatio
} }
} }
GitAsyncTask(callingActivity, true, this, null) GitAsyncTask(callingActivity, true, this, null)
.execute(*this.commands.toTypedArray()) .execute(*this.commands.toTypedArray())
} }
override fun onError(errorMessage: String) { override fun onError(errorMessage: String) {
MaterialAlertDialogBuilder(callingActivity) MaterialAlertDialogBuilder(callingActivity)
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title)) .setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
.setMessage("Error occurred when checking out another branch operation $errorMessage") .setMessage("Error occurred when checking out another branch operation $errorMessage")
.setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> .setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ ->
callingActivity.finish() callingActivity.finish()
}.show() }.show()
} }
override fun onSuccess() { override fun onSuccess() {
MaterialAlertDialogBuilder(callingActivity) MaterialAlertDialogBuilder(callingActivity)
.setTitle(callingActivity.resources.getString(R.string.git_abort_and_push_title)) .setTitle(callingActivity.resources.getString(R.string.git_abort_and_push_title))
.setMessage("There was a conflict when trying to rebase. " + .setMessage("There was a conflict when trying to rebase. " +
"Your local master branch was pushed to another branch named conflicting-master-....\n" + "Your local master branch was pushed to another branch named conflicting-master-....\n" +
"Use this branch to resolve conflict on your computer") "Use this branch to resolve conflict on your computer")
.setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> .setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ ->
callingActivity.finish() callingActivity.finish()
}.show() }.show()
} }
} }

View file

@ -8,9 +8,9 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import java.io.File
import org.eclipse.jgit.api.CloneCommand import org.eclipse.jgit.api.CloneCommand
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import java.io.File
/** /**
* Creates a new clone operation * Creates a new clone operation
@ -28,9 +28,9 @@ class CloneOperation(fileDir: File, callingActivity: Activity) : GitOperation(fi
*/ */
fun setCommand(uri: String): CloneOperation { fun setCommand(uri: String): CloneOperation {
this.command = Git.cloneRepository() this.command = Git.cloneRepository()
.setCloneAllBranches(true) .setCloneAllBranches(true)
.setDirectory(repository?.workTree) .setDirectory(repository?.workTree)
.setURI(uri) .setURI(uri)
return this return this
} }
@ -67,12 +67,12 @@ class CloneOperation(fileDir: File, callingActivity: Activity) : GitOperation(fi
override fun onError(errorMessage: String) { override fun onError(errorMessage: String) {
super.onError(errorMessage) super.onError(errorMessage)
MaterialAlertDialogBuilder(callingActivity) MaterialAlertDialogBuilder(callingActivity)
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title)) .setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
.setMessage("Error occured during the clone operation, " + .setMessage("Error occured during the clone operation, " +
callingActivity.resources.getString(R.string.jgit_error_dialog_text) + callingActivity.resources.getString(R.string.jgit_error_dialog_text) +
errorMessage + errorMessage +
"\nPlease check the FAQ for possible reasons why this error might occur.") "\nPlease check the FAQ for possible reasons why this error might occur.")
.setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> } .setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> }
.show() .show()
} }
} }

View file

@ -8,9 +8,10 @@ import android.app.Activity;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Intent; import android.content.Intent;
import android.os.AsyncTask; import android.os.AsyncTask;
import com.zeapo.pwdstore.PasswordStore; import com.zeapo.pwdstore.PasswordStore;
import com.zeapo.pwdstore.R; import com.zeapo.pwdstore.R;
import java.lang.ref.WeakReference;
import org.eclipse.jgit.api.CommitCommand; import org.eclipse.jgit.api.CommitCommand;
import org.eclipse.jgit.api.GitCommand; import org.eclipse.jgit.api.GitCommand;
import org.eclipse.jgit.api.PullCommand; import org.eclipse.jgit.api.PullCommand;
@ -21,6 +22,8 @@ import org.eclipse.jgit.api.StatusCommand;
import org.eclipse.jgit.transport.PushResult; import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RemoteRefUpdate; import org.eclipse.jgit.transport.RemoteRefUpdate;
import java.lang.ref.WeakReference;
public class GitAsyncTask extends AsyncTask<GitCommand, Integer, String> { public class GitAsyncTask extends AsyncTask<GitCommand, Integer, String> {
private WeakReference<Activity> activityWeakReference; private WeakReference<Activity> activityWeakReference;
private boolean refreshListOnEnd; private boolean refreshListOnEnd;
@ -29,10 +32,10 @@ public class GitAsyncTask extends AsyncTask<GitCommand, Integer, String> {
private Intent finishWithResultOnEnd; private Intent finishWithResultOnEnd;
public GitAsyncTask( public GitAsyncTask(
Activity activity, Activity activity,
boolean refreshListOnEnd, boolean refreshListOnEnd,
GitOperation operation, GitOperation operation,
Intent finishWithResultOnEnd) { Intent finishWithResultOnEnd) {
this.activityWeakReference = new WeakReference<>(activity); this.activityWeakReference = new WeakReference<>(activity);
this.refreshListOnEnd = refreshListOnEnd; this.refreshListOnEnd = refreshListOnEnd;
this.operation = operation; this.operation = operation;
@ -47,7 +50,7 @@ public class GitAsyncTask extends AsyncTask<GitCommand, Integer, String> {
protected void onPreExecute() { protected void onPreExecute() {
this.dialog.setMessage( this.dialog.setMessage(
getActivity().getResources().getString(R.string.running_dialog_text)); getActivity().getResources().getString(R.string.running_dialog_text));
this.dialog.setCancelable(false); this.dialog.setCancelable(false);
this.dialog.show(); this.dialog.show();
} }
@ -85,13 +88,13 @@ public class GitAsyncTask extends AsyncTask<GitCommand, Integer, String> {
case NON_EXISTING: case NON_EXISTING:
case NOT_ATTEMPTED: case NOT_ATTEMPTED:
return activity.getString(R.string.git_push_generic_error) return activity.getString(R.string.git_push_generic_error)
+ rru.getStatus().name(); + rru.getStatus().name();
case REJECTED_OTHER_REASON: case REJECTED_OTHER_REASON:
if ("non-fast-forward".equals(rru.getMessage())) { if ("non-fast-forward".equals(rru.getMessage())) {
return activity.getString(R.string.git_push_other_error); return activity.getString(R.string.git_push_other_error);
} else { } else {
return activity.getString(R.string.git_push_generic_error) return activity.getString(R.string.git_push_generic_error)
+ rru.getMessage(); + rru.getMessage();
} }
default: default:
break; break;

View file

@ -53,9 +53,9 @@ class GitConfigActivity : BaseGitActivity() {
val name = binding.gitUserName.text.toString().trim() val name = binding.gitUserName.text.toString().trim()
if (!email.matches(Patterns.EMAIL_ADDRESS.toRegex())) { if (!email.matches(Patterns.EMAIL_ADDRESS.toRegex())) {
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setMessage(getString(R.string.invalid_email_dialog_text)) .setMessage(getString(R.string.invalid_email_dialog_text))
.setPositiveButton(getString(R.string.dialog_ok), null) .setPositiveButton(getString(R.string.dialog_ok), null)
.show() .show()
} else { } else {
settings.edit { settings.edit {
putString("git_config_user_email", email) putString("git_config_user_email", email)

View file

@ -25,11 +25,11 @@ import com.zeapo.pwdstore.git.config.SshConfigSessionFactory
import com.zeapo.pwdstore.utils.PasswordRepository import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.getEncryptedPrefs import com.zeapo.pwdstore.utils.getEncryptedPrefs
import com.zeapo.pwdstore.utils.requestInputFocusOnView import com.zeapo.pwdstore.utils.requestInputFocusOnView
import java.io.File
import org.eclipse.jgit.api.GitCommand import org.eclipse.jgit.api.GitCommand
import org.eclipse.jgit.lib.Repository import org.eclipse.jgit.lib.Repository
import org.eclipse.jgit.transport.SshSessionFactory import org.eclipse.jgit.transport.SshSessionFactory
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider
import java.io.File
/** /**
* Creates a new git operation * Creates a new git operation
@ -260,14 +260,14 @@ abstract class GitOperation(fileDir: File, internal val callingActivity: Activit
// Clear various auth related fields on failure // Clear various auth related fields on failure
if (SshSessionFactory.getInstance() is SshApiSessionFactory) { if (SshSessionFactory.getInstance() is SshApiSessionFactory) {
PreferenceManager.getDefaultSharedPreferences(callingActivity.applicationContext) PreferenceManager.getDefaultSharedPreferences(callingActivity.applicationContext)
.edit { putString("ssh_openkeystore_keyid", null) } .edit { putString("ssh_openkeystore_keyid", null) }
callingActivity.applicationContext callingActivity.applicationContext
.getEncryptedPrefs("git_operation") .getEncryptedPrefs("git_operation")
.edit { remove("ssh_key_local_passphrase") } .edit { remove("ssh_key_local_passphrase") }
} else if (SshSessionFactory.getInstance() is GitConfigSessionFactory) { } else if (SshSessionFactory.getInstance() is GitConfigSessionFactory) {
callingActivity.applicationContext callingActivity.applicationContext
.getEncryptedPrefs("git_operation") .getEncryptedPrefs("git_operation")
.edit { remove("https_password") } .edit { remove("https_password") }
} }
} }

View file

@ -56,17 +56,17 @@ open class GitOperationActivity : BaseGitActivity() {
private fun syncRepository(operation: Int) { private fun syncRepository(operation: Int) {
if (serverUser.isEmpty() || serverHostname.isEmpty() || url.isNullOrEmpty()) if (serverUser.isEmpty() || serverHostname.isEmpty() || url.isNullOrEmpty())
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setMessage(getString(R.string.set_information_dialog_text)) .setMessage(getString(R.string.set_information_dialog_text))
.setPositiveButton(getString(R.string.dialog_positive)) { _, _ -> .setPositiveButton(getString(R.string.dialog_positive)) { _, _ ->
val intent = Intent(this, UserPreference::class.java) val intent = Intent(this, UserPreference::class.java)
startActivityForResult(intent, REQUEST_PULL) startActivityForResult(intent, REQUEST_PULL)
} }
.setNegativeButton(getString(R.string.dialog_negative)) { _, _ -> .setNegativeButton(getString(R.string.dialog_negative)) { _, _ ->
// do nothing :( // do nothing :(
setResult(RESULT_OK) setResult(RESULT_OK)
finish() finish()
} }
.show() .show()
else { else {
// check that the remote origin is here, else add it // check that the remote origin is here, else add it
PasswordRepository.addRemote("origin", url!!, true) PasswordRepository.addRemote("origin", url!!, true)

View file

@ -144,27 +144,27 @@ class GitServerConfigActivity : BaseGitActivity() {
val localDirFiles = localDir.listFiles() ?: emptyArray() val localDirFiles = localDir.listFiles() ?: emptyArray()
// Warn if non-empty folder unless it's a just-initialized store that has just a .git folder // Warn if non-empty folder unless it's a just-initialized store that has just a .git folder
if (localDir.exists() && localDirFiles.isNotEmpty() && if (localDir.exists() && localDirFiles.isNotEmpty() &&
!(localDirFiles.size == 1 && localDirFiles[0].name == ".git")) { !(localDirFiles.size == 1 && localDirFiles[0].name == ".git")) {
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setTitle(R.string.dialog_delete_title) .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) .setCancelable(false)
.setPositiveButton(R.string.dialog_delete) { dialog, _ -> .setPositiveButton(R.string.dialog_delete) { dialog, _ ->
try { try {
localDir.deleteRecursively() localDir.deleteRecursively()
launchGitOperation(REQUEST_CLONE) launchGitOperation(REQUEST_CLONE)
} catch (e: IOException) { } catch (e: IOException) {
// TODO Handle the exception correctly if we are unable to delete the directory... // TODO Handle the exception correctly if we are unable to delete the directory...
e.printStackTrace() e.printStackTrace()
MaterialAlertDialogBuilder(this).setMessage(e.message).show() MaterialAlertDialogBuilder(this).setMessage(e.message).show()
} finally { } finally {
dialog.cancel()
}
}
.setNegativeButton(R.string.dialog_do_not_delete) { dialog, _ ->
dialog.cancel() dialog.cancel()
} }
.show() }
.setNegativeButton(R.string.dialog_do_not_delete) { dialog, _ ->
dialog.cancel()
}
.show()
} else { } else {
try { try {
// Silently delete & replace the lone .git folder if it exists // Silently delete & replace the lone .git folder if it exists

View file

@ -8,9 +8,9 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import java.io.File
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.PullCommand import org.eclipse.jgit.api.PullCommand
import java.io.File
/** /**
* Creates a new git operation * Creates a new git operation
@ -27,9 +27,9 @@ class PullOperation(fileDir: File, callingActivity: Activity) : GitOperation(fil
*/ */
fun setCommand(): PullOperation { fun setCommand(): PullOperation {
this.command = Git(repository) this.command = Git(repository)
.pull() .pull()
.setRebase(true) .setRebase(true)
.setRemote("origin") .setRemote("origin")
return this return this
} }
@ -41,12 +41,12 @@ class PullOperation(fileDir: File, callingActivity: Activity) : GitOperation(fil
override fun onError(errorMessage: String) { override fun onError(errorMessage: String) {
super.onError(errorMessage) super.onError(errorMessage)
MaterialAlertDialogBuilder(callingActivity) MaterialAlertDialogBuilder(callingActivity)
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title)) .setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
.setMessage("Error occured during the pull operation, " + .setMessage("Error occured during the pull operation, " +
callingActivity.resources.getString(R.string.jgit_error_dialog_text) + callingActivity.resources.getString(R.string.jgit_error_dialog_text) +
errorMessage + errorMessage +
"\nPlease check the FAQ for possible reasons why this error might occur.") "\nPlease check the FAQ for possible reasons why this error might occur.")
.setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> callingActivity.finish() } .setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> callingActivity.finish() }
.show() .show()
} }
} }

View file

@ -8,9 +8,9 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import java.io.File
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.PushCommand import org.eclipse.jgit.api.PushCommand
import java.io.File
/** /**
* Creates a new git operation * Creates a new git operation
@ -27,9 +27,9 @@ class PushOperation(fileDir: File, callingActivity: Activity) : GitOperation(fil
*/ */
fun setCommand(): PushOperation { fun setCommand(): PushOperation {
this.command = Git(repository) this.command = Git(repository)
.push() .push()
.setPushAll() .setPushAll()
.setRemote("origin") .setRemote("origin")
return this return this
} }
@ -42,9 +42,9 @@ class PushOperation(fileDir: File, callingActivity: Activity) : GitOperation(fil
// TODO handle the "Nothing to push" case // TODO handle the "Nothing to push" case
super.onError(errorMessage) super.onError(errorMessage)
MaterialAlertDialogBuilder(callingActivity) MaterialAlertDialogBuilder(callingActivity)
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title)) .setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
.setMessage(callingActivity.getString(R.string.jgit_error_push_dialog_text) + errorMessage) .setMessage(callingActivity.getString(R.string.jgit_error_push_dialog_text) + errorMessage)
.setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> callingActivity.finish() } .setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> callingActivity.finish() }
.show() .show()
} }
} }

View file

@ -8,11 +8,11 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import java.io.File
import org.eclipse.jgit.api.AddCommand import org.eclipse.jgit.api.AddCommand
import org.eclipse.jgit.api.FetchCommand import org.eclipse.jgit.api.FetchCommand
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.ResetCommand import org.eclipse.jgit.api.ResetCommand
import java.io.File
/** /**
* Creates a new git operation * Creates a new git operation
@ -21,6 +21,7 @@ import org.eclipse.jgit.api.ResetCommand
* @param callingActivity the calling activity * @param callingActivity the calling activity
*/ */
class ResetToRemoteOperation(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) { class ResetToRemoteOperation(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) {
private var addCommand: AddCommand? = null private var addCommand: AddCommand? = null
private var fetchCommand: FetchCommand? = null private var fetchCommand: FetchCommand? = null
private var resetCommand: ResetCommand? = null private var resetCommand: ResetCommand? = null
@ -41,18 +42,18 @@ class ResetToRemoteOperation(fileDir: File, callingActivity: Activity) : GitOper
override fun execute() { override fun execute() {
this.fetchCommand?.setCredentialsProvider(this.provider) this.fetchCommand?.setCredentialsProvider(this.provider)
GitAsyncTask(callingActivity, false, this, Intent()) GitAsyncTask(callingActivity, false, this, Intent())
.execute(this.addCommand, this.fetchCommand, this.resetCommand) .execute(this.addCommand, this.fetchCommand, this.resetCommand)
} }
override fun onError(errorMessage: String) { override fun onError(errorMessage: String) {
super.onError(errorMessage) super.onError(errorMessage)
MaterialAlertDialogBuilder(callingActivity) MaterialAlertDialogBuilder(callingActivity)
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title)) .setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
.setMessage("Error occured during the sync operation, " + .setMessage("Error occured during the sync operation, " +
"\nPlease check the FAQ for possible reasons why this error might occur." + "\nPlease check the FAQ for possible reasons why this error might occur." +
callingActivity.resources.getString(R.string.jgit_error_dialog_text) + callingActivity.resources.getString(R.string.jgit_error_dialog_text) +
errorMessage) errorMessage)
.setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> } .setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> }
.show() .show()
} }
} }

View file

@ -8,13 +8,13 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import java.io.File
import org.eclipse.jgit.api.AddCommand import org.eclipse.jgit.api.AddCommand
import org.eclipse.jgit.api.CommitCommand import org.eclipse.jgit.api.CommitCommand
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.PullCommand import org.eclipse.jgit.api.PullCommand
import org.eclipse.jgit.api.PushCommand import org.eclipse.jgit.api.PushCommand
import org.eclipse.jgit.api.StatusCommand import org.eclipse.jgit.api.StatusCommand
import java.io.File
/** /**
* Creates a new git operation * Creates a new git operation
@ -23,6 +23,7 @@ import org.eclipse.jgit.api.StatusCommand
* @param callingActivity the calling activity * @param callingActivity the calling activity
*/ */
class SyncOperation(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) { class SyncOperation(fileDir: File, callingActivity: Activity) : GitOperation(fileDir, callingActivity) {
private var addCommand: AddCommand? = null private var addCommand: AddCommand? = null
private var statusCommand: StatusCommand? = null private var statusCommand: StatusCommand? = null
private var commitCommand: CommitCommand? = null private var commitCommand: CommitCommand? = null
@ -55,12 +56,12 @@ class SyncOperation(fileDir: File, callingActivity: Activity) : GitOperation(fil
override fun onError(errorMessage: String) { override fun onError(errorMessage: String) {
super.onError(errorMessage) super.onError(errorMessage)
MaterialAlertDialogBuilder(callingActivity) MaterialAlertDialogBuilder(callingActivity)
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title)) .setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
.setMessage("Error occured during the sync operation, " + .setMessage("Error occured during the sync operation, " +
"\nPlease check the FAQ for possible reasons why this error might occur." + "\nPlease check the FAQ for possible reasons why this error might occur." +
callingActivity.resources.getString(R.string.jgit_error_dialog_text) + callingActivity.resources.getString(R.string.jgit_error_dialog_text) +
errorMessage) errorMessage)
.setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> callingActivity.finish() } .setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ -> callingActivity.finish() }
.show() .show()
} }
} }

View file

@ -9,8 +9,10 @@ import android.app.PendingIntent;
import android.content.Intent; import android.content.Intent;
import android.content.IntentSender; import android.content.IntentSender;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.jcraft.jsch.Identity; import com.jcraft.jsch.Identity;
import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSch;
@ -19,8 +21,7 @@ import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo; import com.jcraft.jsch.UserInfo;
import com.zeapo.pwdstore.R; import com.zeapo.pwdstore.R;
import com.zeapo.pwdstore.git.BaseGitActivity; import com.zeapo.pwdstore.git.BaseGitActivity;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import org.eclipse.jgit.errors.UnsupportedCredentialItem; import org.eclipse.jgit.errors.UnsupportedCredentialItem;
import org.eclipse.jgit.transport.CredentialItem; import org.eclipse.jgit.transport.CredentialItem;
import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.CredentialsProvider;
@ -39,6 +40,9 @@ import org.openintents.ssh.authentication.request.SigningRequest;
import org.openintents.ssh.authentication.request.SshPublicKeyRequest; import org.openintents.ssh.authentication.request.SshPublicKeyRequest;
import org.openintents.ssh.authentication.util.SshAuthenticationApiUtils; import org.openintents.ssh.authentication.util.SshAuthenticationApiUtils;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class SshApiSessionFactory extends GitConfigSessionFactory { public class SshApiSessionFactory extends GitConfigSessionFactory {
/** /**
* Intent request code indicating a completed signature that should be posted to an outstanding * Intent request code indicating a completed signature that should be posted to an outstanding
@ -57,7 +61,7 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
@NonNull @NonNull
@Override @Override
protected JSch getJSch(@NonNull final OpenSshConfig.Host hc, @NonNull FS fs) protected JSch getJSch(@NonNull final OpenSshConfig.Host hc, @NonNull FS fs)
throws JSchException { throws JSchException {
JSch jsch = super.getJSch(hc, fs); JSch jsch = super.getJSch(hc, fs);
jsch.removeAllIdentity(); jsch.removeAllIdentity();
jsch.addIdentity(identity, null); jsch.addIdentity(identity, null);
@ -70,28 +74,28 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
session.setConfig("PreferredAuthentications", "publickey"); session.setConfig("PreferredAuthentications", "publickey");
CredentialsProvider provider = CredentialsProvider provider =
new CredentialsProvider() { new CredentialsProvider() {
@Override @Override
public boolean isInteractive() { public boolean isInteractive() {
return false; return false;
} }
@Override @Override
public boolean supports(CredentialItem... items) { public boolean supports(CredentialItem... items) {
return true; return true;
} }
@Override @Override
public boolean get(URIish uri, CredentialItem... items) public boolean get(URIish uri, CredentialItem... items)
throws UnsupportedCredentialItem { throws UnsupportedCredentialItem {
for (CredentialItem item : items) { for (CredentialItem item : items) {
if (item instanceof CredentialItem.Username) { if (item instanceof CredentialItem.Username) {
((CredentialItem.Username) item).setValue(username); ((CredentialItem.Username) item).setValue(username);
}
} }
return true;
} }
}; return true;
}
};
UserInfo userInfo = new CredentialsProviderUserInfo(session, provider); UserInfo userInfo = new CredentialsProviderUserInfo(session, provider);
session.setUserInfo(userInfo); session.setUserInfo(userInfo);
} }
@ -114,14 +118,14 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
* Construct a new IdentityBuilder * Construct a new IdentityBuilder
* *
* @param callingActivity Activity that will be used to launch pending intents and that will * @param callingActivity Activity that will be used to launch pending intents and that will
* receive and handle the results. * receive and handle the results.
*/ */
public IdentityBuilder(BaseGitActivity callingActivity) { public IdentityBuilder(BaseGitActivity callingActivity) {
this.callingActivity = callingActivity; this.callingActivity = callingActivity;
List<String> providers = List<String> providers =
SshAuthenticationApiUtils.getAuthenticationProviderPackageNames( SshAuthenticationApiUtils.getAuthenticationProviderPackageNames(
callingActivity); callingActivity);
if (providers.isEmpty()) if (providers.isEmpty())
throw new RuntimeException(callingActivity.getString(R.string.no_ssh_api_provider)); throw new RuntimeException(callingActivity.getString(R.string.no_ssh_api_provider));
@ -130,12 +134,14 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
connection = new SshAuthenticationConnection(callingActivity, providers.get(0)); connection = new SshAuthenticationConnection(callingActivity, providers.get(0));
settings = settings =
PreferenceManager.getDefaultSharedPreferences( PreferenceManager.getDefaultSharedPreferences(
callingActivity.getApplicationContext()); callingActivity.getApplicationContext());
keyId = settings.getString("ssh_openkeystore_keyid", null); keyId = settings.getString("ssh_openkeystore_keyid", null);
} }
/** Free any resources associated with this IdentityBuilder */ /**
* Free any resources associated with this IdentityBuilder
*/
public void close() { public void close() {
if (connection != null && connection.isConnected()) connection.disconnect(); if (connection != null && connection.isConnected()) connection.disconnect();
} }
@ -143,10 +149,10 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
/** /**
* Helper to invoke an OpenKeyshain SSH API method and correctly interpret the result. * Helper to invoke an OpenKeyshain SSH API method and correctly interpret the result.
* *
* @param request The request intent to launch * @param request The request intent to launch
* @param requestCode The request code to use if a pending intent needs to be sent * @param requestCode The request code to use if a pending intent needs to be sent
* @return The resulting intent if the request completed immediately, or null if we had to * @return The resulting intent if the request completed immediately, or null if we had to
* launch a pending intent to interact with the user * launch a pending intent to interact with the user
*/ */
private Intent executeApi(Request request, int requestCode) { private Intent executeApi(Request request, int requestCode) {
Intent result = api.executeApi(request.toIntent()); Intent result = api.executeApi(request.toIntent());
@ -154,13 +160,13 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
switch (result.getIntExtra(SshAuthenticationApi.EXTRA_RESULT_CODE, -1)) { switch (result.getIntExtra(SshAuthenticationApi.EXTRA_RESULT_CODE, -1)) {
case SshAuthenticationApi.RESULT_CODE_ERROR: case SshAuthenticationApi.RESULT_CODE_ERROR:
SshAuthenticationApiError error = SshAuthenticationApiError error =
result.getParcelableExtra(SshAuthenticationApi.EXTRA_ERROR); result.getParcelableExtra(SshAuthenticationApi.EXTRA_ERROR);
// On an OpenKeychain SSH API error, clear out the stored keyid // On an OpenKeychain SSH API error, clear out the stored keyid
settings.edit().putString("ssh_openkeystore_keyid", null).apply(); settings.edit().putString("ssh_openkeystore_keyid", null).apply();
switch (error.getError()) { switch (error.getError()) {
// If the problem was just a bad keyid, reset to allow them to choose a // If the problem was just a bad keyid, reset to allow them to choose a
// different one // different one
case (SshAuthenticationApiError.NO_SUCH_KEY): case (SshAuthenticationApiError.NO_SUCH_KEY):
case (SshAuthenticationApiError.NO_AUTH_KEY): case (SshAuthenticationApiError.NO_AUTH_KEY):
keyId = null; keyId = null;
@ -169,7 +175,7 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
alg = null; alg = null;
return executeApi(new KeySelectionRequest(), requestCode); return executeApi(new KeySelectionRequest(), requestCode);
// Other errors are fatal // Other errors are fatal
default: default:
throw new RuntimeException(error.getMessage()); throw new RuntimeException(error.getMessage());
} }
@ -177,19 +183,19 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
break; break;
case SshAuthenticationApi.RESULT_CODE_USER_INTERACTION_REQUIRED: case SshAuthenticationApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
PendingIntent pendingIntent = PendingIntent pendingIntent =
result.getParcelableExtra(SshAuthenticationApi.EXTRA_PENDING_INTENT); result.getParcelableExtra(SshAuthenticationApi.EXTRA_PENDING_INTENT);
try { try {
callingActivity.startIntentSenderForResult( callingActivity.startIntentSenderForResult(
pendingIntent.getIntentSender(), requestCode, null, 0, 0, 0); pendingIntent.getIntentSender(), requestCode, null, 0, 0, 0);
return null; return null;
} catch (IntentSender.SendIntentException e) { } catch (IntentSender.SendIntentException e) {
e.printStackTrace(); e.printStackTrace();
throw new RuntimeException( throw new RuntimeException(
callingActivity.getString(R.string.ssh_api_pending_intent_failed)); callingActivity.getString(R.string.ssh_api_pending_intent_failed));
} }
default: default:
throw new RuntimeException( throw new RuntimeException(
callingActivity.getString(R.string.ssh_api_unknown_error)); callingActivity.getString(R.string.ssh_api_unknown_error));
} }
return result; return result;
@ -223,32 +229,32 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
* *
* @param requestCode The request code to use if a pending intent needs to be sent * @param requestCode The request code to use if a pending intent needs to be sent
* @return The built identity, or null of user interaction is still required (in which case * @return The built identity, or null of user interaction is still required (in which case
* a pending intent will have already been launched) * a pending intent will have already been launched)
*/ */
public ApiIdentity tryBuild(int requestCode) { public ApiIdentity tryBuild(int requestCode) {
// First gate, need to initiate a connection to the service and wait for it to connect. // First gate, need to initiate a connection to the service and wait for it to connect.
if (api == null) { if (api == null) {
connection.connect( connection.connect(
new SshAuthenticationConnection.OnBound() { new SshAuthenticationConnection.OnBound() {
@Override @Override
public void onBound(ISshAuthenticationService sshAgent) { public void onBound(ISshAuthenticationService sshAgent) {
api = new SshAuthenticationApi(callingActivity, sshAgent); api = new SshAuthenticationApi(callingActivity, sshAgent);
// We can immediately try the next phase without needing to post // We can immediately try the next phase without needing to post
// back // back
// though onActivityResult // though onActivityResult
callingActivity.onActivityResult( callingActivity.onActivityResult(
requestCode, Activity.RESULT_OK, null); requestCode, Activity.RESULT_OK, null);
} }
@Override @Override
public void onError() { public void onError() {
new MaterialAlertDialogBuilder(callingActivity) new MaterialAlertDialogBuilder(callingActivity)
.setMessage( .setMessage(
callingActivity.getString( callingActivity.getString(
R.string.openkeychain_ssh_api_connect_fail)) R.string.openkeychain_ssh_api_connect_fail))
.show(); .show();
} }
}); });
return null; return null;
} }
@ -273,7 +279,9 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
} }
} }
/** A Jsch identity that delegates key operations via the OpenKeychain SSH API */ /**
* A Jsch identity that delegates key operations via the OpenKeychain SSH API
*/
public static class ApiIdentity implements Identity { public static class ApiIdentity implements Identity {
private String keyId, description, alg; private String keyId, description, alg;
private byte[] publicKey; private byte[] publicKey;
@ -283,12 +291,12 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
private byte[] signature; private byte[] signature;
ApiIdentity( ApiIdentity(
String keyId, String keyId,
String description, String description,
byte[] publicKey, byte[] publicKey,
String alg, String alg,
Activity callingActivity, Activity callingActivity,
SshAuthenticationApi api) { SshAuthenticationApi api) {
this.keyId = keyId; this.keyId = keyId;
this.description = description; this.description = description;
this.publicKey = publicKey; this.publicKey = publicKey;
@ -313,35 +321,35 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
* *
* @param result The result intent to handle * @param result The result intent to handle
* @return The signed challenge, or null if it was not immediately available, in which case * @return The signed challenge, or null if it was not immediately available, in which case
* the latch has been initialized and the pending intent started * the latch has been initialized and the pending intent started
*/ */
private byte[] handleSignResult(Intent result) { private byte[] handleSignResult(Intent result) {
switch (result.getIntExtra(SshAuthenticationApi.EXTRA_RESULT_CODE, -1)) { switch (result.getIntExtra(SshAuthenticationApi.EXTRA_RESULT_CODE, -1)) {
case SshAuthenticationApi.RESULT_CODE_ERROR: case SshAuthenticationApi.RESULT_CODE_ERROR:
SshAuthenticationApiError error = SshAuthenticationApiError error =
result.getParcelableExtra(SshAuthenticationApi.EXTRA_ERROR); result.getParcelableExtra(SshAuthenticationApi.EXTRA_ERROR);
throw new RuntimeException(error.getMessage()); throw new RuntimeException(error.getMessage());
case SshAuthenticationApi.RESULT_CODE_SUCCESS: case SshAuthenticationApi.RESULT_CODE_SUCCESS:
return result.getByteArrayExtra(SshAuthenticationApi.EXTRA_SIGNATURE); return result.getByteArrayExtra(SshAuthenticationApi.EXTRA_SIGNATURE);
case SshAuthenticationApi.RESULT_CODE_USER_INTERACTION_REQUIRED: case SshAuthenticationApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
PendingIntent pendingIntent = PendingIntent pendingIntent =
result.getParcelableExtra(SshAuthenticationApi.EXTRA_PENDING_INTENT); result.getParcelableExtra(SshAuthenticationApi.EXTRA_PENDING_INTENT);
try { try {
latch = new CountDownLatch(1); latch = new CountDownLatch(1);
callingActivity.startIntentSenderForResult( callingActivity.startIntentSenderForResult(
pendingIntent.getIntentSender(), POST_SIGNATURE, null, 0, 0, 0); pendingIntent.getIntentSender(), POST_SIGNATURE, null, 0, 0, 0);
return null; return null;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
throw new RuntimeException( throw new RuntimeException(
callingActivity.getString(R.string.ssh_api_pending_intent_failed)); callingActivity.getString(R.string.ssh_api_pending_intent_failed));
} }
default: default:
if (result.hasExtra(SshAuthenticationApi.EXTRA_CHALLENGE)) if (result.hasExtra(SshAuthenticationApi.EXTRA_CHALLENGE))
return handleSignResult(api.executeApi(result)); return handleSignResult(api.executeApi(result));
throw new RuntimeException( throw new RuntimeException(
callingActivity.getString(R.string.ssh_api_unknown_error)); callingActivity.getString(R.string.ssh_api_unknown_error));
} }
} }
@ -399,6 +407,7 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
} }
@Override @Override
public void clear() {} public void clear() {
}
} }
} }

View file

@ -11,46 +11,46 @@ internal object Phonemes {
private const val NOT_FIRST = 0x0008 private const val NOT_FIRST = 0x0008
private val elements = arrayOf( private val elements = arrayOf(
Element("a", VOWEL), Element("a", VOWEL),
Element("ae", VOWEL or DIPTHONG), Element("ae", VOWEL or DIPTHONG),
Element("ah", VOWEL or DIPTHONG), Element("ah", VOWEL or DIPTHONG),
Element("ai", VOWEL or DIPTHONG), Element("ai", VOWEL or DIPTHONG),
Element("b", CONSONANT), Element("b", CONSONANT),
Element("c", CONSONANT), Element("c", CONSONANT),
Element("ch", CONSONANT or DIPTHONG), Element("ch", CONSONANT or DIPTHONG),
Element("d", CONSONANT), Element("d", CONSONANT),
Element("e", VOWEL), Element("e", VOWEL),
Element("ee", VOWEL or DIPTHONG), Element("ee", VOWEL or DIPTHONG),
Element("ei", VOWEL or DIPTHONG), Element("ei", VOWEL or DIPTHONG),
Element("f", CONSONANT), Element("f", CONSONANT),
Element("g", CONSONANT), Element("g", CONSONANT),
Element("gh", CONSONANT or DIPTHONG or NOT_FIRST), Element("gh", CONSONANT or DIPTHONG or NOT_FIRST),
Element("h", CONSONANT), Element("h", CONSONANT),
Element("i", VOWEL), Element("i", VOWEL),
Element("ie", VOWEL or DIPTHONG), Element("ie", VOWEL or DIPTHONG),
Element("j", CONSONANT), Element("j", CONSONANT),
Element("k", CONSONANT), Element("k", CONSONANT),
Element("l", CONSONANT), Element("l", CONSONANT),
Element("m", CONSONANT), Element("m", CONSONANT),
Element("n", CONSONANT), Element("n", CONSONANT),
Element("ng", CONSONANT or DIPTHONG or NOT_FIRST), Element("ng", CONSONANT or DIPTHONG or NOT_FIRST),
Element("o", VOWEL), Element("o", VOWEL),
Element("oh", VOWEL or DIPTHONG), Element("oh", VOWEL or DIPTHONG),
Element("oo", VOWEL or DIPTHONG), Element("oo", VOWEL or DIPTHONG),
Element("p", CONSONANT), Element("p", CONSONANT),
Element("ph", CONSONANT or DIPTHONG), Element("ph", CONSONANT or DIPTHONG),
Element("qu", CONSONANT or DIPTHONG), Element("qu", CONSONANT or DIPTHONG),
Element("r", CONSONANT), Element("r", CONSONANT),
Element("s", CONSONANT), Element("s", CONSONANT),
Element("sh", CONSONANT or DIPTHONG), Element("sh", CONSONANT or DIPTHONG),
Element("t", CONSONANT), Element("t", CONSONANT),
Element("th", CONSONANT or DIPTHONG), Element("th", CONSONANT or DIPTHONG),
Element("u", VOWEL), Element("u", VOWEL),
Element("v", CONSONANT), Element("v", CONSONANT),
Element("w", CONSONANT), Element("w", CONSONANT),
Element("x", CONSONANT), Element("x", CONSONANT),
Element("y", CONSONANT), Element("y", CONSONANT),
Element("z", CONSONANT) Element("z", CONSONANT)
) )
private val NUM_ELEMENTS = elements.size private val NUM_ELEMENTS = elements.size
@ -110,7 +110,7 @@ internal object Phonemes {
} }
// Don't allow VOWEL followed a Vowel/Dipthong pair // Don't allow VOWEL followed a Vowel/Dipthong pair
if (prev and VOWEL > 0 && flags and VOWEL > 0 && if (prev and VOWEL > 0 && flags and VOWEL > 0 &&
flags and DIPTHONG > 0 flags and DIPTHONG > 0
) { ) {
continue continue
} }
@ -125,7 +125,7 @@ internal object Phonemes {
// Handle UPPERS // Handle UPPERS
if (pwFlags and PasswordGenerator.UPPERS > 0) { if (pwFlags and PasswordGenerator.UPPERS > 0) {
if ((pwFlags and PasswordGenerator.LOWERS == 0) || if ((pwFlags and PasswordGenerator.LOWERS == 0) ||
(first || flags and CONSONANT > 0) && RandomNumberGenerator.number(10) < 2) { (first || flags and CONSONANT > 0) && RandomNumberGenerator.number(10) < 2) {
val index = password.length - length val index = password.length - length
password = password.substring(0, index) + str.toUpperCase() password = password.substring(0, index) + str.toUpperCase()
featureFlags = featureFlags and PasswordGenerator.UPPERS.inv() featureFlags = featureFlags and PasswordGenerator.UPPERS.inv()
@ -169,7 +169,7 @@ internal object Phonemes {
cha = Character.forDigit(RandomNumberGenerator.number(10), 10) cha = Character.forDigit(RandomNumberGenerator.number(10), 10)
character = cha.toString() character = cha.toString()
} while (pwFlags and PasswordGenerator.AMBIGUOUS > 0 && } while (pwFlags and PasswordGenerator.AMBIGUOUS > 0 &&
PasswordGenerator.AMBIGUOUS_STR.contains(character)) PasswordGenerator.AMBIGUOUS_STR.contains(character))
password += character password += character
curSize++ curSize++
@ -192,7 +192,7 @@ internal object Phonemes {
cha = PasswordGenerator.SYMBOLS_STR.toCharArray()[num] cha = PasswordGenerator.SYMBOLS_STR.toCharArray()[num]
character = cha.toString() character = cha.toString()
} while (pwFlags and PasswordGenerator.AMBIGUOUS > 0 && } while (pwFlags and PasswordGenerator.AMBIGUOUS > 0 &&
PasswordGenerator.AMBIGUOUS_STR.contains(character)) PasswordGenerator.AMBIGUOUS_STR.contains(character))
password += character password += character
curSize++ curSize++
@ -205,7 +205,7 @@ internal object Phonemes {
VOWEL VOWEL
} else { } else {
if (prev and VOWEL > 0 || flags and DIPTHONG > 0 || if (prev and VOWEL > 0 || flags and DIPTHONG > 0 ||
RandomNumberGenerator.number(10) > 3 RandomNumberGenerator.number(10) > 3
) { ) {
CONSONANT CONSONANT
} else { } else {

View file

@ -53,7 +53,7 @@ internal object RandomPasswordGenerator {
cha = bank.toCharArray()[num] cha = bank.toCharArray()[num]
character = cha.toString() character = cha.toString()
if (pwFlags and PasswordGenerator.AMBIGUOUS > 0 && if (pwFlags and PasswordGenerator.AMBIGUOUS > 0 &&
PasswordGenerator.AMBIGUOUS_STR.contains(character)) { PasswordGenerator.AMBIGUOUS_STR.contains(character)) {
continue continue
} }
if (pwFlags and PasswordGenerator.NO_VOWELS > 0 && PasswordGenerator.VOWELS_STR.contains(character)) { if (pwFlags and PasswordGenerator.NO_VOWELS > 0 && PasswordGenerator.VOWELS_STR.contains(character)) {

View file

@ -127,7 +127,8 @@ class PasswordBuilder(ctx: Context) {
CapsType.TitleCase -> { CapsType.TitleCase -> {
s = capitalize(s) s = capitalize(s)
} }
CapsType.lowercase, CapsType.As_iS -> {} CapsType.lowercase, CapsType.As_iS -> {
}
} }
} }
password.append(s) password.append(s)

View file

@ -16,9 +16,9 @@ import androidx.core.content.getSystemService
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import org.apache.commons.io.FileUtils
import java.io.File import java.io.File
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import org.apache.commons.io.FileUtils
class ShowSshKeyFragment : DialogFragment() { class ShowSshKeyFragment : DialogFragment() {
@ -41,7 +41,8 @@ class ShowSshKeyFragment : DialogFragment() {
ad.setOnShowListener { ad.setOnShowListener {
val b = ad.getButton(AlertDialog.BUTTON_NEUTRAL) val b = ad.getButton(AlertDialog.BUTTON_NEUTRAL)
b.setOnClickListener { b.setOnClickListener {
val clipboard = activity.getSystemService<ClipboardManager>() ?: return@setOnClickListener val clipboard = activity.getSystemService<ClipboardManager>()
?: return@setOnClickListener
val clip = ClipData.newPlainText("public key", publicKey.text.toString()) val clip = ClipData.newPlainText("public key", publicKey.text.toString())
clipboard.setPrimaryClip(clip) clipboard.setPrimaryClip(clip)
} }

View file

@ -15,9 +15,9 @@ class SshKeyGenActivity : AppCompatActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
if (savedInstanceState == null) { if (savedInstanceState == null) {
supportFragmentManager supportFragmentManager
.beginTransaction() .beginTransaction()
.replace(android.R.id.content, SshKeyGenFragment()) .replace(android.R.id.content, SshKeyGenFragment())
.commit() .commit()
} }
} }

View file

@ -19,11 +19,11 @@ import com.jcraft.jsch.JSch
import com.jcraft.jsch.KeyPair import com.jcraft.jsch.KeyPair
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.databinding.FragmentSshKeygenBinding import com.zeapo.pwdstore.databinding.FragmentSshKeygenBinding
import java.io.File
import java.io.FileOutputStream
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileOutputStream
class SshKeyGenFragment : Fragment() { class SshKeyGenFragment : Fragment() {
@ -96,12 +96,12 @@ class SshKeyGenFragment : Fragment() {
prefs.edit { putBoolean("use_generated_key", true) } prefs.edit { putBoolean("use_generated_key", true) }
} else { } else {
MaterialAlertDialogBuilder(activity) MaterialAlertDialogBuilder(activity)
.setTitle(activity.getString(R.string.error_generate_ssh_key)) .setTitle(activity.getString(R.string.error_generate_ssh_key))
.setMessage(activity.getString(R.string.ssh_key_error_dialog_text) + e.message) .setMessage(activity.getString(R.string.ssh_key_error_dialog_text) + e.message)
.setPositiveButton(activity.getString(R.string.dialog_ok)) { _, _ -> .setPositiveButton(activity.getString(R.string.dialog_ok)) { _, _ ->
requireActivity().finish() requireActivity().finish()
} }
.show() .show()
} }
hideKeyboard() hideKeyboard()
} }

View file

@ -80,6 +80,7 @@ open class PasswordItemRecyclerAdapter :
class PasswordItemDetailsLookup(private val recyclerView: RecyclerView) : class PasswordItemDetailsLookup(private val recyclerView: RecyclerView) :
ItemDetailsLookup<String>() { ItemDetailsLookup<String>() {
override fun getItemDetails(event: MotionEvent): ItemDetails<String>? { override fun getItemDetails(event: MotionEvent): ItemDetails<String>? {
val view = recyclerView.findChildViewUnder(event.x, event.y) ?: return null val view = recyclerView.findChildViewUnder(event.x, event.y) ?: return null
return (recyclerView.getChildViewHolder(view) as PasswordItemViewHolder).itemDetails return (recyclerView.getChildViewHolder(view) as PasswordItemViewHolder).itemDetails

View file

@ -23,6 +23,7 @@ import com.zeapo.pwdstore.pwgen.PasswordGenerator.setPrefs
/** A placeholder fragment containing a simple view. */ /** A placeholder fragment containing a simple view. */
class PasswordGeneratorDialogFragment : DialogFragment() { class PasswordGeneratorDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = MaterialAlertDialogBuilder(requireContext()) val builder = MaterialAlertDialogBuilder(requireContext())
val callingActivity = requireActivity() val callingActivity = requireActivity()
@ -31,7 +32,7 @@ class PasswordGeneratorDialogFragment : DialogFragment() {
val monoTypeface = Typeface.createFromAsset(callingActivity.assets, "fonts/sourcecodepro.ttf") val monoTypeface = Typeface.createFromAsset(callingActivity.assets, "fonts/sourcecodepro.ttf")
builder.setView(view) builder.setView(view)
val prefs = requireActivity().applicationContext val prefs = requireActivity().applicationContext
.getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE) .getSharedPreferences("PasswordGenerator", Context.MODE_PRIVATE)
view.findViewById<CheckBox>(R.id.numerals)?.isChecked = !prefs.getBoolean("0", false) view.findViewById<CheckBox>(R.id.numerals)?.isChecked = !prefs.getBoolean("0", false)
view.findViewById<CheckBox>(R.id.symbols)?.isChecked = prefs.getBoolean("y", false) view.findViewById<CheckBox>(R.id.symbols)?.isChecked = prefs.getBoolean("y", false)

View file

@ -117,13 +117,13 @@ class XkPasswordGeneratorDialogFragment : DialogFragment() {
private fun makeAndSetPassword(passwordText: AppCompatTextView) { private fun makeAndSetPassword(passwordText: AppCompatTextView) {
try { try {
passwordText.text = PasswordBuilder(requireContext()) passwordText.text = PasswordBuilder(requireContext())
.setNumberOfWords(Integer.valueOf(editNumWords.text.toString())) .setNumberOfWords(Integer.valueOf(editNumWords.text.toString()))
.setMinimumWordLength(DEFAULT_MIN_WORD_LENGTH) .setMinimumWordLength(DEFAULT_MIN_WORD_LENGTH)
.setMaximumWordLength(DEFAULT_MAX_WORD_LENGTH) .setMaximumWordLength(DEFAULT_MAX_WORD_LENGTH)
.setSeparator(editSeparator.text.toString()) .setSeparator(editSeparator.text.toString())
.appendNumbers(if (cbNumbers.isChecked) Integer.parseInt(spinnerNumbersCount.selectedItem as String) else 0) .appendNumbers(if (cbNumbers.isChecked) Integer.parseInt(spinnerNumbersCount.selectedItem as String) else 0)
.appendSymbols(if (cbSymbols.isChecked) Integer.parseInt(spinnerSymbolsCount.selectedItem as String) else 0) .appendSymbols(if (cbSymbols.isChecked) Integer.parseInt(spinnerSymbolsCount.selectedItem as String) else 0)
.setCapitalization(CapsType.valueOf(spinnerCapsType.selectedItem.toString())).create() .setCapitalization(CapsType.valueOf(spinnerCapsType.selectedItem.toString())).create()
} catch (e: PasswordGenerator.PasswordGeneratorExeption) { } catch (e: PasswordGenerator.PasswordGeneratorExeption) {
Toast.makeText(requireActivity(), e.message, Toast.LENGTH_SHORT).show() Toast.makeText(requireActivity(), e.message, Toast.LENGTH_SHORT).show()
tag("xkpw").e(e, "failure generating xkpasswd") tag("xkpw").e(e, "failure generating xkpasswd")

View file

@ -61,11 +61,11 @@ object BiometricAuthenticator {
} }
val biometricPrompt = BiometricPrompt(activity, { handler.post(it) }, authCallback) val biometricPrompt = BiometricPrompt(activity, { handler.post(it) }, authCallback)
val promptInfo = BiometricPrompt.PromptInfo.Builder() val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle(activity.getString(dialogTitleRes)) .setTitle(activity.getString(dialogTitleRes))
.setDeviceCredentialAllowed(true) .setDeviceCredentialAllowed(true)
.build() .build()
if (BiometricManager.from(activity).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS || if (BiometricManager.from(activity).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS ||
activity.getSystemService<KeyguardManager>()?.isDeviceSecure == true) { activity.getSystemService<KeyguardManager>()?.isDeviceSecure == true) {
biometricPrompt.authenticate(promptInfo) biometricPrompt.authenticate(promptInfo)
} else { } else {
callback(Result.HardwareUnavailableOrDisabled) callback(Result.HardwareUnavailableOrDisabled)

View file

@ -36,11 +36,11 @@ fun Context.getEncryptedPrefs(fileName: String): SharedPreferences {
val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec) val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
return EncryptedSharedPreferences.create( return EncryptedSharedPreferences.create(
fileName, fileName,
masterKeyAlias, masterKeyAlias,
this, this,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
) )
} }
@ -55,7 +55,7 @@ fun <T : View> AlertDialog.requestInputFocusOnView(@IdRes id: Int) {
setOnFocusChangeListener { v, _ -> setOnFocusChangeListener { v, _ ->
v.post { v.post {
context.getSystemService<InputMethodManager>() context.getSystemService<InputMethodManager>()
?.showSoftInput(v, InputMethodManager.SHOW_IMPLICIT) ?.showSoftInput(v, InputMethodManager.SHOW_IMPLICIT)
} }
} }
requestFocus() requestFocus()

View file

@ -4,24 +4,28 @@
*/ */
package com.zeapo.pwdstore.utils; package com.zeapo.pwdstore.utils;
import org.apache.commons.codec.binary.Base32;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Arrays; import java.util.Arrays;
import javax.crypto.Mac; import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base32;
import timber.log.Timber; import timber.log.Timber;
public class Otp { public class Otp {
private static final Base32 BASE_32 = new Base32(); private static final Base32 BASE_32 = new Base32();
private Otp() {} private Otp() {
}
public static String calculateCode( public static String calculateCode(
String secret, long counter, String algorithm, String digits) { String secret, long counter, String algorithm, String digits) {
String[] steam = { String[] steam = {
"2", "3", "4", "5", "6", "7", "8", "9", "B", "C", "D", "F", "G", "H", "J", "K", "M", "2", "3", "4", "5", "6", "7", "8", "9", "B", "C", "D", "F", "G", "H", "J", "K", "M",
"N", "P", "Q", "R", "T", "V", "W", "X", "Y" "N", "P", "Q", "R", "T", "V", "W", "X", "Y"

View file

@ -14,14 +14,15 @@ data class PasswordItem(
val file: File, val file: File,
val rootDir: File val rootDir: File
) : Comparable<PasswordItem> { ) : Comparable<PasswordItem> {
val fullPathToParent = file.absolutePath val fullPathToParent = file.absolutePath
.replace(rootDir.absolutePath, "") .replace(rootDir.absolutePath, "")
.replace(file.name, "") .replace(file.name, "")
val longName = PgpActivity.getLongName( val longName = PgpActivity.getLongName(
fullPathToParent, fullPathToParent,
rootDir.absolutePath, rootDir.absolutePath,
toString()) toString())
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
return (other is PasswordItem) && (other.file == file) return (other is PasswordItem) && (other.file == file)

View file

@ -8,9 +8,6 @@ import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.core.content.edit import androidx.core.content.edit
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import java.io.File
import java.io.FileFilter
import java.util.Comparator
import org.apache.commons.io.filefilter.FileFilterUtils import org.apache.commons.io.filefilter.FileFilterUtils
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.Repository import org.eclipse.jgit.lib.Repository
@ -18,6 +15,9 @@ import org.eclipse.jgit.storage.file.FileRepositoryBuilder
import org.eclipse.jgit.transport.RefSpec import org.eclipse.jgit.transport.RefSpec
import org.eclipse.jgit.transport.RemoteConfig import org.eclipse.jgit.transport.RemoteConfig
import org.eclipse.jgit.transport.URIish import org.eclipse.jgit.transport.URIish
import java.io.File
import java.io.FileFilter
import java.util.Comparator
open class PasswordRepository protected constructor() { open class PasswordRepository protected constructor() {
@ -26,7 +26,7 @@ open class PasswordRepository protected constructor() {
FOLDER_FIRST(Comparator { p1: PasswordItem, p2: PasswordItem -> FOLDER_FIRST(Comparator { p1: PasswordItem, p2: PasswordItem ->
(p1.type + p1.name) (p1.type + p1.name)
.compareTo(p2.type + p2.name, ignoreCase = true) .compareTo(p2.type + p2.name, ignoreCase = true)
}), }),
INDEPENDENT(Comparator { p1: PasswordItem, p2: PasswordItem -> INDEPENDENT(Comparator { p1: PasswordItem, p2: PasswordItem ->
@ -62,8 +62,8 @@ open class PasswordRepository protected constructor() {
val builder = FileRepositoryBuilder() val builder = FileRepositoryBuilder()
try { try {
repository = builder.setGitDir(localDir) repository = builder.setGitDir(localDir)
.readEnvironment() .readEnvironment()
.build() .build()
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
return null return null
@ -81,8 +81,8 @@ open class PasswordRepository protected constructor() {
if (repository != null) { if (repository != null) {
// Check if remote exists // Check if remote exists
return repository!!.config.getSubsections("remote").isNotEmpty() && return repository!!.config.getSubsections("remote").isNotEmpty() &&
repository!!.objectDatabase.exists() && repository!!.objectDatabase.exists() &&
repository!!.allRefs.isNotEmpty() repository!!.allRefs.isNotEmpty()
} }
return false return false
} }
@ -196,9 +196,9 @@ open class PasswordRepository protected constructor() {
if (path == null || !path.exists()) return ArrayList() if (path == null || !path.exists()) return ArrayList()
val directories = (path.listFiles(FileFilterUtils.directoryFileFilter() as FileFilter) val directories = (path.listFiles(FileFilterUtils.directoryFileFilter() as FileFilter)
?: emptyArray()).toList() ?: emptyArray()).toList()
val files = (path.listFiles(FileFilterUtils.suffixFileFilter(".gpg") as FileFilter) val files = (path.listFiles(FileFilterUtils.suffixFileFilter(".gpg") as FileFilter)
?: emptyArray()).toList() ?: emptyArray()).toList()
val items = ArrayList<File>() val items = ArrayList<File>()
items.addAll(directories) items.addAll(directories)

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" > <set xmlns:android="http://schemas.android.com/apk/res/android">
<scale <scale
android:duration="300" android:duration="300"
android:fromXScale="1.0" android:fromXScale="1.0"

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" > <set xmlns:android="http://schemas.android.com/apk/res/android">
<scale <scale
android:duration="300" android:duration="300"
android:fromXScale="0" android:fromXScale="0"

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="false" <item android:color="#00FFFFFF" android:state_checked="false" />
android:color="#00FFFFFF" />
<item android:color="@color/button_color" /> <item android:color="@color/button_color" />
</selector> </selector>

View file

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M20,18H4V8H20M20,6H12L10,4H4C2.89,4 2,4.89 2,6V18A2,2 0,0 0,4 20H20A2,2 0,0 0,22 18V8C22,6.89 21.1,6 20,6Z"/> android:pathData="M20,18H4V8H20M20,6H12L10,4H4C2.89,4 2,4.89 2,6V18A2,2 0,0 0,4 20H20A2,2 0,0 0,22 18V8C22,6.89 21.1,6 20,6Z" />
</vector> </vector>

View file

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M14,2H6A2,2 0,0 0,4 4V20A2,2 0,0 0,6 22H18A2,2 0,0 0,20 20V8L14,2M18,20H6V4H13V9H18M12.83,15A3,3 0,1 0,12.83 17H14V19H16V17H17V15M10,17A1,1 0,1 1,11 16A1,1 0,0 1,10 17Z"/> android:pathData="M14,2H6A2,2 0,0 0,4 4V20A2,2 0,0 0,6 22H18A2,2 0,0 0,20 20V8L14,2M18,20H6V4H13V9H18M12.83,15A3,3 0,1 0,12.83 17H14V19H16V17H17V15M10,17A1,1 0,1 1,11 16A1,1 0,0 1,10 17Z" />
</vector> </vector>

View file

@ -1,10 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:tint="?attr/colorOnPrimary"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24" android:viewportHeight="24">
android:tint="?attr/colorOnPrimary"> <path
<path android:fillColor="#FFFFFFFF"
android:fillColor="#FFFFFFFF" android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM9,6c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2L9,8L9,6zM18,20L6,20L6,10h12v10zM12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z" />
android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM9,6c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2L9,8L9,6zM18,20L6,20L6,10h12v10zM12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z"/>
</vector> </vector>

View file

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/> android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
</vector> </vector>

View file

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#FF000000" android:fillColor="#FF000000"
android:pathData="M14,2H6A2,2 0,0 0,4 4V20A2,2 0,0 0,6 22H18A2,2 0,0 0,20 20V8L14,2M18,20H6V4H13V9H18M12.83,15A3,3 0,1 0,12.83 17H14V19H16V17H17V15M10,17A1,1 0,1 1,11 16A1,1 0,0 1,10 17Z"/> android:pathData="M14,2H6A2,2 0,0 0,4 4V20A2,2 0,0 0,6 22H18A2,2 0,0 0,20 20V8L14,2M18,20H6V4H13V9H18M12.83,15A3,3 0,1 0,12.83 17H14V19H16V17H17V15M10,17A1,1 0,1 1,11 16A1,1 0,0 1,10 17Z" />
</vector> </vector>

View file

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM15.59,7L12,10.59 8.41,7 7,8.41 10.59,12 7,15.59 8.41,17 12,13.41 15.59,17 17,15.59 13.41,12 17,8.41z"/> android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM15.59,7L12,10.59 8.41,7 7,8.41 10.59,12 7,15.59 8.41,17 12,13.41 15.59,17 17,15.59 13.41,12 17,8.41z" />
</vector> </vector>

View file

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#FF000000" android:fillColor="#FF000000"
android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM15,5L8,5c-1.1,0 -1.99,0.9 -1.99,2L6,21c0,1.1 0.89,2 1.99,2L19,23c1.1,0 2,-0.9 2,-2L21,11l-6,-6zM8,21L8,7h6v5h5v9L8,21z"/> android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM15,5L8,5c-1.1,0 -1.99,0.9 -1.99,2L6,21c0,1.1 0.89,2 1.99,2L19,23c1.1,0 2,-0.9 2,-2L21,11l-6,-6zM8,21L8,7h6v5h5v9L8,21z" />
</vector> </vector>

View file

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM15,5L8,5c-1.1,0 -1.99,0.9 -1.99,2L6,21c0,1.1 0.89,2 1.99,2L19,23c1.1,0 2,-0.9 2,-2L21,11l-6,-6zM8,21L8,7h6v5h5v9L8,21z"/> android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM15,5L8,5c-1.1,0 -1.99,0.9 -1.99,2L6,21c0,1.1 0.89,2 1.99,2L19,23c1.1,0 2,-0.9 2,-2L21,11l-6,-6zM8,21L8,7h6v5h5v9L8,21z" />
</vector> </vector>

View file

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M16,9v10H8V9h8m-1.5,-6h-5l-1,1H5v2h14V4h-3.5l-1,-1zM18,7H6v12c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7z"/> android:pathData="M16,9v10H8V9h8m-1.5,-6h-5l-1,1H5v2h14V4h-3.5l-1,-1zM18,7H6v12c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7z" />
</vector> </vector>

View file

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/> android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z" />
</vector> </vector>

View file

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M14.06,9.02l0.92,0.92L5.92,19L5,19v-0.92l9.06,-9.06M17.66,3c-0.25,0 -0.51,0.1 -0.7,0.29l-1.83,1.83 3.75,3.75 1.83,-1.83c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.2,-0.2 -0.45,-0.29 -0.71,-0.29zM14.06,6.19L3,17.25L3,21h3.75L17.81,9.94l-3.75,-3.75z"/> android:pathData="M14.06,9.02l0.92,0.92L5.92,19L5,19v-0.92l9.06,-9.06M17.66,3c-0.25,0 -0.51,0.1 -0.7,0.29l-1.83,1.83 3.75,3.75 1.83,-1.83c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.2,-0.2 -0.45,-0.29 -0.71,-0.29zM14.06,6.19L3,17.25L3,21h3.75L17.81,9.94l-3.75,-3.75z" />
</vector> </vector>

View file

@ -1,10 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:tint="?attr/colorOnPrimary"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24" android:viewportHeight="24">
android:tint="?attr/colorOnPrimary"> <path
<path android:fillColor="#FFFFFFFF"
android:fillColor="#FFFFFFFF" android:pathData="M8.59,16.59L13.17,12 8.59,7.41 10,6l6,6 -6,6 -1.41,-1.41z" />
android:pathData="M8.59,16.59L13.17,12 8.59,7.41 10,6l6,6 -6,6 -1.41,-1.41z"/>
</vector> </vector>

View file

@ -3,23 +3,24 @@
android:height="108dp" android:height="108dp"
android:viewportWidth="110.34687" android:viewportWidth="110.34687"
android:viewportHeight="110.34687"> android:viewportHeight="110.34687">
<group android:translateX="24.828047" <group
android:translateY="24.828047"> android:translateX="24.828047"
<path android:translateY="24.828047">
android:pathData="m18.8,30.2129v-11.546c0,-6.4144 5.1315,-11.546 11.546,-11.546 6.4144,0 11.546,5.1315 11.546,11.546v11.546" <path
android:strokeWidth="5.349" android:fillColor="#00000000"
android:fillColor="#00000000" android:pathData="m18.8,30.2129v-11.546c0,-6.4144 5.1315,-11.546 11.546,-11.546 6.4144,0 11.546,5.1315 11.546,11.546v11.546"
android:strokeColor="#013e5b"/> android:strokeWidth="5.349"
<path android:strokeColor="#013e5b" />
android:pathData="M15.4099,21.8429L45.2811,21.8429A2.2639,2.2639 0,0 1,47.545 24.1068L47.545,53.977A2.2639,2.2639 0,0 1,45.2811 56.2409L15.4099,56.2409A2.2639,2.2639 0,0 1,13.146 53.977L13.146,24.1068A2.2639,2.2639 0,0 1,15.4099 21.8429z" <path
android:fillColor="#00c7a0"/> android:fillColor="#00c7a0"
<path android:pathData="M15.4099,21.8429L45.2811,21.8429A2.2639,2.2639 0,0 1,47.545 24.1068L47.545,53.977A2.2639,2.2639 0,0 1,45.2811 56.2409L15.4099,56.2409A2.2639,2.2639 0,0 1,13.146 53.977L13.146,24.1068A2.2639,2.2639 0,0 1,15.4099 21.8429z" />
android:pathData="m44.8267,37.6961 l-13.1408,-13.1393c-0.7569,-0.7566 -1.9838,-0.7566 -2.7408,0l-13.08,13.0785c-0.7567,0.7573 -0.7567,1.9846 0,2.7419l13.1415,13.14c0.7572,0.7567 1.9842,0.7567 2.7414,0l13.0778,-13.0785c0.7572,-0.7572 0.7572,-1.9849 0,-2.7421" <path
android:strokeWidth=".35344" android:fillColor="#fff"
android:fillColor="#fff"/> android:pathData="m44.8267,37.6961 l-13.1408,-13.1393c-0.7569,-0.7566 -1.9838,-0.7566 -2.7408,0l-13.08,13.0785c-0.7567,0.7573 -0.7567,1.9846 0,2.7419l13.1415,13.14c0.7572,0.7567 1.9842,0.7567 2.7414,0l13.0778,-13.0785c0.7572,-0.7572 0.7572,-1.9849 0,-2.7421"
<path android:strokeWidth=".35344" />
android:pathData="m30.3156,23.9881c-0.496,0 -0.992,0.1893 -1.3705,0.5676l-2.7282,2.7288 3.4612,3.4606c0.8044,-0.2715 1.727,-0.0892 2.368,0.5517 0.6237,0.624 0.8361,1.5493 0.5471,2.3828l3.3357,3.3357c0.8076,-0.2777 1.738,-0.098 2.3828,0.5476 0.9008,0.9005 0.9008,2.361 0,3.2615 -1.7823,1.7848 -4.7253,-0.1767 -3.7641,-2.5087l-3.1111,-3.1106c-2.2315,0.5285 -3.8934,-1.2655 -3.149,-3.1674l-0.6863,-0.6863l0,15.9165l5.4913,0l0,-5.8608c-0.0315,-0.7566 1.1201,-0.7566 1.0886,0l0,6.4043c0.0005,0.3013 -0.2438,0.5457 -0.545,0.5455l-6.5804,0c-0.3015,0.0005 -0.546,-0.2441 -0.5456,-0.5455l0,-17.4333c-0.0005,-0.0363 0.0029,-0.0728 0.0097,-0.1085l-1.6444,-1.6444 -9.0106,9.0084c-0.7567,0.7573 -0.7567,1.9848 0,2.7421l13.1415,13.14c0.7572,0.7567 1.9844,0.7567 2.7416,0l13.0778,-13.0785c0.7572,-0.7572 0.7572,-1.9849 0,-2.7421l-13.14,-13.1393c-0.3785,-0.3783 -0.8746,-0.5676 -1.3705,-0.5676zM29.9512,39.1825c0.1001,0 0.1808,0.0381 0.2426,0.1146 0.0648,0.0736 0.1326,0.1975 0.2032,0.371 0.0705,0.1706 0.1089,0.2615 0.1146,0.2733 0.0059,-0.0119 0.0424,-0.1026 0.11,-0.2733 0.0707,-0.1705 0.1401,-0.2946 0.2078,-0.371 0.0677,-0.0765 0.1513,-0.1146 0.2513,-0.1146 0.1412,0 0.2646,0.047 0.3705,0.1412 0.1059,0.0941 0.1592,0.2103 0.1592,0.3485 0,0.0676 -0.0179,0.1368 -0.0532,0.2073 -0.0323,0.0707 -0.0777,0.1444 -0.1366,0.2211 -0.056,0.0734 -0.1164,0.1571 -0.1812,0.2513 0.0648,-0.0089 0.1589,-0.0251 0.2825,-0.0486 0.1265,-0.0236 0.2268,-0.0354 0.3004,-0.0354 0.0969,0 0.1762,0.0224 0.238,0.0666 0.0648,0.0442 0.1118,0.1042 0.1413,0.1807 0.0294,0.0734 0.044,0.1544 0.044,0.2426 0,0.1384 -0.0337,0.2544 -0.1013,0.3485 -0.0677,0.0912 -0.1749,0.1372 -0.3219,0.1372 -0.0472,0 -0.14,-0.0119 -0.2784,-0.0354 -0.1382,-0.0236 -0.2397,-0.0386 -0.3045,-0.0446 0.1531,0.2177 0.2534,0.3652 0.3004,0.4417 0.047,0.0736 0.0706,0.1544 0.0706,0.2426 0,0.1412 -0.0533,0.2556 -0.1592,0.3439 -0.1028,0.0853 -0.2264,0.1279 -0.3705,0.1279 -0.1028,0 -0.1882,-0.0366 -0.2559,-0.11 -0.0646,-0.0767 -0.1309,-0.1955 -0.1986,-0.3572 -0.0646,-0.1647 -0.1028,-0.259 -0.1146,-0.2825 -0.0117,0.0235 -0.0516,0.1178 -0.1192,0.2825 -0.0648,0.1646 -0.1281,0.2836 -0.1899,0.3572 -0.0617,0.0735 -0.1454,0.11 -0.2513,0.11 -0.15,0 -0.2779,-0.0427 -0.3838,-0.1279 -0.1059,-0.0883 -0.1587,-0.2027 -0.1587,-0.3439 0,-0.0618 0.0156,-0.1263 0.0481,-0.194 0.0323,-0.0707 0.0665,-0.1311 0.1018,-0.1812 0.0382,-0.0499 0.1118,-0.1532 0.2206,-0.3091 -0.0736,0.0059 -0.1751,0.021 -0.3045,0.0446 -0.1294,0.0236 -0.2176,0.0354 -0.2646,0.0354 -0.1472,0 -0.2577,-0.046 -0.3311,-0.1372 -0.0707,-0.0941 -0.1059,-0.2101 -0.1059,-0.3485 0,-0.1411 0.0352,-0.2573 0.1059,-0.3485 0.0734,-0.0942 0.184,-0.1412 0.3311,-0.1412 0.0736,0 0.1628,0.0107 0.2687,0.0312 0.1059,0.0206 0.2062,0.038 0.3004,0.0527 -0.0824,-0.1177 -0.1648,-0.2366 -0.2472,-0.3572 -0.0824,-0.1206 -0.1233,-0.2282 -0.1233,-0.3224 0,-0.1382 0.0543,-0.2544 0.1633,-0.3485 0.1088,-0.0942 0.2351,-0.1412 0.3792,-0.1412zM29.9512,43.2235c0.1001,0 0.1808,0.0383 0.2426,0.1146 0.0648,0.0735 0.1326,0.197 0.2032,0.3705 0.0705,0.1707 0.1089,0.262 0.1146,0.2738 0.0059,-0.0119 0.0424,-0.1031 0.11,-0.2738 0.0707,-0.1705 0.1401,-0.2941 0.2078,-0.3705 0.0677,-0.0765 0.1513,-0.1146 0.2513,-0.1146 0.1412,0 0.2646,0.047 0.3705,0.1412 0.1059,0.0941 0.1592,0.2102 0.1592,0.3485 0,0.0675 -0.0179,0.1366 -0.0532,0.2073 -0.0323,0.0705 -0.0777,0.1441 -0.1366,0.2206 -0.056,0.0735 -0.1164,0.1576 -0.1812,0.2518 0.0648,-0.0089 0.1589,-0.0251 0.2825,-0.0486 0.1265,-0.0236 0.2268,-0.0353 0.3004,-0.0353 0.0969,0 0.1762,0.0219 0.238,0.0661 0.0648,0.0442 0.1118,0.1047 0.1413,0.1812 0.0294,0.0736 0.044,0.1542 0.044,0.2426 0,0.1382 -0.0337,0.2544 -0.1013,0.3485 -0.0677,0.0912 -0.1749,0.1366 -0.3219,0.1366 -0.0472,0 -0.14,-0.0119 -0.2784,-0.0354 -0.1382,-0.0236 -0.2397,-0.038 -0.3045,-0.044 0.1531,0.2177 0.2534,0.3647 0.3004,0.4411 0.047,0.0736 0.0706,0.1549 0.0706,0.2431 0,0.1411 -0.0533,0.2558 -0.1592,0.3439 -0.1028,0.0854 -0.2264,0.1279 -0.3705,0.1279 -0.1028,0 -0.1882,-0.0371 -0.2559,-0.1105 -0.0646,-0.0765 -0.1309,-0.1955 -0.1986,-0.3572 -0.0646,-0.1647 -0.1028,-0.259 -0.1146,-0.2825 -0.0117,0.0236 -0.0516,0.1178 -0.1192,0.2825 -0.0648,0.1648 -0.1281,0.2836 -0.1899,0.3572 -0.0617,0.0735 -0.1454,0.1105 -0.2513,0.1105 -0.15,0 -0.2779,-0.0425 -0.3838,-0.1279 -0.1059,-0.0881 -0.1587,-0.2028 -0.1587,-0.3439 0,-0.0617 0.0156,-0.1268 0.0481,-0.1945 0.0323,-0.0705 0.0665,-0.1311 0.1018,-0.1812 0.0382,-0.0499 0.1118,-0.1527 0.2206,-0.3086 -0.0736,0.0059 -0.1751,0.0205 -0.3045,0.044 -0.1294,0.0236 -0.2176,0.0354 -0.2646,0.0354 -0.1472,0 -0.2577,-0.0454 -0.3311,-0.1366 -0.0707,-0.0941 -0.1059,-0.2103 -0.1059,-0.3485 0,-0.1412 0.0352,-0.2573 0.1059,-0.3485 0.0734,-0.0942 0.184,-0.1412 0.3311,-0.1412 0.0736,0 0.1628,0.0101 0.2687,0.0307 0.1059,0.0204 0.2062,0.0384 0.3004,0.0532 -0.0824,-0.1177 -0.1648,-0.2371 -0.2472,-0.3577 -0.0824,-0.1206 -0.1233,-0.2278 -0.1233,-0.3219 0,-0.1384 0.0543,-0.2544 0.1633,-0.3485 0.1088,-0.0942 0.2351,-0.1412 0.3792,-0.1412z" <path
android:strokeWidth="1.3358" android:fillColor="#3bdbbc"
android:fillColor="#3bdbbc"/> android:pathData="m30.3156,23.9881c-0.496,0 -0.992,0.1893 -1.3705,0.5676l-2.7282,2.7288 3.4612,3.4606c0.8044,-0.2715 1.727,-0.0892 2.368,0.5517 0.6237,0.624 0.8361,1.5493 0.5471,2.3828l3.3357,3.3357c0.8076,-0.2777 1.738,-0.098 2.3828,0.5476 0.9008,0.9005 0.9008,2.361 0,3.2615 -1.7823,1.7848 -4.7253,-0.1767 -3.7641,-2.5087l-3.1111,-3.1106c-2.2315,0.5285 -3.8934,-1.2655 -3.149,-3.1674l-0.6863,-0.6863l0,15.9165l5.4913,0l0,-5.8608c-0.0315,-0.7566 1.1201,-0.7566 1.0886,0l0,6.4043c0.0005,0.3013 -0.2438,0.5457 -0.545,0.5455l-6.5804,0c-0.3015,0.0005 -0.546,-0.2441 -0.5456,-0.5455l0,-17.4333c-0.0005,-0.0363 0.0029,-0.0728 0.0097,-0.1085l-1.6444,-1.6444 -9.0106,9.0084c-0.7567,0.7573 -0.7567,1.9848 0,2.7421l13.1415,13.14c0.7572,0.7567 1.9844,0.7567 2.7416,0l13.0778,-13.0785c0.7572,-0.7572 0.7572,-1.9849 0,-2.7421l-13.14,-13.1393c-0.3785,-0.3783 -0.8746,-0.5676 -1.3705,-0.5676zM29.9512,39.1825c0.1001,0 0.1808,0.0381 0.2426,0.1146 0.0648,0.0736 0.1326,0.1975 0.2032,0.371 0.0705,0.1706 0.1089,0.2615 0.1146,0.2733 0.0059,-0.0119 0.0424,-0.1026 0.11,-0.2733 0.0707,-0.1705 0.1401,-0.2946 0.2078,-0.371 0.0677,-0.0765 0.1513,-0.1146 0.2513,-0.1146 0.1412,0 0.2646,0.047 0.3705,0.1412 0.1059,0.0941 0.1592,0.2103 0.1592,0.3485 0,0.0676 -0.0179,0.1368 -0.0532,0.2073 -0.0323,0.0707 -0.0777,0.1444 -0.1366,0.2211 -0.056,0.0734 -0.1164,0.1571 -0.1812,0.2513 0.0648,-0.0089 0.1589,-0.0251 0.2825,-0.0486 0.1265,-0.0236 0.2268,-0.0354 0.3004,-0.0354 0.0969,0 0.1762,0.0224 0.238,0.0666 0.0648,0.0442 0.1118,0.1042 0.1413,0.1807 0.0294,0.0734 0.044,0.1544 0.044,0.2426 0,0.1384 -0.0337,0.2544 -0.1013,0.3485 -0.0677,0.0912 -0.1749,0.1372 -0.3219,0.1372 -0.0472,0 -0.14,-0.0119 -0.2784,-0.0354 -0.1382,-0.0236 -0.2397,-0.0386 -0.3045,-0.0446 0.1531,0.2177 0.2534,0.3652 0.3004,0.4417 0.047,0.0736 0.0706,0.1544 0.0706,0.2426 0,0.1412 -0.0533,0.2556 -0.1592,0.3439 -0.1028,0.0853 -0.2264,0.1279 -0.3705,0.1279 -0.1028,0 -0.1882,-0.0366 -0.2559,-0.11 -0.0646,-0.0767 -0.1309,-0.1955 -0.1986,-0.3572 -0.0646,-0.1647 -0.1028,-0.259 -0.1146,-0.2825 -0.0117,0.0235 -0.0516,0.1178 -0.1192,0.2825 -0.0648,0.1646 -0.1281,0.2836 -0.1899,0.3572 -0.0617,0.0735 -0.1454,0.11 -0.2513,0.11 -0.15,0 -0.2779,-0.0427 -0.3838,-0.1279 -0.1059,-0.0883 -0.1587,-0.2027 -0.1587,-0.3439 0,-0.0618 0.0156,-0.1263 0.0481,-0.194 0.0323,-0.0707 0.0665,-0.1311 0.1018,-0.1812 0.0382,-0.0499 0.1118,-0.1532 0.2206,-0.3091 -0.0736,0.0059 -0.1751,0.021 -0.3045,0.0446 -0.1294,0.0236 -0.2176,0.0354 -0.2646,0.0354 -0.1472,0 -0.2577,-0.046 -0.3311,-0.1372 -0.0707,-0.0941 -0.1059,-0.2101 -0.1059,-0.3485 0,-0.1411 0.0352,-0.2573 0.1059,-0.3485 0.0734,-0.0942 0.184,-0.1412 0.3311,-0.1412 0.0736,0 0.1628,0.0107 0.2687,0.0312 0.1059,0.0206 0.2062,0.038 0.3004,0.0527 -0.0824,-0.1177 -0.1648,-0.2366 -0.2472,-0.3572 -0.0824,-0.1206 -0.1233,-0.2282 -0.1233,-0.3224 0,-0.1382 0.0543,-0.2544 0.1633,-0.3485 0.1088,-0.0942 0.2351,-0.1412 0.3792,-0.1412zM29.9512,43.2235c0.1001,0 0.1808,0.0383 0.2426,0.1146 0.0648,0.0735 0.1326,0.197 0.2032,0.3705 0.0705,0.1707 0.1089,0.262 0.1146,0.2738 0.0059,-0.0119 0.0424,-0.1031 0.11,-0.2738 0.0707,-0.1705 0.1401,-0.2941 0.2078,-0.3705 0.0677,-0.0765 0.1513,-0.1146 0.2513,-0.1146 0.1412,0 0.2646,0.047 0.3705,0.1412 0.1059,0.0941 0.1592,0.2102 0.1592,0.3485 0,0.0675 -0.0179,0.1366 -0.0532,0.2073 -0.0323,0.0705 -0.0777,0.1441 -0.1366,0.2206 -0.056,0.0735 -0.1164,0.1576 -0.1812,0.2518 0.0648,-0.0089 0.1589,-0.0251 0.2825,-0.0486 0.1265,-0.0236 0.2268,-0.0353 0.3004,-0.0353 0.0969,0 0.1762,0.0219 0.238,0.0661 0.0648,0.0442 0.1118,0.1047 0.1413,0.1812 0.0294,0.0736 0.044,0.1542 0.044,0.2426 0,0.1382 -0.0337,0.2544 -0.1013,0.3485 -0.0677,0.0912 -0.1749,0.1366 -0.3219,0.1366 -0.0472,0 -0.14,-0.0119 -0.2784,-0.0354 -0.1382,-0.0236 -0.2397,-0.038 -0.3045,-0.044 0.1531,0.2177 0.2534,0.3647 0.3004,0.4411 0.047,0.0736 0.0706,0.1549 0.0706,0.2431 0,0.1411 -0.0533,0.2558 -0.1592,0.3439 -0.1028,0.0854 -0.2264,0.1279 -0.3705,0.1279 -0.1028,0 -0.1882,-0.0371 -0.2559,-0.1105 -0.0646,-0.0765 -0.1309,-0.1955 -0.1986,-0.3572 -0.0646,-0.1647 -0.1028,-0.259 -0.1146,-0.2825 -0.0117,0.0236 -0.0516,0.1178 -0.1192,0.2825 -0.0648,0.1648 -0.1281,0.2836 -0.1899,0.3572 -0.0617,0.0735 -0.1454,0.1105 -0.2513,0.1105 -0.15,0 -0.2779,-0.0425 -0.3838,-0.1279 -0.1059,-0.0881 -0.1587,-0.2028 -0.1587,-0.3439 0,-0.0617 0.0156,-0.1268 0.0481,-0.1945 0.0323,-0.0705 0.0665,-0.1311 0.1018,-0.1812 0.0382,-0.0499 0.1118,-0.1527 0.2206,-0.3086 -0.0736,0.0059 -0.1751,0.0205 -0.3045,0.044 -0.1294,0.0236 -0.2176,0.0354 -0.2646,0.0354 -0.1472,0 -0.2577,-0.0454 -0.3311,-0.1366 -0.0707,-0.0941 -0.1059,-0.2103 -0.1059,-0.3485 0,-0.1412 0.0352,-0.2573 0.1059,-0.3485 0.0734,-0.0942 0.184,-0.1412 0.3311,-0.1412 0.0736,0 0.1628,0.0101 0.2687,0.0307 0.1059,0.0204 0.2062,0.0384 0.3004,0.0532 -0.0824,-0.1177 -0.1648,-0.2371 -0.2472,-0.3577 -0.0824,-0.1206 -0.1233,-0.2278 -0.1233,-0.3219 0,-0.1384 0.0543,-0.2544 0.1633,-0.3485 0.1088,-0.0942 0.2351,-0.1412 0.3792,-0.1412z"
android:strokeWidth="1.3358" />
</group> </group>
</vector> </vector>

View file

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M20,6L12,6L10,4L4,4A2,2 0,0 0,2 6L2,18a2,2 0,0 0,2 2L20,20a2,2 0,0 0,2 -2L22,8A2,2 0,0 0,20 6ZM20,18L4,18L4,8L20,8ZM13,12L7,12v2h6v2l4,-3 -4,-3Z"/> android:pathData="M20,6L12,6L10,4L4,4A2,2 0,0 0,2 6L2,18a2,2 0,0 0,2 2L20,20a2,2 0,0 0,2 -2L22,8A2,2 0,0 0,20 6ZM20,18L4,18L4,8L20,8ZM13,12L7,12v2h6v2l4,-3 -4,-3Z" />
</vector> </vector>

View file

@ -1,10 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:tint="?colorOnPrimary"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24" android:viewportHeight="24">
android:tint="?colorOnPrimary"> <path
<path android:fillColor="#FFFFFFFF"
android:fillColor="#FFFFFFFF" android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM15,5L8,5c-1.1,0 -1.99,0.9 -1.99,2L6,21c0,1.1 0.89,2 1.99,2L19,23c1.1,0 2,-0.9 2,-2L21,11l-6,-6zM8,21L8,7h6v5h5v9L8,21z" />
android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM15,5L8,5c-1.1,0 -1.99,0.9 -1.99,2L6,21c0,1.1 0.89,2 1.99,2L19,23c1.1,0 2,-0.9 2,-2L21,11l-6,-6zM8,21L8,7h6v5h5v9L8,21z"/>
</vector> </vector>

View file

@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:viewportWidth="24.0" android:viewportWidth="24.0"
android:viewportHeight="24.0"> android:viewportHeight="24.0">
<path <path
android:fillColor="#FF000000" android:fillColor="#FF000000"
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/> android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z" />
</vector> </vector>

View file

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M19,5L7,5A2,2 0,0 0,5 7L5,21a2,2 0,0 0,2 2L21,23a2,2 0,0 0,2 -2L23,9ZM21,21L7,21L7,7L18.17,7L21,9.83ZM14,14a3,3 0,1 0,3 3A3,3 0,0 0,14 14ZM8,8h9v4L8,12ZM15,1L3,1A2,2 0,0 0,1 3L1,17L3,17L3,3L15,3Z"/> android:pathData="M19,5L7,5A2,2 0,0 0,5 7L5,21a2,2 0,0 0,2 2L21,23a2,2 0,0 0,2 -2L23,9ZM21,21L7,21L7,7L18.17,7L21,9.83ZM14,14a3,3 0,1 0,3 3A3,3 0,0 0,14 14ZM8,8h9v4L8,12ZM15,1L3,1A2,2 0,0 0,1 3L1,17L3,17L3,3L15,3Z" />
</vector> </vector>

View file

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM19,19L5,19L5,5h11.17L19,7.83L19,19zM12,12c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zM6,6h9v4L6,10z"/> android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM19,19L5,19L5,5h11.17L19,7.83L19,19zM12,12c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zM6,6h9v4L6,10z" />
</vector> </vector>

View file

@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:viewportWidth="24.0" android:viewportWidth="24.0"
android:viewportHeight="24.0"> android:viewportHeight="24.0">
<path <path
android:fillColor="#FF000000" android:fillColor="#FF000000"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/> android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
</vector> </vector>

View file

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/> android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
</vector> </vector>

View file

@ -3,7 +3,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92s2.92,-1.31 2.92,-2.92c0,-1.61 -1.31,-2.92 -2.92,-2.92zM18,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM6,13c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM18,20.02c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z"/> android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92s2.92,-1.31 2.92,-2.92c0,-1.61 -1.31,-2.92 -2.92,-2.92zM18,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM6,13c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM18,20.02c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z" />
</vector> </vector>

View file

@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:viewportWidth="24.0" android:viewportWidth="24.0"
android:viewportHeight="24.0"> android:viewportHeight="24.0">
<path <path
android:fillColor="#FFFF0000" android:fillColor="#FFFF0000"
android:pathData="M1,21h22L12,2 1,21zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z"/> android:pathData="M1,21h22L12,2 1,21zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z" />
</vector> </vector>

View file

@ -1,14 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!--
<!--
Requires a layer-list since attributes cannot be resolved in selectors, see: Requires a layer-list since attributes cannot be resolved in selectors, see:
https://stackoverflow.com/a/36424426/297261 https://stackoverflow.com/a/36424426/297261
--> -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item> <item>
<selector> <selector>
<item android:drawable="@color/list_multiselect_background" android:state_selected="true" /> <item
<item android:drawable="@android:color/transparent"/> android:drawable="@color/list_multiselect_background"
</selector> android:state_selected="true" />
</item> <item android:drawable="@android:color/transparent" />
<item android:drawable="?android:attr/selectableItemBackground"/> </selector>
</item>
<item android:drawable="?android:attr/selectableItemBackground" />
</layer-list> </layer-list>

View file

@ -3,44 +3,44 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?android:attr/windowBackground"
android:padding="@dimen/activity_horizontal_margin" android:padding="@dimen/activity_horizontal_margin"
tools:context="com.zeapo.pwdstore.git.GitOperationActivity" tools:context="com.zeapo.pwdstore.git.GitOperationActivity">
android:background="?android:attr/windowBackground">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
style="@style/TextAppearance.MaterialComponents.Headline5"
android:id="@+id/server_label" android:id="@+id/server_label"
android:textStyle="bold" style="@style/TextAppearance.MaterialComponents.Headline5"
android:textSize="24sp"
android:text="@string/server_name"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp" android:layout_margin="8dp"
android:text="@string/server_name"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
style="@style/TextAppearance.MaterialComponents.Headline6"
android:id="@+id/label_server_protocol" android:id="@+id/label_server_protocol"
style="@style/TextAppearance.MaterialComponents.Headline6"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/server_protocol"
android:layout_margin="8dp" android:layout_margin="8dp"
app:layout_constraintTop_toBottomOf="@id/server_label" android:text="@string/server_protocol"
app:layout_constraintStart_toStartOf="parent" /> app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/server_label" />
<com.google.android.material.button.MaterialButtonToggleGroup <com.google.android.material.button.MaterialButtonToggleGroup
style="@style/TextAppearance.MaterialComponents.Headline1"
android:id="@+id/clone_protocol_group" android:id="@+id/clone_protocol_group"
style="@style/TextAppearance.MaterialComponents.Headline1"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp" android:layout_margin="8dp"
app:layout_constraintTop_toBottomOf="@id/label_server_protocol"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/label_server_protocol"
app:selectionRequired="true" app:selectionRequired="true"
app:singleSelection="true"> app:singleSelection="true">
@ -61,11 +61,12 @@
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/server_user_layout" android:id="@+id/server_user_layout"
android:hint="@string/server_user"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp" android:layout_margin="8dp"
android:hint="@string/server_user"
app:layout_constraintTop_toBottomOf="@id/clone_protocol_group"> app:layout_constraintTop_toBottomOf="@id/clone_protocol_group">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/server_user" android:id="@+id/server_user"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -79,14 +80,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp" android:layout_margin="8dp"
android:hint="@string/server_url" android:hint="@string/server_url"
app:layout_constraintTop_toBottomOf="@id/server_user_layout" app:layout_constraintEnd_toStartOf="@id/label_server_port"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/label_server_port"> app:layout_constraintTop_toBottomOf="@id/server_user_layout">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/server_url"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/server_url"
android:inputType="textWebEmailAddress" /> android:inputType="textWebEmailAddress" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
@ -97,10 +98,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp" android:layout_margin="8dp"
android:hint="@string/server_port_hint" android:hint="@string/server_port_hint"
app:layout_constraintStart_toEndOf="@id/label_server_url" app:layout_constraintDimensionRatio="1:0.8"
app:layout_constraintTop_toBottomOf="@id/server_user_layout"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintDimensionRatio="1:0.8"> app:layout_constraintStart_toEndOf="@id/label_server_url"
app:layout_constraintTop_toBottomOf="@id/server_user_layout">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/server_port" android:id="@+id/server_port"
@ -123,19 +124,19 @@
android:id="@+id/server_path" android:id="@+id/server_path"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="textWebEmailAddress"/> android:inputType="textWebEmailAddress" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
style="@style/TextAppearance.MaterialComponents.Headline6"
android:id="@+id/label_connection_mode" android:id="@+id/label_connection_mode"
style="@style/TextAppearance.MaterialComponents.Headline6"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/connection_mode"
android:layout_margin="8dp" android:layout_margin="8dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
android:text="@string/connection_mode"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/label_server_path" /> app:layout_constraintTop_toBottomOf="@id/label_server_path" />
@ -144,8 +145,8 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@id/label_connection_mode" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toStartOf="parent"> app:layout_constraintTop_toBottomOf="@id/label_connection_mode">
<RadioButton <RadioButton
android:id="@+id/connection_mode_ssh_key" android:id="@+id/connection_mode_ssh_key"
@ -179,11 +180,11 @@
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/save_button" android:id="@+id/save_button"
android:text="@string/crypto_save"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@id/connection_mode_group" android:text="@string/crypto_save"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/connection_mode_group" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView> </ScrollView>

View file

@ -3,8 +3,8 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:padding="@dimen/activity_horizontal_margin"
android:background="?android:attr/windowBackground" android:background="?android:attr/windowBackground"
android:padding="@dimen/activity_horizontal_margin"
tools:context="com.zeapo.pwdstore.git.GitConfigActivity" tools:context="com.zeapo.pwdstore.git.GitConfigActivity"
tools:layout_editor_absoluteX="0dp" tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="81dp"> tools:layout_editor_absoluteY="81dp">
@ -15,8 +15,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp" android:layout_margin="8dp"
android:hint="@string/git_user_name_hint" android:hint="@string/git_user_name_hint"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteY="64dp"> tools:layout_editor_absoluteY="64dp">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
@ -33,8 +33,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp" android:layout_margin="8dp"
android:hint="@string/git_user_email" android:hint="@string/git_user_email"
app:layout_constraintTop_toBottomOf="@id/username_input_layout" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toStartOf="parent"> app:layout_constraintTop_toBottomOf="@id/username_input_layout">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/git_user_email" android:id="@+id/git_user_email"
@ -50,20 +50,20 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp" android:layout_margin="8dp"
android:text="@string/crypto_save" android:text="@string/crypto_save"
app:layout_constraintTop_toBottomOf="@id/email_input_layout" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toEndOf="parent"/> app:layout_constraintTop_toBottomOf="@id/email_input_layout" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
style="@style/TextAppearance.MaterialComponents.Headline5"
android:textStyle="bold"
android:textSize="24sp"
android:id="@+id/git_tools_title" android:id="@+id/git_tools_title"
style="@style/TextAppearance.MaterialComponents.Headline5"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp" android:layout_margin="8dp"
android:text="@string/hackish_tools" android:text="@string/hackish_tools"
app:layout_constraintTop_toBottomOf="@id/save_button" android:textSize="24sp"
app:layout_constraintStart_toStartOf="parent" /> android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/save_button" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/commit_hash_label" android:id="@+id/commit_hash_label"
@ -71,8 +71,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp" android:layout_margin="8dp"
android:text="@string/commit_hash" android:text="@string/commit_hash"
app:layout_constraintTop_toBottomOf="@id/git_tools_title" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toStartOf="parent"/> app:layout_constraintTop_toBottomOf="@id/git_tools_title" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/git_commit_hash" android:id="@+id/git_commit_hash"
@ -80,9 +80,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp" android:layout_margin="8dp"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@id/git_tools_title"
app:layout_constraintStart_toEndOf="@id/commit_hash_label" app:layout_constraintStart_toEndOf="@id/commit_hash_label"
tools:text="HASH"/> app:layout_constraintTop_toBottomOf="@id/git_tools_title"
tools:text="HASH" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/git_abort_rebase" android:id="@+id/git_abort_rebase"

View file

@ -42,15 +42,16 @@
android:inputType="text" android:inputType="text"
tools:text="example.com" /> tools:text="example.com" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<ViewSwitcher <ViewSwitcher
android:id="@+id/rvPasswordSwitcher" android:id="@+id/rvPasswordSwitcher"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="@dimen/activity_vertical_margin" android:layout_marginTop="@dimen/activity_vertical_margin"
app:layout_constraintBottom_toTopOf="@id/strictDomainSearch" app:layout_constraintBottom_toTopOf="@id/strictDomainSearch"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/searchLayout" app:layout_constraintTop_toBottomOf="@id/searchLayout">
android:layout_height="0dp">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvPassword" android:id="@+id/rvPassword"
@ -84,7 +85,7 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/rvPasswordSwitcher" app:layout_constraintTop_toBottomOf="@id/rvPasswordSwitcher"
app:layout_constraintVertical_bias="1.0" app:layout_constraintVertical_bias="1.0"
tools:text="Phishing-resistant search"/> tools:text="Phishing-resistant search" />
<Switch <Switch
android:id="@+id/shouldMatch" android:id="@+id/shouldMatch"

View file

@ -2,13 +2,13 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".PasswordStore" android:orientation="vertical"
android:orientation="vertical"> tools:context=".PasswordStore">
<LinearLayout <LinearLayout
android:id="@+id/main_layout" android:id="@+id/main_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"/> android:orientation="vertical" />
</LinearLayout> </LinearLayout>

View file

@ -7,10 +7,10 @@
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="20dp"
android:paddingLeft="24dp" android:paddingLeft="24dp"
android:paddingTop="20dp"
android:paddingRight="24dp" android:paddingRight="24dp"
android:paddingTop="20dp"> android:paddingBottom="20dp">
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -21,19 +21,19 @@
<androidx.appcompat.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:src="@drawable/autofill_ins_1" android:contentDescription="@string/autofill_ins_1_hint"
android:contentDescription="@string/autofill_ins_1_hint" /> android:src="@drawable/autofill_ins_1" />
<androidx.appcompat.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:src="@drawable/autofill_ins_2" android:contentDescription="@string/autofill_ins_2_hint"
android:contentDescription="@string/autofill_ins_2_hint" /> android:src="@drawable/autofill_ins_2" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -44,10 +44,10 @@
<androidx.appcompat.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="114dp" android:layout_height="114dp"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:src="@drawable/autofill_ins_3" android:layout_marginBottom="8dp"
android:contentDescription="@string/autofill_ins_3_hint" /> android:contentDescription="@string/autofill_ins_3_hint"
android:src="@drawable/autofill_ins_3" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/autofill_recycler" android:id="@+id/autofill_recycler"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:scrollbars="none" android:scrollbars="none"
tools:listitem="@layout/autofill_row_layout" tools:itemCount="20"
tools:itemCount="20"/> tools:listitem="@layout/autofill_row_layout" />
<ProgressBar <ProgressBar
android:id="@+id/progress_bar" android:id="@+id/progress_bar"
@ -24,16 +24,16 @@
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab" android:id="@+id/fab"
android:src="@drawable/ic_add_white_48dp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_compat_margin"
android:src="@drawable/ic_add_white_48dp"
app:backgroundTint="?attr/colorSecondary"
app:borderWidth="0dp"
app:elevation="6dp" app:elevation="6dp"
app:pressedTranslationZ="12dp" app:pressedTranslationZ="12dp"
app:backgroundTint="?attr/colorSecondary" app:rippleColor="?attr/colorSecondary" />
app:rippleColor="?attr/colorSecondary"
app:borderWidth="0dp"
android:layout_margin="@dimen/fab_compat_margin"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"/>
</RelativeLayout> </RelativeLayout>

View file

@ -1,37 +1,37 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="64dp" android:layout_height="64dp"
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin">
xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.appcompat.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:id="@+id/app_icon" android:id="@+id/app_icon"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:contentDescription="@string/app_icon_hint"/> android:contentDescription="@string/app_icon_hint" />
<LinearLayout <LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="8dp"
android:gravity="center_vertical"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/app_name"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="wrap_content" />
android:layout_marginStart="8dp"
android:gravity="center_vertical"
android:orientation="vertical" >
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/app_name" android:id="@+id/secondary_text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
<androidx.appcompat.widget.AppCompatTextView android:textColor="?android:attr/textColor" />
android:id="@+id/secondary_text" </LinearLayout>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColor" />
</LinearLayout>
</LinearLayout> </LinearLayout>

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView 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" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="?android:attr/windowBackground" android:background="?android:attr/windowBackground"
android:orientation="vertical" android:orientation="vertical"
tools:context="com.zeapo.pwdstore.crypto.PgpActivity"> tools:context="com.zeapo.pwdstore.crypto.PgpActivity">
@ -58,8 +58,8 @@
android:id="@+id/divider" android:id="@+id/divider"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:src="@drawable/divider" android:src="@drawable/divider"
app:layout_constraintTop_toBottomOf="@id/crypto_password_last_changed" app:layout_constraintTop_toBottomOf="@id/crypto_password_last_changed"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
@ -70,8 +70,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/activity_vertical_margin" android:layout_marginTop="@dimen/activity_vertical_margin"
android:visibility="invisible" android:visibility="invisible"
tools:visibility="visible" app:layout_constraintTop_toBottomOf="@id/divider"
app:layout_constraintTop_toBottomOf="@id/divider"> tools:visibility="visible">
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/crypto_password_show_label" android:id="@+id/crypto_password_show_label"
@ -79,9 +79,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/password" android:text="@string/password"
android:textColor="?android:attr/textColor" android:textColor="?android:attr/textColor"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent" />
android:textStyle="bold" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/crypto_password_show" android:id="@+id/crypto_password_show"
@ -90,16 +90,16 @@
android:layout_gravity="fill" android:layout_gravity="fill"
android:gravity="bottom" android:gravity="bottom"
android:textColor="?android:attr/textColor" android:textColor="?android:attr/textColor"
app:layout_constraintStart_toEndOf="@id/crypto_password_show_label" android:typeface="monospace"
app:layout_constraintBaseline_toBaselineOf="@id/crypto_password_show_label" app:layout_constraintBaseline_toBaselineOf="@id/crypto_password_show_label"
android:typeface="monospace" /> app:layout_constraintStart_toEndOf="@id/crypto_password_show_label" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/crypto_password_toggle_show" android:id="@+id/crypto_password_toggle_show"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/show_password"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:text="@string/show_password"
app:layout_constraintTop_toBottomOf="@id/crypto_password_show_label" /> app:layout_constraintTop_toBottomOf="@id/crypto_password_show_label" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@ -116,15 +116,15 @@
android:id="@+id/crypto_copy_username" android:id="@+id/crypto_copy_username"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:background="?android:attr/windowBackground" android:background="?android:attr/windowBackground"
android:contentDescription="@string/copy_username" android:contentDescription="@string/copy_username"
android:src="@drawable/ic_content_copy" android:src="@drawable/ic_content_copy"
android:visibility="invisible" android:visibility="invisible"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
tools:visibility="visible"/> app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/crypto_username_show_label" android:id="@+id/crypto_username_show_label"
@ -139,84 +139,84 @@
android:visibility="invisible" android:visibility="invisible"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible"/> tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/crypto_username_show" android:id="@+id/crypto_username_show"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@id/crypto_username_show_label" android:layout_below="@id/crypto_username_show_label"
android:layout_alignParentStart="true"
android:layout_toStartOf="@id/crypto_copy_username" android:layout_toStartOf="@id/crypto_copy_username"
android:textColor="?android:attr/textColor" android:textColor="?android:attr/textColor"
android:textIsSelectable="true" android:textIsSelectable="true"
android:typeface="monospace" android:typeface="monospace"
android:visibility="invisible" android:visibility="invisible"
app:layout_constraintTop_toBottomOf="@id/crypto_username_show_label"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible"/> app:layout_constraintTop_toBottomOf="@id/crypto_username_show_label"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:id="@+id/crypto_copy_otp" android:id="@+id/crypto_copy_otp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_below="@id/crypto_username_show" android:layout_below="@id/crypto_username_show"
android:layout_alignParentEnd="true"
android:background="?android:attr/windowBackground" android:background="?android:attr/windowBackground"
android:contentDescription="@string/copy_otp" android:contentDescription="@string/copy_otp"
android:src="@drawable/ic_content_copy" android:src="@drawable/ic_content_copy"
android:visibility="invisible" android:visibility="invisible"
app:layout_constraintTop_toTopOf="@id/crypto_otp_show_label"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
tools:visibility="visible"/> app:layout_constraintTop_toTopOf="@id/crypto_otp_show_label"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/crypto_otp_show_label" android:id="@+id/crypto_otp_show_label"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@id/crypto_username_show" android:layout_below="@id/crypto_username_show"
android:layout_alignParentStart="true"
android:layout_toStartOf="@id/crypto_copy_otp" android:layout_toStartOf="@id/crypto_copy_otp"
android:text="@string/otp" android:text="@string/otp"
android:textColor="?android:attr/textColor" android:textColor="?android:attr/textColor"
app:layout_constraintTop_toBottomOf="@id/crypto_username_show" android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
android:textStyle="bold" /> app:layout_constraintTop_toBottomOf="@id/crypto_username_show" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/crypto_otp_show" android:id="@+id/crypto_otp_show"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@id/crypto_otp_show_label" android:layout_below="@id/crypto_otp_show_label"
android:layout_alignParentStart="true"
android:layout_toStartOf="@id/crypto_copy_otp" android:layout_toStartOf="@id/crypto_copy_otp"
android:textColor="?android:attr/textColor" android:textColor="?android:attr/textColor"
android:textIsSelectable="true" android:textIsSelectable="true"
app:layout_constraintTop_toBottomOf="@id/crypto_otp_show_label" android:typeface="monospace"
android:typeface="monospace" /> app:layout_constraintTop_toBottomOf="@id/crypto_otp_show_label" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/crypto_extra_show_label" android:id="@+id/crypto_extra_show_label"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@id/crypto_otp_show" android:layout_below="@id/crypto_otp_show"
android:layout_alignParentStart="true"
android:text="@string/extra_content" android:text="@string/extra_content"
android:textColor="?android:attr/textColor" android:textColor="?android:attr/textColor"
app:layout_constraintTop_toBottomOf="@id/crypto_otp_show" android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
android:textStyle="bold" /> app:layout_constraintTop_toBottomOf="@id/crypto_otp_show" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/crypto_extra_show" android:id="@+id/crypto_extra_show"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@id/crypto_extra_show_label" android:layout_below="@id/crypto_extra_show_label"
android:layout_alignParentStart="true"
android:textColor="?android:attr/textColor" android:textColor="?android:attr/textColor"
android:textIsSelectable="true" android:textIsSelectable="true"
app:layout_constraintTop_toBottomOf="@id/crypto_extra_show_label" android:typeface="monospace"
android:typeface="monospace" /> app:layout_constraintTop_toBottomOf="@id/crypto_extra_show_label" />
<ToggleButton <ToggleButton
android:id="@+id/crypto_extra_toggle_show" android:id="@+id/crypto_extra_toggle_show"
@ -225,13 +225,13 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/crypto_extra_show" android:layout_below="@id/crypto_extra_show"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:backgroundTint="?attr/colorSecondary"
android:checked="false" android:checked="false"
android:paddingTop="8dp" android:paddingTop="8dp"
android:textColor="?android:attr/windowBackground" android:textColor="?android:attr/windowBackground"
android:textOff="@string/show_extra" android:textOff="@string/show_extra"
android:textOn="@string/hide_extra" android:textOn="@string/hide_extra"
app:layout_constraintTop_toBottomOf="@id/crypto_extra_show" app:layout_constraintTop_toBottomOf="@id/crypto_extra_show" />
android:backgroundTint="?attr/colorSecondary"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <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" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="?android:attr/windowBackground" android:background="?android:attr/windowBackground"
android:orientation="vertical" android:orientation="vertical"
android:padding="@dimen/activity_horizontal_margin" android:padding="@dimen/activity_horizontal_margin"
@ -14,8 +14,8 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/activity_horizontal_margin" android:layout_marginStart="@dimen/activity_horizontal_margin"
android:textColor="?android:attr/textColor"
android:enabled="false" android:enabled="false"
android:textColor="?android:attr/textColor"
android:textSize="18sp" android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
@ -23,17 +23,18 @@
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/name_input_layout" android:id="@+id/name_input_layout"
android:layout_gravity="center_vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_margin="8dp" android:layout_margin="8dp"
android:hint="@string/crypto_name_hint" android:hint="@string/crypto_name_hint"
app:layout_constraintTop_toBottomOf="@id/crypto_password_category" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toStartOf="parent"> app:layout_constraintTop_toBottomOf="@id/crypto_password_category">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/crypto_password_file_edit" android:id="@+id/crypto_password_file_edit"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"/> android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
@ -43,13 +44,14 @@
android:layout_margin="8dp" android:layout_margin="8dp"
android:hint="@string/crypto_pass_label" android:hint="@string/crypto_pass_label"
app:endIconMode="password_toggle" app:endIconMode="password_toggle"
app:layout_constraintTop_toBottomOf="@id/name_input_layout" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toStartOf="parent"> app:layout_constraintTop_toBottomOf="@id/name_input_layout">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/crypto_password_edit" android:id="@+id/crypto_password_edit"
android:inputType="textVisiblePassword"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"
android:inputType="textVisiblePassword" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
@ -58,8 +60,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp" android:layout_margin="8dp"
android:text="@string/pwd_generate_button" android:text="@string/pwd_generate_button"
app:layout_constraintTop_toBottomOf="@id/password_input_layout" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toEndOf="parent"/> app:layout_constraintTop_toBottomOf="@id/password_input_layout" />
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/extra_input_layout" android:id="@+id/extra_input_layout"
@ -67,14 +69,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp" android:layout_margin="8dp"
android:hint="@string/crypto_extra_label" android:hint="@string/crypto_extra_label"
app:layout_constraintTop_toBottomOf="@id/generate_password" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toStartOf="parent"> app:layout_constraintTop_toBottomOf="@id/generate_password">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/crypto_extra_edit" android:id="@+id/crypto_extra_edit"
android:inputType="textMultiLine|textVisiblePassword"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"
android:inputType="textMultiLine|textVisiblePassword" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>

View file

@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
@ -17,7 +15,7 @@
android:id="@+id/folder_name_text" android:id="@+id/folder_name_text"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="textNoSuggestions|textVisiblePassword"/> android:inputType="textNoSuggestions|textVisiblePassword" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
</LinearLayout> </LinearLayout>

View file

@ -1,25 +1,25 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout 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" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="20dp"
android:paddingLeft="24dp" android:paddingLeft="24dp"
android:paddingTop="20dp"
android:paddingRight="24dp" android:paddingRight="24dp"
android:paddingTop="20dp"> android:paddingBottom="20dp">
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="URL"> android:hint="URL">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/webURL" android:id="@+id/webURL"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="textUri"/> android:inputType="textUri" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<RadioGroup <RadioGroup
@ -56,14 +56,14 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:layout_weight="1"/> android:layout_weight="1" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/matchButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="+"
android:id="@+id/matchButton"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:text="+"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
<RadioButton <RadioButton
@ -72,7 +72,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:checked="false" android:checked="false"
android:text="@string/autofill_apps_never"/> android:text="@string/autofill_apps_never" />
</RadioGroup> </RadioGroup>

View file

@ -7,10 +7,10 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="20dp"
android:paddingLeft="24dp" android:paddingLeft="24dp"
android:paddingRight="24dp"
android:paddingTop="20dp" android:paddingTop="20dp"
android:paddingRight="24dp"
android:paddingBottom="20dp"
tools:context=".MainActivityFragment"> tools:context=".MainActivityFragment">
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView

View file

@ -7,10 +7,10 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="20dp"
android:paddingLeft="24dp" android:paddingLeft="24dp"
android:paddingTop="20dp"
android:paddingRight="24dp" android:paddingRight="24dp"
android:paddingTop="20dp"> android:paddingBottom="20dp">
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/public_key" android:id="@+id/public_key"

View file

@ -18,18 +18,20 @@
android:text="@string/ssh_keygen_length" /> android:text="@string/ssh_keygen_length" />
<com.google.android.material.button.MaterialButtonToggleGroup <com.google.android.material.button.MaterialButtonToggleGroup
style="@style/TextAppearance.MaterialComponents.Headline1"
android:id="@+id/key_length_group" android:id="@+id/key_length_group"
style="@style/TextAppearance.MaterialComponents.Headline1"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:selectionRequired="true" app:selectionRequired="true"
app:singleSelection="true"> app:singleSelection="true">
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/key_length_2048" android:id="@+id/key_length_2048"
style="?attr/materialButtonOutlinedStyle" style="?attr/materialButtonOutlinedStyle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/key_length_2048" /> android:text="@string/key_length_2048" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/key_length_4096" android:id="@+id/key_length_4096"
style="?attr/materialButtonOutlinedStyle" style="?attr/materialButtonOutlinedStyle"
@ -49,8 +51,8 @@
android:id="@+id/passphrase" android:id="@+id/passphrase"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:importantForAccessibility="no"
android:fontFamily="@font/sourcecodepro" android:fontFamily="@font/sourcecodepro"
android:importantForAccessibility="no"
android:inputType="textPassword" /> android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>

View file

@ -1,8 +1,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="?attr/colorPrimary" android:background="?attr/colorPrimary"
android:orientation="vertical"> android:orientation="vertical">
@ -11,37 +10,37 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:src="@mipmap/ic_launcher"
android:contentDescription="@string/app_icon_hint" android:contentDescription="@string/app_icon_hint"
app:layout_constraintTop_toTopOf="parent" android:src="@mipmap/ic_launcher"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/app_name" android:id="@+id/app_name"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/app_name"
android:layout_below="@+id/app_icon" android:layout_below="@+id/app_icon"
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:text="@string/app_name"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@+id/app_icon" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintTop_toBottomOf="@+id/app_icon" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
style="@style/Widget.MaterialComponents.Button.TextButton" style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:onClick="openSettings"
android:text="@string/action_settings"
android:textColor="@android:color/white"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_marginEnd="@dimen/activity_horizontal_margin" android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:onClick="openSettings"
android:text="@string/action_settings"
android:textColor="@android:color/white"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
@ -49,13 +48,13 @@
android:id="@+id/use_local_directory" android:id="@+id/use_local_directory"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="48dp"
android:onClick="createNewRepository" android:onClick="createNewRepository"
android:text="@string/initialize" android:text="@string/initialize"
android:textSize="12sp" android:textSize="12sp"
android:layout_marginTop="48dp" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/app_name"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintTop_toBottomOf="@id/app_name" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -63,8 +62,8 @@
android:onClick="cloneExistingRepository" android:onClick="cloneExistingRepository"
android:text="@string/clone" android:text="@string/clone"
android:textSize="12sp" android:textSize="12sp"
app:layout_constraintTop_toBottomOf="@id/use_local_directory" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintTop_toBottomOf="@id/use_local_directory" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

Some files were not shown because too many files have changed in this diff Show more