Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3a92ed6
refactor: data folder
JeelDobariya38 Nov 17, 2025
e4fd22c
chore(deps): add a datastore dependency
JeelDobariya38 Nov 19, 2025
85a98a9
Revert "chore(deps): add a datastore dependency"
JeelDobariya38 Nov 19, 2025
1e196e3
chore: improve build script
JeelDobariya38 Nov 19, 2025
162d729
chore(deps): add datastore preference as dependency
JeelDobariya38 Nov 19, 2025
a9b120a
chore(deps): okhttp 5.3.0 -> 5.3.2
JeelDobariya38 Nov 19, 2025
fb69f9b
feat: make a datastore for feature flags
JeelDobariya38 Nov 19, 2025
6eb481e
feat: use feature flag datastore
JeelDobariya38 Nov 19, 2025
3d6f63d
chore: remove toggling feature flag function
JeelDobariya38 Nov 20, 2025
44b8915
feat: made password manager activity stateflow based
JeelDobariya38 Nov 20, 2025
ebe5b94
fix the error with preview feature toggle
JeelDobariya38 Nov 20, 2025
af1f30b
feat: made a app setting datapreference
JeelDobariya38 Nov 20, 2025
60be334
refactor: add defualt value for feature flags settings
JeelDobariya38 Nov 20, 2025
429efc0
feat: add a defualt value
JeelDobariya38 Nov 20, 2025
f356283
feat: use the datastore for the theme app setting
JeelDobariya38 Nov 20, 2025
d88c2fb
Merge branch 'main' into jeel-dev
JeelDobariya38 Nov 20, 2025
50a32e3
chore: unverified solution for data store migration....
JeelDobariya38 Nov 21, 2025
fc77f01
feat: change datastore file name
JeelDobariya38 Nov 21, 2025
49fa7cb
feat: prepare for unofficial release in telegram community
JeelDobariya38 Nov 21, 2025
ae5ceba
fix: icons of staging & debug build
JeelDobariya38 Nov 21, 2025
3b4c196
chore: Add a shared git hooks
JeelDobariya38 Nov 21, 2025
ccd20dd
feat: add conventional git commit hook
JeelDobariya38 Nov 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .githooks/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env bash

# Path to the commit message file (provided by Git).
COMMIT_MSG_FILE=$1

# Ignore automatic commit messages containing ' into ' or 'Merge'.
if grep --quiet --extended-regexp " into |^Merge " "$COMMIT_MSG_FILE"; then
exit 0
fi

# Read the commit message from the file.
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")

CONVENTIONAL_COMMIT_REGEX='^(feat|fix|docs|style|refactor|test|chore|build|ci|perf|revert)(\([a-z0-9_.-]+\))?(!)?:\s.*$'

# Check if the commit message matches the regex.
if ! [[ $COMMIT_MSG =~ $CONVENTIONAL_COMMIT_REGEX ]]; then
echo "ERROR: Commit message does not follow Conventional Commits format."
echo
echo "The commit message should be structured as follows:"
echo "<type>(<optional scope>): <description>"
echo "[optional body]"
echo "[optional footer(s)]"
echo
echo "Valid types are:"
echo " feat: A new feature."
echo " fix: A bug fix."
echo " docs: Documentation changes."
echo " style: Code style changes (formatting, missing semicolons, etc.)."
echo " refactor: Code refactoring (neither fixes a bug nor adds a feature)."
echo " test: Adding or updating tests."
echo " chore: Routine tasks like updating dependencies or build tools."
echo " build: Changes affecting the build system or external dependencies."
echo " ci: Changes to CI configuration files or scripts."
echo " perf: Performance improvements."
echo " revert: Reverting a previous commit."
echo
echo "Examples:"
echo " feat(auth): add login functionality"
echo " fix(api)!: resolve timeout issue"
echo " docs(readme): update installation instructions"
echo
exit 1
fi

exit 0

45 changes: 22 additions & 23 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.jetbrains.kotlin.serialization)
alias(libs.plugins.ksp)
alias(libs.plugins.oss.licenses)
}

kotlin {
compilerOptions {
jvmTarget = JvmTarget.JVM_21
jvmTarget.set(JvmTarget.JVM_21)
}
}

Expand All @@ -28,7 +29,7 @@ android {
minSdk = 26
targetSdk = 34
versionCode = 2
versionName = "v1.1.2-rc.1"
versionName = "v1.1.2-rc.2"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down Expand Up @@ -90,6 +91,8 @@ android {
// throw GradleException("Can't Sign Release Build")
}

isDebuggable = false
isShrinkResources = true
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")

Expand All @@ -101,10 +104,13 @@ android {
getByName("debug") {
applicationIdSuffix = ".dev"
versionNameSuffix = "-Dev"

isDebuggable = true
isShrinkResources = false
isMinifyEnabled = false

manifestPlaceholders["appIcon"] = "@mipmap/dev_ic_launcher"
manifestPlaceholders["appLabel"] = "Passcodes Dev"
manifestPlaceholders["appLabel"] = "Passcodes-Dev"
}

create("staging") {
Expand All @@ -118,35 +124,25 @@ android {
applicationIdSuffix = ".staging"
versionNameSuffix = "-Staging"

isMinifyEnabled = true
isShrinkResources = true
isDebuggable = false
isShrinkResources = true
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")

manifestPlaceholders["appIcon"] = "@mipmap/dev_ic_launcher"
manifestPlaceholders["appLabel"] = "Passcodes Staging"
manifestPlaceholders["appLabel"] = "Passcodes-Staging"
}
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}

buildFeatures {
viewBinding = true
buildConfig = true
compose = true
}
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}

buildFeatures {
viewBinding = true
buildConfig = true
compose = true
}

Expand Down Expand Up @@ -194,6 +190,9 @@ dependencies {
// Dependency Injection
implementation(libs.bundles.koin)

// Datastore Preferences
implementation(libs.bundles.datastore.preferences)


// --- Testing ---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import android.service.autofill.SaveRequest
import android.view.autofill.AutofillValue
import android.widget.RemoteViews
import com.jeeldobariya.passcodes.R
import com.jeeldobariya.passcodes.data.Passcode
import com.jeeldobariya.passcodes.data.PasscodeDatabase
import com.jeeldobariya.passcodes.autofill.data.Passcode
import com.jeeldobariya.passcodes.autofill.data.PasscodeDatabase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.jeeldobariya.passcodes.data
package com.jeeldobariya.passcodes.autofill.data

import androidx.room.Entity
import androidx.room.PrimaryKey
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.jeeldobariya.passcodes.data
package com.jeeldobariya.passcodes.autofill.data

import androidx.room.Dao
import androidx.room.Delete
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.jeeldobariya.passcodes.data
package com.jeeldobariya.passcodes.autofill.data

import android.content.Context
import androidx.room.Database
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.jeeldobariya.passcodes.flags

// This file contains a solution as commented code... ignore it.. if you don't entertain it.


import android.content.Context
// import androidx.datastore.core.DataMigration
import androidx.datastore.dataStore
import kotlinx.serialization.Serializable

val Context.featureFlagsDatastore by dataStore(
fileName = "feature-flags-settings.json",
serializer = FeatureFlagsSettingsSerializer,
// produceMigrations = { listOf(DataStoreMigration1_2) }
)

@Serializable
data class FeatureFlagsSettings(
val version: Int = 1,
val isPreviewFeaturesEnabled: Boolean = false,
val isPreviewLayoutEnabled: Boolean = false
)

/*
Let say we have added a new feature flag, for custom backends..
like this in Feature Flag Settings.

```kotlin
@Serializable
data class FeatureFlagsSettings(
val version: Int = 2,
val isPreviewFeaturesEnabled: Boolean = false,
val isPreviewLayoutEnabled: Boolean = false,
val isCustomBackendFeatureEnabled: Boolean = false,
)
```

Then we can create a migration like this, and data will be migrated

```kotlin
data object DataStoreMigration1_2: DataMigration<FeatureFlagsSettings> {
override suspend fun cleanUp() {
// I don't think this is need in our case (according to rules written below..)
TODO("Not yet implemented")
}

override suspend fun migrate(currentData: FeatureFlagsSettings): FeatureFlagsSettings {
return FeatureFlagsSettings(
// this is previous version flags this will be need to restore users current data.
isPreviewFeaturesEnabled = currentData.isPreviewFeaturesEnabled,
isPreviewLayoutEnabled = currentData.isPreviewLayoutEnabled,

// this is new flag has already have default value, but we will explicitly declare it for clarity reasons....
isCustomBackendFeatureEnabled = false

// [!WARNING]
// this should not restore version field from current data....
// because if we do `version = currentData.version` then it will break the system in next run of migrations.
)
}

override suspend fun shouldMigrate(currentData: FeatureFlagsSettings): Boolean {
return currentData.version < 2
}
}
```

## Conclusion:

I don't know weather this will work or not... let say it will..
we will eventually find out...

Here I am just proposing the solution that work perfectly in my mind..
I have not tested this solution nor do, I have seen this anywhere so I don't know weather this work or not..
But here are some links that I have refer to, while coming up with the solution...

- https://medium.com/androiddevelopers/datastore-and-data-migration-fdca806eb1aa
- https://stackoverflow.com/questions/69457920/how-to-perform-version-migrations-in-android-jetpack-datastore
- https://github.com/yogeshpaliyal/KeyPass/blob/master/common/src/main/java/com/yogeshpaliyal/common/utils/SharedPreferenceUtils.kt
- https://github.com/PasscodesApp/Passcodes/pull/52

they all use preferenceDataStore. which i don;t entertain using... because i guess it too complex..

### Rules (Assumptions):

- To this solution to work we can only add the new field...
- we can't change existing field nor do we can change it's datatype.
- we need to provide the default value to each and every field...
- we can only extend it can;t modify it....

**Main Assumption**: the under-lying datastore androidx library, will pass the currentData in `migrate()` function..
with old field restore and new field will have default value.

I think for this use case, we won;t need to modify field, so this solution work here..
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.jeeldobariya.passcodes.flags

import androidx.datastore.core.Serializer
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import java.io.InputStream
import java.io.OutputStream

object FeatureFlagsSettingsSerializer: Serializer<FeatureFlagsSettings> {
override val defaultValue: FeatureFlagsSettings
get() = FeatureFlagsSettings()

override suspend fun readFrom(input: InputStream): FeatureFlagsSettings {
return try {
Json.decodeFromString(
deserializer = FeatureFlagsSettings.serializer(),
string = input.readBytes().decodeToString(),
)
} catch (e: SerializationException) {
e.printStackTrace()
defaultValue
}
}

override suspend fun writeTo(
t: FeatureFlagsSettings,
output: OutputStream
) {
output.write(
Json.encodeToString(
serializer = FeatureFlagsSettings.serializer(),
value = t
).encodeToByteArray()
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.toUri
import com.jeeldobariya.passcodes.databinding.ActivityAboutUsBinding
import com.jeeldobariya.passcodes.utils.CommonUtils
import com.jeeldobariya.passcodes.utils.Constant
import com.jeeldobariya.passcodes.utils.appDatastore
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking

class AboutUsActivity : AppCompatActivity() {

private lateinit var binding: ActivityAboutUsBinding

override fun onCreate(savedInstanceState: Bundle?) {
CommonUtils.updateCurrTheme(this)
runBlocking {
setTheme(appDatastore.data.first().theme)
}
super.onCreate(savedInstanceState)
binding = ActivityAboutUsBinding.inflate(layoutInflater)
setContentView(binding.root)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
import com.jeeldobariya.passcodes.databinding.ActivityLicenseBinding
import com.jeeldobariya.passcodes.utils.CommonUtils
import com.jeeldobariya.passcodes.utils.appDatastore
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import java.io.BufferedReader
import java.io.InputStreamReader

class LicenseActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
CommonUtils.updateCurrTheme(this)
runBlocking {
setTheme(appDatastore.data.first().theme)
}
super.onCreate(savedInstanceState)
var binding = ActivityLicenseBinding.inflate(layoutInflater)
val binding = ActivityLicenseBinding.inflate(layoutInflater)
setContentView(binding.root)

try {
Expand Down
Loading
Loading