Final wrapped up TOTP

Compatible to android 12
This commit is contained in:
Yogesh Choudhary Paliyal
2021-10-04 22:21:54 +05:30
parent 7097f96395
commit db3b59a181
17 changed files with 97 additions and 142 deletions

1
.idea/misc.xml generated
View File

@@ -7,6 +7,7 @@
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/drawable/ic_twotone_content_copy_24.xml" value="0.10989583333333333" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/activity_add_totpactivity.xml" value="0.16485507246376813" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/activity_dashboard.xml" value="0.2" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/activity_scanner.xml" value="0.10416666666666667" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/fragment_detail.xml" value="0.1403985507246377" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/fragment_home.xml" value="0.16304347826086957" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/item_accounts.xml" value="0.1" />

View File

@@ -13,15 +13,15 @@ def appPackageId = 'com.yogeshpaliyal.keypass'
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
compileSdkVersion 31
buildToolsVersion "30.0.3"
defaultConfig {
applicationId appPackageId
minSdkVersion 22
targetSdkVersion 30
versionCode 1309
versionName "1.3.9"
targetSdkVersion 31
versionCode 1400
versionName "1.4.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@@ -73,11 +73,11 @@ android {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.5.31"
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
implementation 'androidx.preference:preference-ktx:1.1.1'
testImplementation 'junit:junit:4.13.2'
@@ -134,11 +134,10 @@ dependencies {
// zxing library
// implementation "com.googl.ezxing:android-core:3.4.1"
implementation 'com.budiyev.android:code-scanner:2.1.0'
implementation 'com.journeyapps:zxing-android-embedded:4.2.0'
// apache common codec
implementation "commons-codec:commons-codec:1.15"
}

View File

@@ -4,6 +4,7 @@
package="com.yogeshpaliyal.keypass">
<uses-permission android:name="android.permission.CAMERA" />
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
<application
android:name=".MyApplication"
@@ -12,16 +13,20 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:hardwareAccelerated="true"
android:theme="@style/Theme.KeyPass">
<activity
android:name=".ui.addTOTP.AddTOTPActivity"
android:exported="true" />
android:exported="false" />
<activity
android:name=".ui.addTOTP.ScannerActivity"
android:exported="false" />
<activity android:name=".ui.backup.BackupActivity"
android:exported="false" />
<activity android:name=".ui.generate.GeneratePasswordActivity"
android:exported="true" />
<activity android:name=".ui.backup.BackupActivity" />
<activity android:name=".ui.generate.GeneratePasswordActivity" />
<activity android:name=".ui.auth.AuthenticationActivity">
<activity android:name=".ui.auth.AuthenticationActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -32,10 +37,19 @@
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
</activity>
<activity
android:exported="false"
android:name="com.journeyapps.barcodescanner.CaptureActivity"
android:screenOrientation="portrait"
tools:replace="screenOrientation" />
<activity
android:exported="false"
android:name=".ui.nav.DashboardActivity"
android:windowSoftInputMode="adjustPan" />
<activity
android:exported="false"
android:name=".ui.detail.DetailActivity"
android:windowSoftInputMode="adjustResize" />
@@ -45,7 +59,8 @@
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
tools:node="remove"
android:exported="false"/>
</application>
</manifest>

View File

@@ -15,15 +15,12 @@ import javax.inject.Inject
@HiltAndroidApp
class MyApplication : Application(), Configuration.Provider {
companion object {
lateinit var instance: MyApplication
}
@Inject
lateinit var workerFactory: HiltWorkerFactory
override fun onCreate() {
super.onCreate()
instance = this
}
override fun getWorkManagerConfiguration(): Configuration {

View File

@@ -27,7 +27,7 @@ object AppModule {
return Room.databaseBuilder(
context,
AppDatabase::class.java,
MyApplication.instance.getString(R.string.app_name)
context.getString(R.string.app_name)
).addMigrations(object : Migration(3, 4) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE `account` ADD COLUMN `unique_id` TEXT")

View File

@@ -2,25 +2,26 @@ package com.yogeshpaliyal.keypass.ui.addTOTP
import android.content.Context
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import com.google.zxing.integration.android.IntentIntegrator
import com.yogeshpaliyal.keypass.R
import com.yogeshpaliyal.keypass.constants.IntentKeys
import com.yogeshpaliyal.keypass.constants.RequestCodes
import com.yogeshpaliyal.keypass.databinding.ActivityAddTotpactivityBinding
import com.yogeshpaliyal.keypass.utils.TOTPHelper
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class AddTOTPActivity : AppCompatActivity() {
companion object{
companion object {
private const val ARG_ACCOUNT_ID = "account_id"
@@ -51,13 +52,15 @@ class AddTOTPActivity : AppCompatActivity() {
setSupportActionBar(binding.toolbar)
binding.tilSecretKey.isVisible = accountId == null
mViewModel.loadOldAccount(accountId)
binding.toolbar.setNavigationOnClickListener {
onBackPressed()
}
binding.tilSecretKey.setEndIconOnClickListener {
ScannerActivity.start(this)
// ScannerActivity.start(this)
IntentIntegrator(this).setPrompt("").initiateScan()
}
mViewModel.error.observe(this, Observer {
@@ -80,12 +83,12 @@ class AddTOTPActivity : AppCompatActivity() {
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
if (accountId != null)
menuInflater.inflate(R.menu.menu_delete,menu)
menuInflater.inflate(R.menu.menu_delete, menu)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.action_delete){
if (item.itemId == R.id.action_delete) {
deleteAccount()
}
return super.onOptionsItemSelected(item)
@@ -114,8 +117,23 @@ class AddTOTPActivity : AppCompatActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == RequestCodes.SCANNER && resultCode == RESULT_OK){
mViewModel.setSecretKey(data?.extras?.getString(IntentKeys.SCANNED_TEXT) ?: "")
val result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data)
if (result != null) {
if (result.contents != null) {
try {
val secretKey = TOTPHelper.getSecretKey(result.contents)
mViewModel.setSecretKey(secretKey)
} catch (e: Exception) {
e.printStackTrace()
}
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
if (requestCode == IntentIntegrator.REQUEST_CODE && resultCode == RESULT_OK) {
}
}
}

View File

@@ -10,6 +10,7 @@ import com.yogeshpaliyal.keypass.constants.AccountType
import com.yogeshpaliyal.keypass.data.AccountModel
import com.yogeshpaliyal.keypass.utils.Event
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import javax.inject.Inject
@@ -27,6 +28,16 @@ class AddTOTPViewModel @Inject constructor(private val appDatabase: AppDatabase)
val accountName = MutableLiveData<String>("")
fun loadOldAccount(accountId: String?){
accountId ?: return
viewModelScope.launch(Dispatchers.IO) {
appDatabase.getDao().getAccount(accountId)?.let { accountModel ->
accountName.postValue(accountModel.title ?: "")
}
}
}
fun saveAccount(accountId: String?) {
viewModelScope.launch {
val secretKey = secretKey.value

View File

@@ -1,20 +1,12 @@
package com.yogeshpaliyal.keypass.ui.addTOTP
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.budiyev.android.codescanner.CodeScanner
import com.budiyev.android.codescanner.DecodeCallback
import com.budiyev.android.codescanner.ErrorCallback
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.yogeshpaliyal.keypass.constants.IntentKeys
import com.yogeshpaliyal.keypass.constants.RequestCodes
import com.yogeshpaliyal.keypass.databinding.ActivityScannerBinding
import com.yogeshpaliyal.keypass.utils.TOTPHelper
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
@@ -22,7 +14,6 @@ class ScannerActivity : AppCompatActivity() {
private lateinit var binding: ActivityScannerBinding
private var codeScanner: CodeScanner? = null
private val REQUEST_CAM_PERMISSION = 432
@@ -46,53 +37,9 @@ class ScannerActivity : AppCompatActivity() {
onBackPressed()
}
codeScanner = CodeScanner(this, binding.scannerView)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf(Manifest.permission.CAMERA), REQUEST_CAM_PERMISSION)
} else {
codeScanner?.startPreview()
// Callbacks
codeScanner?.decodeCallback = DecodeCallback { result ->
runOnUiThread {
try {
val secretKey = TOTPHelper.getSecretKey(result.text)
setResult(RESULT_OK, Intent().also {
it.putExtra(IntentKeys.SCANNED_TEXT, secretKey)
})
finish()
} catch (e: Exception) {
MaterialAlertDialogBuilder(this)
.setMessage(e.localizedMessage)
.show()
e.printStackTrace()
}
//Toast.makeText(this, "Scan result: ${it.text}", Toast.LENGTH_LONG).show()
}
}
codeScanner?.errorCallback = ErrorCallback { // or ErrorCallback.SUPPRESS
runOnUiThread {
/* Toast.makeText(
this, "Camera initialization error: ${it.message}",
Toast.LENGTH_LONG
).show()*/
}
}
}
}
override fun onResume() {
super.onResume()
codeScanner?.startPreview()
}
override fun onPause() {
codeScanner?.releaseResources()
super.onPause()
}
override fun onRequestPermissionsResult(
requestCode: Int,
@@ -102,7 +49,7 @@ class ScannerActivity : AppCompatActivity() {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CAM_PERMISSION) {
if (isAllRequestGranted(grantResults)) {
codeScanner?.startPreview()
// codeScanner?.startPreview()
}
}
}

View File

@@ -168,7 +168,7 @@ class BottomNavDrawerFragment :
}
override fun onNavMenuItemClicked(item: NavigationModelItem.NavMenuItem) {
mViewModel.setNavigationMenuItemChecked(item.id)
//mViewModel.setNavigationMenuItemChecked(item.id)
close()
navigationListeners.forEach { it.onNavMenuItemClicked(item) }
}

View File

@@ -87,8 +87,7 @@ class DashboardActivity :
}
}
// DetailActivity.start(this)
AddTOTPActivity.start(this)
DetailActivity.start(this)
}
bottomNavDrawer.apply {
@@ -160,6 +159,9 @@ class DashboardActivity :
val args = HomeFragmentDirections.actionGlobalHomeFragment()
findNavController(R.id.nav_host_fragment).navigate(args)
}
NavigationModel.ADD_TOPT -> {
AddTOTPActivity.start(this)
}
}
}

View File

@@ -9,6 +9,7 @@ object NavigationModel {
const val HOME = 0
const val GENERATE_PASSWORD = 1
const val ADD_TOPT = 2
var navigationMenuItems = mutableListOf(
NavigationModelItem.NavMenuItem(
@@ -22,6 +23,12 @@ object NavigationModel {
icon = R.drawable.ic_twotone_vpn_key_24,
titleRes = R.string.generate_password,
checked = false,
),
NavigationModelItem.NavMenuItem(
id = ADD_TOPT,
icon = R.drawable.ic_twotone_totp,
titleRes = R.string.add_totp,
checked = false,
)
)
}

View File

@@ -8,7 +8,7 @@ import androidx.security.crypto.MasterKey
import com.yogeshpaliyal.keypass.MyApplication
class MySharedPreferences(context: Context) {
private val masterKeyAlias = MasterKey.Builder(MyApplication.instance).also {
private val masterKeyAlias = MasterKey.Builder(context).also {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
// this is equivalent to using deprecated MasterKeys.AES256_GCM_SPEC

View File

@@ -1,34 +0,0 @@
package com.yogeshpaliyal.keypass.utils
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import com.yogeshpaliyal.keypass.MyApplication
/*
CREATED BY : yogesh ON 06/08/20 12:06 PM
*/
class ViewModelFactory() :
ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
/* if (modelClass.isAssignableFrom(SingleNetworkCallViewModel::class.java)) {
return SingleNetworkCallViewModel(apiHelper, dbHelper) as T
}*/
return super.create(modelClass)
}
}
fun <T : AndroidViewModel> ViewModelStoreOwner.initViewModel(viewModel: Class<T>): T =
ViewModelProvider(
this,
ViewModelProvider.AndroidViewModelFactory(MyApplication.instance)
).get(viewModel)
fun <T : ViewModel> ViewModelStoreOwner.initViewModel(viewModel: Class<T>): T = ViewModelProvider(
this,
ViewModelFactory()
).get(viewModel)

View File

@@ -22,26 +22,7 @@
app:navigationIcon="@drawable/ic_baseline_arrow_back_ios_24"
app:title="@string/scanner"/>
<com.budiyev.android.codescanner.CodeScannerView
android:id="@+id/scanner_view"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/toolbar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:autoFocusButtonColor="@android:color/white"
app:autoFocusButtonVisible="true"
app:flashButtonColor="@android:color/white"
app:flashButtonVisible="true"
app:frameColor="@android:color/white"
app:frameCornersSize="50dp"
app:frameCornersRadius="0dp"
app:frameAspectRatioWidth="1"
app:frameAspectRatioHeight="1"
app:frameSize="0.75"
app:frameThickness="2dp"
app:maskColor="#77000000"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -66,4 +66,5 @@
<string name="scanner">Scanner</string>
<string name="alert_black_secret_key">Please enter secret key</string>
<string name="alert_black_account_name">Please enter account name</string>
<string name="add_totp">Add TOTP</string>
</resources>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,6v3l4,-4 -4,-4v3c-4.42,0 -8,3.58 -8,8 0,1.57 0.46,3.03 1.24,4.26L6.7,14.8c-0.45,-0.83 -0.7,-1.79 -0.7,-2.8 0,-3.31 2.69,-6 6,-6zM18.76,7.74L17.3,9.2c0.44,0.84 0.7,1.79 0.7,2.8 0,3.31 -2.69,6 -6,6v-3l-4,4 4,4v-3c4.42,0 8,-3.58 8,-8 0,-1.57 -0.46,-3.03 -1.24,-4.26z"/>
</vector>

View File

@@ -2,7 +2,7 @@
buildscript {
ext{
kotlin_version = "1.5.30"
kotlin_version = "1.5.31"
lifecycle_version = "2.3.1"
room_version = "2.3.0"
navigation_version = '2.3.5'