mirror of
https://github.com/yogeshpaliyal/KeyPass.git
synced 2026-02-18 01:18:29 -06:00
Final wrapped up TOTP
Compatible to android 12
This commit is contained in:
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
@@ -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" />
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
10
app/src/staging/res/drawable/ic_twotone_totp.xml
Normal file
10
app/src/staging/res/drawable/ic_twotone_totp.xml
Normal 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>
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user