Skip to content
This repository was archived by the owner on Jan 25, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
6216ac2
added 19 digit card support
Jan 12, 2023
74e92f4
improved payment gateway detection
Jan 12, 2023
1ff7b5d
fixed card pin bug in dashboard
Jan 12, 2023
8054450
defaulted to username on login item if available
Jan 12, 2023
720a373
added username clearing to AddLogin.kt
Jan 12, 2023
29f9503
added username clearing support logic to Dashboard
Jan 12, 2023
008b53a
added acra skeleton
Jan 16, 2023
6e75b2f
added totp size checker
Jan 16, 2023
1a940c5
implemented crash handler
Jan 17, 2023
4e4ece0
removed generic exception handlers
Jan 17, 2023
5bc31a5
removed generic exception handlers
Jan 17, 2023
6735af4
fixed more than 19 digits issue
Jan 18, 2023
a900727
fixed dashboard crash
Jan 18, 2023
f939bc2
Merge pull request #50 from Keyspace-cloud/totp_length_validation
nimish-ks Jan 19, 2023
9bb915f
removed email/username switch
Jan 19, 2023
bb6e078
added an email subject prefix
Jan 19, 2023
18f4b20
added basic deleted items UI
Jan 29, 2023
ff9a839
made settings UI match Android 13 spec even more
Jan 29, 2023
8dbc71e
adjusted margins and paddings
Jan 30, 2023
f0c3d8e
revamped developer options screen to Material 3.0 spec
Jan 30, 2023
b8ba703
renamed a setting category
Jan 30, 2023
a44ed77
Merge pull request #46 from Keyspace-cloud/usernames_on_dashboard
rohan-chaturvedi Feb 1, 2023
542786d
Merge pull request #45 from Keyspace-cloud/19_digit_cards
rohan-chaturvedi Feb 1, 2023
3c09799
chore: delete local.properties
rohan-chaturvedi Feb 1, 2023
afd644e
Update app/src/main/res/layout/settings.xml
0x4f53 Feb 1, 2023
40f730b
Merge pull request #53 from Keyspace-cloud/settings_ui_updates
0x4f53 Feb 1, 2023
d24ff2c
Update app/src/main/res/values/strings.xml
0x4f53 Feb 1, 2023
b358e3e
Merge pull request #49 from Keyspace-cloud/crash_logging
rohan-chaturvedi Feb 2, 2023
95a3624
chore: bump version
rohan-chaturvedi Feb 2, 2023
57ff73e
chore: bump versioncode
rohan-chaturvedi Feb 2, 2023
466f356
Merge pull request #56 from Keyspace-cloud/chore--bump-version
0x4f53 Feb 2, 2023
dad262b
Merge pull request #54 from Keyspace-cloud/chore--delete-local-proper…
0x4f53 Feb 2, 2023
ba47186
fixed nonfunctional back button in Developer Options
Feb 2, 2023
4c23e1b
added basic delete ui
Feb 4, 2023
8da4ad5
added delete tag to IOUtilities functions
Feb 4, 2023
2ba9ae0
added trashing to all add activities
Feb 4, 2023
748acd5
Merge branch 'v1.4.2' into recycle_bin
Feb 4, 2023
4c494db
added ignoreProperties to all IO data classes
Feb 4, 2023
19d8d2a
made delete all and restore all buttons work
Feb 5, 2023
b3bba9e
added no vault data graphic
Feb 5, 2023
1dc0a72
fixed dashboard illustration not showing
Feb 5, 2023
d8b9b5e
fixed recycler issues
Feb 5, 2023
4918a84
finished implementing logins deletion
Feb 6, 2023
e7fe91e
finished implementing notes deletion
Feb 6, 2023
d83f44f
finished implementing cards deletion
Feb 6, 2023
8f0dac8
fixed bank name discoloration in deleted items section
Feb 6, 2023
0fb71cc
made delete alertdialogs dismissable
Feb 6, 2023
30e3a8b
fixed dialog typos
Feb 6, 2023
2228d39
fixed syncing items bug on dashboard
Feb 6, 2023
085865c
fixed grammar on deleted screen
Feb 6, 2023
d2dcedd
fixed a visual inconsistency on deleted screen
Feb 6, 2023
f25fb7b
fixed a visual inconsistency on deleted screen... again
Feb 6, 2023
b554923
fixed more visual inconsistencies
Feb 6, 2023
c6efcd1
fixed theme switch crash
Feb 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 2 additions & 21 deletions .idea/assetWizardSettings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 10 additions & 6 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ android {
applicationId "cloud.keyspace.android"
minSdkVersion 27
targetSdkVersion 33
versionCode 141
versionName "1.4.1"
versionCode 142
versionName "1.4.2"
multiDexEnabled true

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
Expand Down Expand Up @@ -51,7 +51,7 @@ android {
dependencies {
implementation 'com.android.support:multidex:2.0.1' // noinspection GradleDependency
implementation 'androidx.core:core-ktx:1.9.0' // Default Android stuff
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'androidx.appcompat:appcompat:1.6.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.gridlayout:gridlayout:1.0.0'
Expand All @@ -67,8 +67,8 @@ dependencies {
implementation 'androidx.core:core-ktx:1.9.0'
testImplementation 'junit:junit:4.13.2' // Testing
implementation 'androidx.biometric:biometric:1.2.0-alpha05' // Biometrics
androidTestImplementation 'androidx.test.ext:junit:1.1.4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
implementation 'com.google.android.material:material:1.7.0' // Material design
implementation 'androidx.fragment:fragment-ktx:1.5.5'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
Expand Down Expand Up @@ -99,4 +99,8 @@ dependencies {
implementation 'com.scottyab:rootbeer-lib:0.1.0' // To check if device is rooted
implementation "androidx.core:core-splashscreen:1.0.0" // Splash screen library
implementation 'com.nulab-inc:zxcvbn:1.7.0' // password strength
}

// ACRA for crash logging
implementation 'ch.acra:acra-mail:5.9.7' // mail component
implementation 'ch.acra:acra-dialog:5.9.7' // dialog component
}
19 changes: 18 additions & 1 deletion app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,21 @@

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
#-renamesourcefileattribute SourceFile

#ACRA specifics
# Restore some Source file names and restore approximate line numbers in the stack traces,
# otherwise the stack traces are pretty useless
-keepattributes SourceFile, LineNumberTable

# ACRA needs "annotations" so add this...
# Note: This may already be defined in the default "proguard-android-optimize.txt"
# file in the SDK. If it is, then you don't need to duplicate it. See your
# "project.properties" file to get the path to the default "proguard-android-optimize.txt".
-keepattributes *Annotation*

# Keep all the ACRA classes
-keep class org.acra.** { *; }

# Don't warn about removed methods from AppCompat
-dontwarn android.support.v4.app.NotificationCompat*
7 changes: 7 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
android:fullBackupContent="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:name="cloud.keyspace.android.Keyspace"
android:largeHeap="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
Expand Down Expand Up @@ -73,6 +74,12 @@
android:theme="@style/Theme.KeyspaceMobile.NoActionBar"
android:configChanges="keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustPan" />
<activity
android:name=".DeletedItems"
android:launchMode="singleInstance"
android:theme="@style/Theme.KeyspaceMobile.NoActionBar"
android:configChanges="keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustPan" />
<activity
android:name=".Dashboard"
android:exported="false"
Expand Down
65 changes: 36 additions & 29 deletions app/src/main/kotlin/cloud/keyspace/android/AddCard.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.Editable
import android.text.SpannableStringBuilder
import android.text.TextUtils
import android.text.TextWatcher
import android.text.method.PasswordTransformationMethod
Expand Down Expand Up @@ -62,6 +63,7 @@ class AddCard : AppCompatActivity() {
lateinit var doneButton: ImageView
lateinit var backButton: ImageView
lateinit var deleteButton: ImageView
var deleted: Boolean = false

var cardColor: String? = null
lateinit var colorButton: ImageView
Expand Down Expand Up @@ -157,30 +159,18 @@ class AddCard : AppCompatActivity() {
deleteButton = findViewById (R.id.delete)
if (itemId != null) {
deleteButton.setOnClickListener {
val alertDialog: AlertDialog = MaterialAlertDialogBuilder(this).create()
alertDialog.setTitle("Delete")
alertDialog.setMessage("Would you like to delete \"${card.name}\"")
alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, "Delete") { dialog, _ ->

vault.card!!.remove(io.getCard(itemId!!, vault))
io.writeVault(vault)

network.writeQueueTask (itemId!!, mode = network.MODE_DELETE)
crypto.secureStartActivity (
nextActivity = Dashboard(),
nextActivityClassNameAsString = getString(R.string.title_activity_dashboard),
keyring = keyring,
itemId = null
)

}
alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "Go back") { dialog, _ -> dialog.dismiss() }
alertDialog.show()

val builder = MaterialAlertDialogBuilder(this@AddCard)
.setTitle("Delete card")
.setCancelable(true)
.setMessage("Would you like to delete this card?")
.setNegativeButton("Go back"){ _, _ -> }
.setPositiveButton("Delete"){ _, _ ->
deleted = !deleted
saveItem()
}
builder.show()
}
} else {
deleteButton.visibility = View.GONE
}
} else deleteButton.visibility = View.GONE

tagButton = findViewById (R.id.tag)
tagPicker = AddTag (tagId, applicationContext, this@AddCard, keyring)
Expand Down Expand Up @@ -266,12 +256,24 @@ class AddCard : AppCompatActivity() {
if (s.isNotEmpty() && s.length % 5 == 0) {
val c = s[s.length - 1]
if (space == c) s.delete(s.length - 1, s.length)
}

if (s.isNotEmpty() && s.length % 5 == 0) {
val c = s[s.length - 1]
if (Character.isDigit(c) && TextUtils.split(s.toString(), space.toString()).size <= 3) s.insert(s.length - 1, space.toString())
}
if (s.toString().replace(" ", "").length in 0..16) {
cardNumberInput.removeTextChangedListener(this)
cardNumberInput.setText(s.toString().replace(" ", "").replace("....".toRegex(), "$0 ")?.trim())
cardNumberInput.addTextChangedListener(this)
cardNumberInput.setSelection(cardNumberInput.text.toString().length)
}
if (s.toString().replace(" ", "").length in 17..18) {
for (c in s) {
if (c == ' ') {
s.delete(s.indexOf(c), s.indexOf(c)+1)
}
}
}
if (s.toString().replace(" ", "").length > 19) {
s.delete(s.length - 1, s.length)
}
}
override fun beforeTextChanged(cardNumber: CharSequence, start: Int, count: Int, after: Int) { }
override fun onTextChanged(cardNumber: CharSequence, start: Int, before: Int, count: Int) {
Expand Down Expand Up @@ -372,7 +374,9 @@ class AddCard : AppCompatActivity() {
vault.card?.remove(io.getCard(itemId!!, vault))
}

if (cardNumberInput.text.toString().length < 16) cardNumberInput.error = "Enter a valid 16 digit card number"
if (cardNumberInput.text.toString().replace(" ", "").length < 16) cardNumberInput.error = "Enter a valid 16 digit card number"
else if (cardNumberInput.text.toString().replace(" ", "").length in 17..18
|| cardNumberInput.text.toString().replace(" ", "").length > 19) cardNumberInput.error = "Enter a valid 19 digit card number"
else if (securityCode.text.toString().length !in 3..4) securityCode.error = "Enter a valid security code"
else if (toDate.text.toString().isEmpty()) toDate.error = "Enter an expiry date"
else if (cardholderNameInput.text.toString().isEmpty()) cardholderNameInput.error = "Enter card holder's name"
Expand All @@ -388,6 +392,7 @@ class AddCard : AppCompatActivity() {
name = nameInput.text.toString(),
color = cardColor,
favorite = favorite,
deleted = deleted,
tagId = tagPicker.getSelectedTagId() ?: tagId,
dateCreated = dateCreated,
dateModified = Instant.now().epochSecond,
Expand Down Expand Up @@ -438,7 +443,9 @@ class AddCard : AppCompatActivity() {

notesInput.setText(card.notes)

cardNumberInput.setText(card.cardNumber?.replace("....".toRegex(), "$0 "))
if (card.cardNumber?.length!! == 16) cardNumberInput.setText(card.cardNumber.replace("....".toRegex(), "$0 "))
else cardNumberInput.setText(card.cardNumber)

toDate.setText(card.expiry)
securityCode.setText(card.securityCode)
cardholderNameInput.setText(card.cardholderName)
Expand Down
65 changes: 24 additions & 41 deletions app/src/main/kotlin/cloud/keyspace/android/AddLogin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,6 @@ class AddLogin : AppCompatActivity() {
lateinit var emailInputLayout: TextInputLayout
lateinit var emailInput: TextInputEditText

lateinit var emailAsUsername: MaterialSwitch

lateinit var passwordInputLayout: TextInputLayout
lateinit var passwordInput: TextInputEditText
lateinit var clearButton: ImageView
Expand Down Expand Up @@ -136,6 +134,7 @@ class AddLogin : AppCompatActivity() {
lateinit var doneButton: ImageView
lateinit var backButton: ImageView
lateinit var deleteButton: ImageView
var deleted: Boolean = false

lateinit var keyring: CryptoUtilities.Keyring
private var itemId: String? = null
Expand Down Expand Up @@ -195,6 +194,11 @@ class AddLogin : AppCompatActivity() {
}
}

if (secretInput.text.toString().isNotBlank() && secretInput.text.toString().length < 6) {
secretInput.error = "Please enter a valid TOTP secret"
return@setOnClickListener
}

saveItem()

}
Expand All @@ -206,31 +210,20 @@ class AddLogin : AppCompatActivity() {

deleteButton = findViewById (R.id.delete)
if (itemId != null) {
deleteButton.visibility = View.VISIBLE
deleteButton.setOnClickListener {
val alertDialog: AlertDialog = MaterialAlertDialogBuilder(this).create()
alertDialog.setTitle("Delete")
alertDialog.setMessage("Would you like to delete \"${login.name}\"")
alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, "Delete") { dialog, _ ->

vault.login!!.remove(io.getLogin(itemId!!, vault))
io.writeVault(vault)

network.writeQueueTask (itemId!!, mode = network.MODE_DELETE)
crypto.secureStartActivity (
nextActivity = Dashboard(),
nextActivityClassNameAsString = getString(R.string.title_activity_dashboard),
keyring = keyring,
itemId = null
)

}
alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "Go back") { dialog, _ -> dialog.dismiss() }
alertDialog.show()

val builder = MaterialAlertDialogBuilder(this@AddLogin)
.setTitle("Delete login")
.setCancelable(true)
.setMessage("Would you like to delete this login?")
.setNegativeButton("Go back"){ _, _ -> }
.setPositiveButton("Delete"){ _, _ ->
deleted = !deleted
saveItem()
}
builder.show()
}
} else {
deleteButton.visibility = View.GONE
}
} else deleteButton.visibility = View.GONE

tagButton = findViewById (R.id.tag)
tagPicker = AddTag (tagId, applicationContext, this@AddLogin, keyring)
Expand Down Expand Up @@ -290,14 +283,6 @@ class AddLogin : AppCompatActivity() {
userNameInput = findViewById (R.id.userNameInput)
userNameInput.imeOptions = IME_FLAG_NO_PERSONALIZED_LEARNING
userNameInputLayout = findViewById (R.id.userNameInputLayout)
userNameInputLayout.visibility = View.GONE

emailAsUsername = findViewById (R.id.emailAsUsername)
emailAsUsername.isChecked = true
emailAsUsername.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) userNameInputLayout.visibility = View.GONE
else userNameInputLayout.visibility = View.VISIBLE
}

emailInput = findViewById (R.id.emailInput)
emailInput.imeOptions = IME_FLAG_NO_PERSONALIZED_LEARNING
Expand Down Expand Up @@ -629,7 +614,10 @@ class AddLogin : AppCompatActivity() {
override fun afterTextChanged (s: Editable) { }
override fun beforeTextChanged (s: CharSequence, start: Int, count: Int, after: Int) { }
override fun onTextChanged (s: CharSequence, start: Int, before: Int, count: Int) {
if (s.length >= 8) {
if (s.length >= 6) {

mfaTokenBox.visibility = View.VISIBLE

try {
otpCode = GoogleAuthenticator(base32secret = secretInput.text.toString()).generate()
runOnUiThread { tokenPreview.text = otpCode!!.replace("...".toRegex(), "$0 ") }
Expand All @@ -645,9 +633,7 @@ class AddLogin : AppCompatActivity() {
}
}
}, 0, 1000) // 1000 milliseconds = 1 second
} catch (timerError: IllegalStateException) { }

mfaTokenBox.visibility = View.VISIBLE
} catch (_: IllegalStateException) { } catch (_: IllegalArgumentException) { mfaTokenBox.visibility = View.GONE }

} else {
mfaTokenBox.visibility = View.GONE
Expand Down Expand Up @@ -784,10 +770,6 @@ class AddLogin : AppCompatActivity() {

if (!login.loginData!!.username.isNullOrBlank()) {
userNameInput.setText(login.loginData.username)
emailAsUsername.isChecked = false
} else {
userNameInputLayout.visibility = View.GONE
emailAsUsername.isChecked = true
}

if (!login.loginData.password.isNullOrEmpty()) {
Expand Down Expand Up @@ -982,6 +964,7 @@ class AddLogin : AppCompatActivity() {
name = siteNameInput.text.toString(),
notes = notesInput.text.toString(),
favorite = favorite,
deleted = deleted,
tagId = tagPicker.getSelectedTagId() ?: tagId,
loginData = IOUtilities.LoginData(
username = userNameInput.text.toString(),
Expand Down
Loading