From dc3fcbdc8e8a81f48be6f33b5eb1cb54b8ef56a0 Mon Sep 17 00:00:00 2001 From: Fabian Henneke Date: Thu, 23 Jul 2020 13:54:53 +0200 Subject: [PATCH 1/7] Fix a crash when parsing long key IDs (#959) Co-authored-by: Harsh Shandilya (cherry picked from commit fbd84fde3a25d23cbf51fe0cd609ce9bd13836cc) --- .../com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt b/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt index 6cacf4b90..ee57acf81 100644 --- a/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt @@ -257,8 +257,8 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB it.matches("[a-fA-F0-9]{16}".toRegex()) } if (maybeLongKeyId != null) { - val keyId = maybeLongKeyId.toULong() - return GpgIdentifier.KeyId(maybeLongKeyId.toLong()) + val keyId = maybeLongKeyId.toULong(16) + return GpgIdentifier.KeyId(keyId.toLong()) } // Match fingerprints: From 859da9d9141f15fe6d61a942458a3a10ce89b081 Mon Sep 17 00:00:00 2001 From: Fabian Henneke Date: Thu, 23 Jul 2020 15:59:12 +0200 Subject: [PATCH 2/7] Fix OpenPgpApi extra type confusion (#960) (cherry picked from commit da167599d2d7f387daf62bbc51720577de30e7ad) --- .../java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt b/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt index ee57acf81..851dbaa10 100644 --- a/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt @@ -313,7 +313,7 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB } val gpgIdentifierFileContent = gpgIdentifierFile.useLines { it.firstOrNull() } ?: "" when (val identifier = parseGpgIdentifier(gpgIdentifierFileContent)) { - is GpgIdentifier.KeyId -> data.putExtra(OpenPgpApi.EXTRA_KEY_IDS, arrayOf(identifier.id)) + is GpgIdentifier.KeyId -> data.putExtra(OpenPgpApi.EXTRA_KEY_IDS, arrayOf(identifier.id).toLongArray()) is GpgIdentifier.UserId -> data.putExtra(OpenPgpApi.EXTRA_USER_IDS, arrayOf(identifier.email)) null -> { snackbar(message = resources.getString(R.string.invalid_gpg_id)) From 1546f862c56e72540b79126433e2deb3d26bef7f Mon Sep 17 00:00:00 2001 From: Harsh Shandilya Date: Thu, 23 Jul 2020 21:29:04 +0530 Subject: [PATCH 3/7] Wire in fallback key selection flow (#958) Co-authored-by: Fabian Henneke (cherry picked from commit 084b833fa49a583433284f0173cb7342152b263b) --- app/src/main/AndroidManifest.xml | 5 + .../pwdstore/crypto/GetKeyIdsActivity.kt | 74 +++++ .../crypto/PasswordCreationActivity.kt | 298 ++++++++++-------- .../zeapo/pwdstore/utils/PreferenceKeys.kt | 3 - app/src/main/res/values/strings.xml | 3 +- 5 files changed, 245 insertions(+), 138 deletions(-) create mode 100644 app/src/main/java/com/zeapo/pwdstore/crypto/GetKeyIdsActivity.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2fc14f3bc..dae4466bf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -81,6 +81,11 @@ android:parentActivityName=".PasswordStore" android:windowSoftInputMode="adjustResize" /> + + + if (result.data == null || result.resultCode == RESULT_CANCELED) { + setResult(RESULT_CANCELED, result.data) + finish() + return@registerForActivityResult + } + getKeyIds(result.data!!) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + bindToOpenKeychain(this) + } + + override fun onBound(service: IOpenPgpService2) { + super.onBound(service) + getKeyIds() + } + + override fun onError(e: Exception) { + e(e) + } + + /** + * Get the Key ids from OpenKeychain + */ + private fun getKeyIds(data: Intent = Intent()) { + data.action = OpenPgpApi.ACTION_GET_KEY_IDS + lifecycleScope.launch(Dispatchers.IO) { + api?.executeApiAsync(data, null, null) { result -> + when (result?.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { + OpenPgpApi.RESULT_CODE_SUCCESS -> { + try { + val ids = result.getLongArrayExtra(OpenPgpApi.RESULT_KEY_IDS)?.map { + OpenPgpUtils.convertKeyIdToHex(it) + } ?: emptyList() + val keyResult = Intent().putExtra(OpenPgpApi.EXTRA_KEY_IDS, ids.toTypedArray()) + setResult(RESULT_OK, keyResult) + finish() + } catch (e: Exception) { + e(e) + } + } + OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED -> { + val sender = getUserInteractionRequestIntent(result) + userInteractionRequiredResult.launch(IntentSenderRequest.Builder(sender).build()) + } + OpenPgpApi.RESULT_CODE_ERROR -> handleError(result) + } + } + } + } +} diff --git a/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt b/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt index 851dbaa10..3c1757f1f 100644 --- a/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt @@ -250,7 +250,7 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB } @OptIn(ExperimentalUnsignedTypes::class) - private fun parseGpgIdentifier(identifier: String) : GpgIdentifier? { + private fun parseGpgIdentifier(identifier: String): GpgIdentifier? { // Match long key IDs: // FF22334455667788 or 0xFF22334455667788 val maybeLongKeyId = identifier.removePrefix("0x").takeIf { @@ -279,166 +279,196 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB /** * Encrypts the password and the extra content */ - private fun encrypt(receivedIntent: Intent? = null) = with(binding) { - val editName = filename.text.toString().trim() - val editPass = password.text.toString() - val editExtra = extraContent.text.toString() + private fun encrypt(receivedIntent: Intent? = null) { + with(binding) { + val editName = filename.text.toString().trim() + val editPass = password.text.toString() + val editExtra = extraContent.text.toString() - if (editName.isEmpty()) { - snackbar(message = resources.getString(R.string.file_toast_text)) - return@with - } else if (editName.contains('/')) { - snackbar(message = resources.getString(R.string.invalid_filename_text)) - return@with - } - - if (editPass.isEmpty() && editExtra.isEmpty()) { - snackbar(message = resources.getString(R.string.empty_toast_text)) - return@with - } - - if (copy) { - copyPasswordToClipboard(editPass) - } - - val data = receivedIntent ?: Intent() - data.action = OpenPgpApi.ACTION_ENCRYPT - - // pass enters the key ID into `.gpg-id`. - val repoRoot = PasswordRepository.getRepositoryDirectory(applicationContext) - val gpgIdentifierFile = File(repoRoot, directory.text.toString()).findTillRoot(".gpg-id", repoRoot) - if (gpgIdentifierFile == null) { - snackbar(message = resources.getString(R.string.failed_to_find_key_id)) - return@with - } - val gpgIdentifierFileContent = gpgIdentifierFile.useLines { it.firstOrNull() } ?: "" - when (val identifier = parseGpgIdentifier(gpgIdentifierFileContent)) { - is GpgIdentifier.KeyId -> data.putExtra(OpenPgpApi.EXTRA_KEY_IDS, arrayOf(identifier.id).toLongArray()) - is GpgIdentifier.UserId -> data.putExtra(OpenPgpApi.EXTRA_USER_IDS, arrayOf(identifier.email)) - null -> { - snackbar(message = resources.getString(R.string.invalid_gpg_id)) + if (editName.isEmpty()) { + snackbar(message = resources.getString(R.string.file_toast_text)) + return@with + } else if (editName.contains('/')) { + snackbar(message = resources.getString(R.string.invalid_filename_text)) return@with } - } - data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true) - val content = "$editPass\n$editExtra" - val inputStream = ByteArrayInputStream(content.toByteArray()) - val outputStream = ByteArrayOutputStream() - - val path = when { - // If we allowed the user to edit the relative path, we have to consider it here instead - // of fullPath. - directoryInputLayout.isEnabled -> { - val editRelativePath = directory.text.toString().trim() - if (editRelativePath.isEmpty()) { - snackbar(message = resources.getString(R.string.path_toast_text)) - return - } - val passwordDirectory = File("$repoPath/${editRelativePath.trim('/')}") - if (!passwordDirectory.exists() && !passwordDirectory.mkdir()) { - snackbar(message = "Failed to create directory ${editRelativePath.trim('/')}") - return - } - - "${passwordDirectory.path}/$editName.gpg" + if (editPass.isEmpty() && editExtra.isEmpty()) { + snackbar(message = resources.getString(R.string.empty_toast_text)) + return@with } - else -> "$fullPath/$editName.gpg" - } - lifecycleScope.launch(Dispatchers.IO) { - api?.executeApiAsync(data, inputStream, outputStream) { result -> - when (result?.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { - OpenPgpApi.RESULT_CODE_SUCCESS -> { - try { - val file = File(path) - // If we're not editing, this file should not already exist! - if (!editing && file.exists()) { - snackbar(message = getString(R.string.password_creation_duplicate_error)) - return@executeApiAsync - } + if (copy) { + copyPasswordToClipboard(editPass) + } - if (!isInsideRepository(file)) { - snackbar(message = getString(R.string.message_error_destination_outside_repo)) - return@executeApiAsync - } - - try { - file.outputStream().use { - it.write(outputStream.toByteArray()) - } - } catch (e: IOException) { - e(e) { "Failed to write password file" } - setResult(RESULT_CANCELED) - MaterialAlertDialogBuilder(this@PasswordCreationActivity) - .setTitle(getString(R.string.password_creation_file_fail_title)) - .setMessage(getString(R.string.password_creation_file_write_fail_message)) - .setCancelable(false) - .setPositiveButton(android.R.string.ok) { _, _ -> - finish() - } - .show() - return@executeApiAsync - } - - val returnIntent = Intent() - returnIntent.putExtra(RETURN_EXTRA_CREATED_FILE, path) - returnIntent.putExtra(RETURN_EXTRA_NAME, editName) - returnIntent.putExtra(RETURN_EXTRA_LONG_NAME, getLongName(fullPath, repoPath, editName)) - - if (shouldGeneratePassword) { - val directoryStructure = - AutofillPreferences.directoryStructure(applicationContext) - val entry = PasswordEntry(content) - returnIntent.putExtra(RETURN_EXTRA_PASSWORD, entry.password) - val username = PasswordEntry(content).username - ?: directoryStructure.getUsernameFor(file) - returnIntent.putExtra(RETURN_EXTRA_USERNAME, username) - } + val data = receivedIntent ?: Intent() + data.action = OpenPgpApi.ACTION_ENCRYPT + // pass enters the key ID into `.gpg-id`. + val repoRoot = PasswordRepository.getRepositoryDirectory(applicationContext) + val gpgIdentifierFile = File(repoRoot, directory.text.toString()).findTillRoot(".gpg-id", repoRoot) + if (gpgIdentifierFile == null) { + snackbar(message = resources.getString(R.string.failed_to_find_key_id)) + return@with + } + val gpgIdentifiers = gpgIdentifierFile.readLines() + .filter { it.isNotBlank() } + .map { line -> + parseGpgIdentifier(line) ?: run { + snackbar(message = resources.getString(R.string.invalid_gpg_id)) + return@with + } + } + if (gpgIdentifiers.isEmpty()) { + registerForActivityResult(StartActivityForResult()) { result -> + if (result.resultCode == RESULT_OK) { + result.data?.getStringArrayExtra(OpenPgpApi.EXTRA_KEY_IDS)?.let { keyIds -> + gpgIdentifierFile.writeText(keyIds.joinToString("\n")) val repo = PasswordRepository.getRepository(null) if (repo != null) { - val status = Git(repo).status().call() - if (status.modified.isNotEmpty()) { - commitChange( - getString( - R.string.git_commit_edit_text, - getLongName(fullPath, repoPath, editName) - ) + commitChange( + getString( + R.string.git_commit_gpg_id, + getLongName(gpgIdentifierFile.parentFile!!.absolutePath, repoPath, gpgIdentifierFile.name) ) - } + ) } + encrypt(data) + } + } + }.launch(Intent(this@PasswordCreationActivity, GetKeyIdsActivity::class.java)) + return@with + } + val keyIds = gpgIdentifiers.filterIsInstance().map { it.id }.toLongArray() + if (keyIds.isNotEmpty()) { + data.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keyIds) + } + val userIds = gpgIdentifiers.filterIsInstance().map { it.email }.toTypedArray() + if (userIds.isNotEmpty()) { + data.putExtra(OpenPgpApi.EXTRA_USER_IDS, userIds) + } - if (directoryInputLayout.isVisible && directoryInputLayout.isEnabled && oldFileName != null) { - val oldFile = File("$repoPath/${oldCategory?.trim('/')}/$oldFileName.gpg") - if (oldFile.path != file.path && !oldFile.delete()) { + data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true) + + val content = "$editPass\n$editExtra" + val inputStream = ByteArrayInputStream(content.toByteArray()) + val outputStream = ByteArrayOutputStream() + + val path = when { + // If we allowed the user to edit the relative path, we have to consider it here instead + // of fullPath. + directoryInputLayout.isEnabled -> { + val editRelativePath = directory.text.toString().trim() + if (editRelativePath.isEmpty()) { + snackbar(message = resources.getString(R.string.path_toast_text)) + return + } + val passwordDirectory = File("$repoPath/${editRelativePath.trim('/')}") + if (!passwordDirectory.exists() && !passwordDirectory.mkdir()) { + snackbar(message = "Failed to create directory ${editRelativePath.trim('/')}") + return + } + + "${passwordDirectory.path}/$editName.gpg" + } + else -> "$fullPath/$editName.gpg" + } + + lifecycleScope.launch(Dispatchers.IO) { + api?.executeApiAsync(data, inputStream, outputStream) { result -> + when (result?.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { + OpenPgpApi.RESULT_CODE_SUCCESS -> { + try { + val file = File(path) + // If we're not editing, this file should not already exist! + if (!editing && file.exists()) { + snackbar(message = getString(R.string.password_creation_duplicate_error)) + return@executeApiAsync + } + + if (!isInsideRepository(file)) { + snackbar(message = getString(R.string.message_error_destination_outside_repo)) + return@executeApiAsync + } + + try { + file.outputStream().use { + it.write(outputStream.toByteArray()) + } + } catch (e: IOException) { + e(e) { "Failed to write password file" } setResult(RESULT_CANCELED) MaterialAlertDialogBuilder(this@PasswordCreationActivity) - .setTitle(R.string.password_creation_file_fail_title) - .setMessage(getString(R.string.password_creation_file_delete_fail_message, oldFileName)) + .setTitle(getString(R.string.password_creation_file_fail_title)) + .setMessage(getString(R.string.password_creation_file_write_fail_message)) .setCancelable(false) .setPositiveButton(android.R.string.ok) { _, _ -> finish() } .show() + return@executeApiAsync + } + + val returnIntent = Intent() + returnIntent.putExtra(RETURN_EXTRA_CREATED_FILE, path) + returnIntent.putExtra(RETURN_EXTRA_NAME, editName) + returnIntent.putExtra(RETURN_EXTRA_LONG_NAME, getLongName(fullPath, repoPath, editName)) + + if (shouldGeneratePassword) { + val directoryStructure = + AutofillPreferences.directoryStructure(applicationContext) + val entry = PasswordEntry(content) + returnIntent.putExtra(RETURN_EXTRA_PASSWORD, entry.password) + val username = PasswordEntry(content).username + ?: directoryStructure.getUsernameFor(file) + returnIntent.putExtra(RETURN_EXTRA_USERNAME, username) + } + + val repo = PasswordRepository.getRepository(null) + if (repo != null) { + val status = Git(repo).status().call() + if (status.modified.isNotEmpty()) { + commitChange( + getString( + R.string.git_commit_edit_text, + getLongName(fullPath, repoPath, editName) + ) + ) + } + } + + if (directoryInputLayout.isVisible && directoryInputLayout.isEnabled && oldFileName != null) { + val oldFile = File("$repoPath/${oldCategory?.trim('/')}/$oldFileName.gpg") + if (oldFile.path != file.path && !oldFile.delete()) { + setResult(RESULT_CANCELED) + MaterialAlertDialogBuilder(this@PasswordCreationActivity) + .setTitle(R.string.password_creation_file_fail_title) + .setMessage(getString(R.string.password_creation_file_delete_fail_message, oldFileName)) + .setCancelable(false) + .setPositiveButton(android.R.string.ok) { _, _ -> + finish() + } + .show() + } else { + setResult(RESULT_OK, returnIntent) + finish() + } } else { setResult(RESULT_OK, returnIntent) finish() } - } else { - setResult(RESULT_OK, returnIntent) - finish() - } - } catch (e: Exception) { - e(e) { "An Exception occurred" } + } catch (e: Exception) { + e(e) { "An Exception occurred" } + } } + OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED -> { + val sender = getUserInteractionRequestIntent(result) + userInteractionRequiredResult.launch(IntentSenderRequest.Builder(sender).build()) + } + OpenPgpApi.RESULT_CODE_ERROR -> handleError(result) } - OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED -> { - val sender = getUserInteractionRequestIntent(result) - userInteractionRequiredResult.launch(IntentSenderRequest.Builder(sender).build()) - } - OpenPgpApi.RESULT_CODE_ERROR -> handleError(result) } } } diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/PreferenceKeys.kt b/app/src/main/java/com/zeapo/pwdstore/utils/PreferenceKeys.kt index 3235c7fc9..9eb549dae 100644 --- a/app/src/main/java/com/zeapo/pwdstore/utils/PreferenceKeys.kt +++ b/app/src/main/java/com/zeapo/pwdstore/utils/PreferenceKeys.kt @@ -37,9 +37,6 @@ object PreferenceKeys { const val GIT_SERVER_INFO = "git_server_info" const val HTTPS_PASSWORD = "https_password" const val LENGTH = "length" - const val OPENPGP_KEY_IDS_SET = "openpgp_key_ids_set" - const val OPENPGP_KEY_ID_PREF = "openpgp_key_id_pref" - const val OPENPGP_PROVIDER_LIST = "openpgp_provider_list" const val OREO_AUTOFILL_CUSTOM_PUBLIC_SUFFIXES = "oreo_autofill_custom_public_suffixes" const val OREO_AUTOFILL_DEFAULT_USERNAME = "oreo_autofill_default_username" const val OREO_AUTOFILL_DIRECTORY_STRUCTURE = "oreo_autofill_directory_structure" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 489b07264..a59d59651 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -45,6 +45,7 @@ Remove %1$s from store. Rename %1$s to %2$s. Move multiple passwords to %1$s. + Initialize GPG IDs in %1$s. Password copied to clipboard, you have %d seconds to paste it somewhere. @@ -366,7 +367,7 @@ Failed to import TOTP configuration Exporting passwords… Failed to locate .gpg-id, is your store set up correctly? - Found .gpg-id, but it did not contain a key ID, fingerprint or user ID + Found .gpg-id, but it contains an invalid key ID, fingerprint or user ID File name must not contain \'/\', set directory above Directory From f1ad84c34b571bd75bc0b1f73a7cf26b0e2b42e4 Mon Sep 17 00:00:00 2001 From: Fabian Henneke Date: Thu, 23 Jul 2020 18:06:36 +0200 Subject: [PATCH 4/7] Add CHANGELOG entries for 1.10.1 (#961) (cherry picked from commit da3ca10de664ec90f12320f3059c7b8fdc8315f9) --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37c7d3bfc..2df62f160 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Fixed + +- Using long key IDs in .gpg-id no longer leads to a crash +- Long key IDs and fingerprints are now correctly forwarded to OpenKeychain + +### Added + +- Support for multiple GPG IDs in .gpg-id +- Creating an entry in an empty store now lets you select keys to initialize .gpg-id with + ## [1.10.0] - 2020-07-22 ### Changed From e15a1d2159ca0f825d166c91457914a832d0f646 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 23 Jul 2020 16:30:46 +0000 Subject: [PATCH 5/7] Prepare release 1.10.1 --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2df62f160..b186d093e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [1.10.1] - 2020-07-23 + ### Fixed - Using long key IDs in .gpg-id no longer leads to a crash @@ -257,7 +259,9 @@ All notable changes to this project will be documented in this file. - Fix elements overlapping. -[Unreleased]: https://github.com/android-password-store/Android-Password-Store/compare/1.10.0...HEAD +[Unreleased]: https://github.com/android-password-store/Android-Password-Store/compare/1.10.1...HEAD + +[1.10.1]: https://github.com/android-password-store/Android-Password-Store/compare/1.10.0...1.10.1 [1.10.0]: https://github.com/android-password-store/Android-Password-Store/compare/1.9.2...1.10.0 From 4063a429aca4f55e30ce7f53b13873f1dc3744b3 Mon Sep 17 00:00:00 2001 From: Harsh Shandilya Date: Thu, 23 Jul 2020 21:30:47 +0530 Subject: [PATCH 6/7] build: bump version Signed-off-by: Harsh Shandilya --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 756555159..9206396fc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,8 +25,8 @@ android { defaultConfig { applicationId 'dev.msfjarvis.aps' - versionCode 11001 - versionName '1.11.0-SNAPSHOT' + versionCode 11010 + versionName '1.10.1' } lintOptions { From 5c28fe9817446008c7d343fa3e020114b132ab15 Mon Sep 17 00:00:00 2001 From: Harsh Shandilya Date: Thu, 23 Jul 2020 22:02:32 +0530 Subject: [PATCH 7/7] build: prepare next development version Signed-off-by: Harsh Shandilya --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 9206396fc..78d2c50a0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,8 +25,8 @@ android { defaultConfig { applicationId 'dev.msfjarvis.aps' - versionCode 11010 - versionName '1.10.1' + versionCode 11011 + versionName '1.11.0-SNAPSHOT' } lintOptions {