mirror of
https://github.com/yogeshpaliyal/KeyPass.git
synced 2026-01-05 01:09:40 -06:00
Connected TOTP and the scanner
This commit is contained in:
7
.idea/misc.xml
generated
7
.idea/misc.xml
generated
@@ -5,13 +5,10 @@
|
||||
<map>
|
||||
<entry key="../../../../layout/custom_preview.xml" value="0.20833333333333334" />
|
||||
<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.25" />
|
||||
<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.2" />
|
||||
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/backup_activity.xml" value="0.12635869565217392" />
|
||||
<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/item_accounts.xml" value="0.15" />
|
||||
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/item_totp.xml" value="0.33" />
|
||||
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/item_accounts.xml" value="0.1" />
|
||||
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/xml/backup_preferences.xml" value="0.4287690179806362" />
|
||||
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/production/res/mipmap-anydpi-v26/ic_launcher.xml" value="0.21614583333333334" />
|
||||
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/staging/res/drawable/ic_twotone_content_copy_24.xml" value="0.21614583333333334" />
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.yogeshpaliyal.keypass.constants
|
||||
|
||||
object IntentKeys {
|
||||
const val SCANNED_TEXT = "scanned_text"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.yogeshpaliyal.keypass.constants
|
||||
|
||||
object RequestCodes {
|
||||
const val SCANNER = 342
|
||||
}
|
||||
@@ -16,29 +16,29 @@ import kotlinx.coroutines.flow.Flow
|
||||
abstract class DbDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract fun insertOrUpdateAccount(accountModel: AccountModel)
|
||||
abstract suspend fun insertOrUpdateAccount(vararg accountModel: AccountModel)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract fun insertOrUpdateAccount(accountModel: List<AccountModel>)
|
||||
abstract suspend fun insertOrUpdateAccount(accountModel: List<AccountModel>)
|
||||
|
||||
@Query("SELECT * FROM account ORDER BY title ASC")
|
||||
abstract fun getAllAccounts(): LiveData<List<AccountModel>>
|
||||
|
||||
@Query("SELECT * FROM account ORDER BY title ASC")
|
||||
abstract fun getAllAccountsList(): List<AccountModel>
|
||||
abstract suspend fun getAllAccountsList(): List<AccountModel>
|
||||
|
||||
@Query("SELECT * FROM account WHERE CASE WHEN :tag IS NOT NULL THEN tags = :tag ELSE 1 END AND ((username LIKE '%'||:query||'%' ) OR (title LIKE '%'||:query||'%' ) OR (notes LIKE '%'||:query||'%' )) ORDER BY title ASC")
|
||||
abstract fun getAllAccounts(query: String?, tag: String?): LiveData<List<AccountModel>>
|
||||
|
||||
@Query("SELECT * FROM account WHERE id = :id")
|
||||
abstract fun getAccount(id: Long?): AccountModel?
|
||||
abstract suspend fun getAccount(id: Long?): AccountModel?
|
||||
|
||||
@Query("SELECT DISTINCT tags FROM account")
|
||||
abstract fun getTags(): Flow<List<String>>
|
||||
|
||||
@Query("DELETE from account WHERE id = :id")
|
||||
abstract fun deleteAccount(id: Long?)
|
||||
abstract suspend fun deleteAccount(id: Long?)
|
||||
|
||||
@Delete
|
||||
abstract fun deleteAccount(accountModel: AccountModel)
|
||||
abstract suspend fun deleteAccount(accountModel: AccountModel)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,11 @@ import android.content.Intent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import androidx.activity.viewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.yogeshpaliyal.keypass.R
|
||||
import com.yogeshpaliyal.keypass.constants.IntentKeys
|
||||
import com.yogeshpaliyal.keypass.constants.RequestCodes
|
||||
import com.yogeshpaliyal.keypass.databinding.ActivityAddTotpactivityBinding
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@@ -41,5 +45,18 @@ class AddTOTPActivity : AppCompatActivity() {
|
||||
ScannerActivity.start(this)
|
||||
}
|
||||
|
||||
mViewModel.error.observe(this, Observer {
|
||||
it?.getContentIfNotHandled()?.let {
|
||||
Snackbar.make(binding.root, it, Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
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) ?: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,53 @@
|
||||
package com.yogeshpaliyal.keypass.ui.addTOTP
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.yogeshpaliyal.keypass.AppDatabase
|
||||
import com.yogeshpaliyal.keypass.R
|
||||
import com.yogeshpaliyal.keypass.data.AccountModel
|
||||
import com.yogeshpaliyal.keypass.utils.Event
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class AddTOTPViewModel @Inject constructor(): ViewModel() {
|
||||
class AddTOTPViewModel @Inject constructor(private val appDatabase: AppDatabase): ViewModel() {
|
||||
|
||||
|
||||
private val _goBack = MutableLiveData<Event<Unit>>()
|
||||
val goBack : LiveData<Event<Unit>> = _goBack
|
||||
|
||||
private val _error = MutableLiveData<Event<Int>>()
|
||||
val error : LiveData<Event<Int>> = _error
|
||||
|
||||
val secretKey = MutableLiveData<String>("")
|
||||
|
||||
val accountName = MutableLiveData<String>("")
|
||||
|
||||
fun saveAccount(){
|
||||
viewModelScope.launch {
|
||||
val secretKey = secretKey.value
|
||||
val accountName = accountName.value
|
||||
|
||||
if (accountName.isNullOrEmpty()){
|
||||
_error.postValue(Event(R.string.alert_black_secret_key))
|
||||
return@launch
|
||||
}
|
||||
|
||||
if (accountName.isNullOrEmpty()){
|
||||
_error.postValue(Event(R.string.alert_black_secret_key))
|
||||
return@launch
|
||||
}
|
||||
|
||||
val accountModel = AccountModel(password = secretKey, title = accountName)
|
||||
appDatabase.getDao().insertOrUpdateAccount(accountModel)
|
||||
}
|
||||
}
|
||||
|
||||
fun setSecretKey(secretKey: String){
|
||||
this.secretKey.value = secretKey
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.yogeshpaliyal.keypass.ui.addTOTP
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
@@ -11,6 +12,8 @@ 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.yogeshpaliyal.keypass.constants.IntentKeys
|
||||
import com.yogeshpaliyal.keypass.constants.RequestCodes
|
||||
import com.yogeshpaliyal.keypass.databinding.ActivityScannerBinding
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@@ -25,9 +28,9 @@ class ScannerActivity : AppCompatActivity() {
|
||||
|
||||
companion object{
|
||||
@JvmStatic
|
||||
fun start(context: Context) {
|
||||
val starter = Intent(context, ScannerActivity::class.java)
|
||||
context.startActivity(starter)
|
||||
fun start(activity: Activity) {
|
||||
val starter = Intent(activity, ScannerActivity::class.java)
|
||||
activity.startActivityForResult(starter,RequestCodes.SCANNER)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,17 +53,21 @@ class ScannerActivity : AppCompatActivity() {
|
||||
} else {
|
||||
codeScanner?.startPreview()
|
||||
// Callbacks
|
||||
codeScanner?.decodeCallback = DecodeCallback {
|
||||
codeScanner?.decodeCallback = DecodeCallback { result ->
|
||||
runOnUiThread {
|
||||
Toast.makeText(this, "Scan result: ${it.text}", Toast.LENGTH_LONG).show()
|
||||
setResult(RESULT_OK, Intent().also {
|
||||
it.putExtra(IntentKeys.SCANNED_TEXT, result.text)
|
||||
})
|
||||
finish()
|
||||
//Toast.makeText(this, "Scan result: ${it.text}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
codeScanner?.errorCallback = ErrorCallback { // or ErrorCallback.SUPPRESS
|
||||
runOnUiThread {
|
||||
Toast.makeText(
|
||||
/* Toast.makeText(
|
||||
this, "Camera initialization error: ${it.message}",
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
).show()*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class DashboardActivity :
|
||||
|
||||
private val mViewModel by viewModels<DashboardViewModel>()
|
||||
|
||||
val currentNavigationFragment: Fragment?
|
||||
private val currentNavigationFragment: Fragment?
|
||||
get() = supportFragmentManager.findFragmentById(R.id.nav_host_fragment)
|
||||
?.childFragmentManager
|
||||
?.fragments
|
||||
|
||||
27
app/src/main/java/com/yogeshpaliyal/keypass/utils/Event.kt
Normal file
27
app/src/main/java/com/yogeshpaliyal/keypass/utils/Event.kt
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.yogeshpaliyal.keypass.utils
|
||||
|
||||
/*Used as a wrapper for data that is exposed via a LiveData that represents an
|
||||
event.*/
|
||||
|
||||
open class Event<out T>(private val content: T) {
|
||||
|
||||
var hasBeenHandled = false
|
||||
private set // Allow external read but not write
|
||||
|
||||
/**
|
||||
* Returns the content and prevents its use again.
|
||||
*/
|
||||
fun getContentIfNotHandled(): T? {
|
||||
return if (hasBeenHandled) {
|
||||
null
|
||||
} else {
|
||||
hasBeenHandled = true
|
||||
content
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content, even if it's already been handled.
|
||||
*/
|
||||
fun peekContent(): T = content
|
||||
}
|
||||
@@ -41,6 +41,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Secret Key"
|
||||
android:text="@{mViewModel.secretKey}"
|
||||
android:id="@+id/etSecretKey"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
@@ -58,6 +59,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/account_name"
|
||||
android:text="@{mViewModel.accountName}"
|
||||
android:id="@+id/etAccount"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
@@ -69,6 +71,8 @@
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_margin="@dimen/grid_2"
|
||||
android:text="Save"
|
||||
android:id="@+id/btnSave"
|
||||
android:onClick="@{()->mViewModel.saveAccount()}"
|
||||
app:icon="@drawable/ic_round_done_24"
|
||||
android:textColor="?attr/colorOnSecondary"/>
|
||||
|
||||
|
||||
@@ -64,4 +64,6 @@
|
||||
<string name="enabled">Enabled</string>
|
||||
<string name="override_auto_backup_file">Override Auto Backup File</string>
|
||||
<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>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user