Skip to content

Commit d4ed3be

Browse files
authored
Merge pull request #8 from Ladsers/developing
25.2.0
2 parents 72eabb1 + 15b23af commit d4ed3be

34 files changed

+731
-49
lines changed

NOTICE.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Passtable (Android Application)
2-
Copyright 2024 Maxim Korolev
2+
Copyright 2025 Maxim Korolev
33
<br /><br />
44
### This software includes binary files and/or source codes from third party:
55

@@ -31,6 +31,11 @@ Web site: https://manropefont.com <br />
3131
SIL Open Font License: https://raw.githubusercontent.com/sharanda/manrope/master/OFL.txt <br />
3232
<br />
3333

34+
**IBM Plex Sans** <br />
35+
*IBM Corp., Mike Abbink, Bold Monday* <br />
36+
Web site: https://www.ibm.com/plex/<br />
37+
SIL Open Font License: https://raw.githubusercontent.com/IBM/plex/master/LICENSE.txt <br />
38+
<br />
3439

3540
**Overpass Mono** <br />
3641
*Delve Withrington, Dave Bailey, Thomas Jockin* <br />

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ android {
1515
applicationId "com.ladsers.passtable.android"
1616
minSdk 24
1717
targetSdk 34
18-
versionCode 10
19-
versionName "24.3.2"
18+
versionCode 11
19+
versionName "25.2.0"
2020

2121
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
2222
}

app/src/main/java/com/ladsers/passtable/android/activities/EditActivity.kt

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import android.widget.Button
1111
import android.widget.EditText
1212
import androidx.appcompat.app.AppCompatActivity
1313
import androidx.core.content.ContextCompat
14+
import androidx.core.content.res.ResourcesCompat
1415
import androidx.core.widget.doAfterTextChanged
1516
import androidx.core.widget.doBeforeTextChanged
1617
import com.google.android.material.button.MaterialButton
@@ -109,9 +110,24 @@ class EditActivity : AppCompatActivity() {
109110
}
110111
}
111112

112-
editTextBehavior(binding.etNote, binding.btUndoNote, originalNote)
113-
editTextBehavior(binding.etUsername, binding.btUndoUsername, originalUsername)
114-
editTextBehavior(binding.etPassword, binding.btUndoPassword, originalPassword)
113+
editTextBehavior(
114+
editText = binding.etNote,
115+
button = binding.btUndoNote,
116+
originalVal = originalNote,
117+
isNeedChangeFont = true
118+
)
119+
editTextBehavior(
120+
editText = binding.etUsername,
121+
button = binding.btUndoUsername,
122+
originalVal = originalUsername,
123+
isNeedChangeFont = true
124+
)
125+
editTextBehavior(
126+
editText = binding.etPassword,
127+
button = binding.btUndoPassword,
128+
originalVal = originalPassword,
129+
isNeedChangeFont = false
130+
)
115131

116132
if (resources.configuration.keyboard == Configuration.KEYBOARD_QWERTY) binding.etNote.requestFocus()
117133

@@ -143,10 +159,24 @@ class EditActivity : AppCompatActivity() {
143159
canBeSavedCheck()
144160
}
145161

146-
private fun editTextBehavior(editText: EditText, button: Button, originalVal: String) {
162+
private fun editTextBehavior(
163+
editText: EditText,
164+
button: Button,
165+
originalVal: String,
166+
isNeedChangeFont: Boolean
167+
) {
147168
editText.post { editText.setText(originalVal) }
148169

149170
editText.doAfterTextChanged { x ->
171+
172+
if (isNeedChangeFont) {
173+
val isNotEmpty = x.toString().isNotEmpty()
174+
editText.typeface = ResourcesCompat.getFont(
175+
this,
176+
if (isNotEmpty) R.font.ibmplexsans_regular else R.font.manrope
177+
)
178+
}
179+
150180
binding.clErr.removeCallbacks(doNotMatchMsgWithDelay)
151181
button.isEnabled = editMode && x.toString() != originalVal
152182
canBeSavedCheck()

app/src/main/java/com/ladsers/passtable/android/activities/MainActivity.kt

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import com.ladsers.passtable.android.R
2222
import com.ladsers.passtable.android.adapters.RecentAdapter
2323
import com.ladsers.passtable.android.components.ClipboardManager
2424
import com.ladsers.passtable.android.components.PasswordGeneratorProcessor
25+
import com.ladsers.passtable.android.components.ProjectSupportProcessor
26+
import com.ladsers.passtable.android.components.ShareManager
2527
import com.ladsers.passtable.android.components.SnackbarManager
2628
import com.ladsers.passtable.android.components.menus.MainMenu
2729
import com.ladsers.passtable.android.enums.Param
@@ -30,6 +32,8 @@ import com.ladsers.passtable.android.containers.RecentFiles
3032
import com.ladsers.passtable.android.databinding.ActivityMainBinding
3133
import com.ladsers.passtable.android.dialogs.FileCreatorDlg
3234
import com.ladsers.passtable.android.dialogs.MessageDlg
35+
import com.ladsers.passtable.android.enums.RecentFileStatus
36+
import com.ladsers.passtable.android.enums.RecentItemPopupAction
3337

3438
class MainActivity : AppCompatActivity() {
3539
private lateinit var binding: ActivityMainBinding
@@ -102,7 +106,13 @@ class MainActivity : AppCompatActivity() {
102106
{ id, resCode -> popupAction(id, resCode) })
103107
binding.rvRecent.adapter = adapter
104108

105-
showInfoLicense()
109+
if (handleFirstLaunch()) {
110+
// if the app is opened for the first time
111+
ProjectSupportProcessor.updateState(this)
112+
} else {
113+
// if the app is opened after updating
114+
ProjectSupportProcessor.boostCounterIfZero(this)
115+
}
106116
}
107117

108118
override fun onResume() {
@@ -167,21 +177,23 @@ class MainActivity : AppCompatActivity() {
167177
startActivity(intent)
168178
}
169179

170-
private fun openRecentFile(id: Int, resCode: Int) {
171-
when (resCode) {
172-
0 -> { // ok
180+
private fun openRecentFile(id: Int, status: RecentFileStatus) {
181+
when (status) {
182+
RecentFileStatus.OK -> {
173183
val intent = Intent(this, TableActivity::class.java)
174184
intent.putExtra("fileUri", recentUri[id])
175185
intent.putExtra("newFile", false)
176186
startActivity(intent)
177187
}
178-
1 -> { // file from google disk is lost / not available
188+
189+
RecentFileStatus.GDRIVE_FILE_NOT_AVAILABLE -> {
179190
refreshRecentList()
180191
Toast.makeText(
181192
this, getString(R.string.ui_msg_recentFilesUpdated), Toast.LENGTH_SHORT
182193
).show()
183194
}
184-
2 -> { // local file is lost
195+
196+
RecentFileStatus.LOCAL_FILE_NOT_AVAILABLE -> {
185197
messageDlg.create(
186198
getString(R.string.dlg_title_cannotBeOpened),
187199
getString(R.string.dlg_err_couldNotOpenRecentFile)
@@ -196,12 +208,11 @@ class MainActivity : AppCompatActivity() {
196208
}
197209
}
198210

199-
private fun popupAction(id: Int, resCode: Int) {
200-
when (resCode) {
201-
1 -> { // remove from list
202-
removeFromRecentList(id)
203-
}
204-
2 -> { // forget password
211+
private fun popupAction(id: Int, action: RecentItemPopupAction) {
212+
when (action) {
213+
RecentItemPopupAction.SHARE_FILE -> ShareManager.shareFile(this, recentUri[id])
214+
RecentItemPopupAction.REMOVE_FROM_LIST -> removeFromRecentList(id)
215+
RecentItemPopupAction.DISABLE_BIOMETRIC -> {
205216
RecentFiles.forgetPasswordEncrypted(this, recentUri[id])
206217
recentPasswords[id] = false
207218
adapter.notifyItemChanged(id)
@@ -236,10 +247,12 @@ class MainActivity : AppCompatActivity() {
236247
if (recentUri.isEmpty()) View.VISIBLE else View.GONE
237248
}
238249

239-
private fun showInfoLicense() {
250+
private fun handleFirstLaunch(): Boolean {
240251
val param = Param.INITIAL_INFO_LICENSE
241-
if (!ParamStorage.getBool(this, param)) return
252+
if (!ParamStorage.getBool(this, param)) return false
242253
val info = getString(R.string.app_info_license)
254+
// show license info
243255
SnackbarManager.showInitInfo(this, binding.root, param, info, 4000)
256+
return true
244257
}
245258
}

app/src/main/java/com/ladsers/passtable/android/activities/TableActivity.kt

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ import com.ladsers.passtable.android.callbacks.ReorderCallback
2626
import com.ladsers.passtable.android.callbacks.SearchDiffCallback
2727
import com.ladsers.passtable.android.components.BackupManager
2828
import com.ladsers.passtable.android.components.BiometricAuth
29+
import com.ladsers.passtable.android.components.PasswordUserValidator
30+
import com.ladsers.passtable.android.components.ProjectSupportProcessor
2931
import com.ladsers.passtable.android.components.Searcher
32+
import com.ladsers.passtable.android.components.ShareManager
3033
import com.ladsers.passtable.android.components.menus.DataItemMenu
3134
import com.ladsers.passtable.android.components.tableActivity.TableInitInfo
3235
import com.ladsers.passtable.android.containers.DataTableAndroid
@@ -222,7 +225,21 @@ class TableActivity : AppCompatActivity() {
222225
RecentFiles.add(this, mainUri)
223226
val passEncrypted = RecentFiles.getLastPasswordEncrypted(this)
224227
if (passEncrypted.isNullOrBlank()) primaryPasswordDlg.show(PrimaryPasswordDlg.Mode.OPEN)
225-
else biometricAuth.startAuth(passEncrypted)
228+
else {
229+
val needValidate = PasswordUserValidator.isNeedValidate(this)
230+
if (needValidate) {
231+
PasswordUserValidator.showValidationDialog(
232+
messageDlg = messageDlg,
233+
activity = this,
234+
enterPasswordAction = {
235+
primaryPasswordDlg.show(
236+
PrimaryPasswordDlg.Mode.OPEN,
237+
canRememberPass = false
238+
)
239+
},
240+
skipAction = { biometricAuth.startAuth(passEncrypted) })
241+
} else biometricAuth.startAuth(passEncrypted)
242+
}
226243
} else primaryPasswordDlg.show(PrimaryPasswordDlg.Mode.OPEN, canRememberPass = false)
227244
}
228245

@@ -246,6 +263,9 @@ class TableActivity : AppCompatActivity() {
246263
table = DataTableAndroid(mainUri.toString(), primaryPassword, cryptData, contentResolver)
247264
when (table.fill()) {
248265
0 -> {
266+
if (primaryPasswordDlg.passwordEnteredManually)
267+
PasswordUserValidator.handleSuccessValidation(this)
268+
249269
if (primaryPasswordDlg.isNeedRememberPassword)
250270
biometricAuth.activateAuth(primaryPassword)
251271
else loginCompleted()
@@ -305,6 +325,8 @@ class TableActivity : AppCompatActivity() {
305325
/* Notify user */
306326
TableInitInfo.showKeyboardShortcuts(this, binding)
307327
notifyUser()
328+
329+
ProjectSupportProcessor.updateState(this)
308330
}
309331

310332
override fun onCreateOptionsMenu(menu: Menu): Boolean {
@@ -319,6 +341,10 @@ class TableActivity : AppCompatActivity() {
319341
addItem()
320342
true
321343
}
344+
R.id.btShare -> {
345+
ShareManager.shareFile(this, mainUri)
346+
true
347+
}
322348
R.id.btSaveAs -> {
323349
disableLockFileSystem = true
324350
saveAsMode = true

app/src/main/java/com/ladsers/passtable/android/adapters/RecentAdapter.kt

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import androidx.core.content.ContextCompat
1111
import androidx.recyclerview.widget.RecyclerView
1212
import com.ladsers.passtable.android.R
1313
import com.ladsers.passtable.android.databinding.ItemRecentFileBinding
14+
import com.ladsers.passtable.android.enums.RecentFileStatus
15+
import com.ladsers.passtable.android.enums.RecentItemPopupAction
1416
import com.ladsers.passtable.android.extensions.getFileName
1517
import java.text.SimpleDateFormat
1618
import java.time.LocalDateTime
@@ -24,8 +26,8 @@ class RecentAdapter(
2426
private val recentDate: MutableList<String>,
2527
private val recentPasswords: MutableList<Boolean>, // having encrypted primary passwords?
2628
private val contextActivity: Context,
27-
private val open: (Int, Int) -> Unit,
28-
private val popupAction: (Int, Int) -> Unit,
29+
private val open: (Int, RecentFileStatus) -> Unit,
30+
private val popupAction: (Int, RecentItemPopupAction) -> Unit,
2931
) : RecyclerView.Adapter<RecentAdapter.ItemViewHolder>() {
3032

3133
class ItemViewHolder(val binding: ItemRecentFileBinding) :
@@ -40,6 +42,7 @@ class RecentAdapter(
4042
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
4143
with(holder) {
4244
val fileName = contextActivity.getFileName(recentUri[position]) ?: "???"
45+
4346
binding.tvFileName.text = fileName
4447
binding.tvLastDate.text = getFormattedDate(recentDate[position])
4548

@@ -53,18 +56,18 @@ class RecentAdapter(
5356
binding.ivGdrive.visibility = View.GONE
5457
}
5558

56-
val openCode = when (true) {
57-
(fileName != "???") -> 0
58-
(fileName == "???" && gdriveFile) -> 1
59-
else -> 2
60-
}
61-
binding.clItem.setOnClickListener { open(position, openCode) }
59+
val canBeOpened = fileName != "???"
60+
val recentFileStatus = if (canBeOpened) RecentFileStatus.OK
61+
else if (gdriveFile) RecentFileStatus.GDRIVE_FILE_NOT_AVAILABLE
62+
else RecentFileStatus.LOCAL_FILE_NOT_AVAILABLE
63+
64+
binding.clItem.setOnClickListener { open(position, recentFileStatus) }
6265
binding.clItem.setOnLongClickListener {
6366
it.performHapticFeedback(
6467
HapticFeedbackConstants.VIRTUAL_KEY,
6568
HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING
6669
)
67-
showPopupMenu(it, position)
70+
showPopupMenu(it, recentFileStatus, position)
6871
}
6972
}
7073
}
@@ -97,7 +100,7 @@ class RecentAdapter(
97100
return result
98101
}
99102

100-
private fun showPopupMenu(view: View, position: Int): Boolean {
103+
private fun showPopupMenu(view: View, status: RecentFileStatus, position: Int): Boolean {
101104
val pop = PopupMenu(
102105
contextActivity, view, Gravity.CENTER, 0,
103106
R.style.PopupMenuCustomPosRecent
@@ -113,11 +116,23 @@ class RecentAdapter(
113116
val spanStr = SpannableString(btRemoveFromList.title.toString())
114117
spanStr.setSpan(ForegroundColorSpan(colorNegative), 0, spanStr.length, 0)
115118
btRemoveFromList.title = spanStr
119+
val btShare = pop.menu.findItem(R.id.btShare)
120+
btShare.isVisible = status == RecentFileStatus.OK
121+
btShare.isEnabled = status == RecentFileStatus.OK
116122

117123
pop.setOnMenuItemClickListener { item ->
118124
when (item.itemId) {
119-
R.id.btRemoveFromList -> popupAction(position, 1)
120-
R.id.btDisableBiometric -> popupAction(position, 2)
125+
R.id.btRemoveFromList -> popupAction(
126+
position,
127+
RecentItemPopupAction.REMOVE_FROM_LIST
128+
)
129+
130+
R.id.btDisableBiometric -> popupAction(
131+
position,
132+
RecentItemPopupAction.DISABLE_BIOMETRIC
133+
)
134+
135+
R.id.btShare -> popupAction(position, RecentItemPopupAction.SHARE_FILE)
121136
}
122137
true
123138
}

app/src/main/java/com/ladsers/passtable/android/components/BiometricAuth.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ class BiometricAuth(
112112
val outStr = "$iv@$data"
113113
strToBiometricPrompt = null
114114
if (RecentFiles.rememberLastPasswordEncrypted(context, outStr)) {
115+
RecentFiles.rememberLastVerificationDate(context)
115116
Toast.makeText(
116117
context,
117118
context.getString(R.string.ui_msg_biometricEnabled),

0 commit comments

Comments
 (0)