Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
f5b8f77
feat(ui): Material Design 3 Expressive Redesign
xLexip May 21, 2025
2966150
chore: Apply minor dependency updates
xLexip Oct 30, 2025
afd37cd
chore: Update sonarqube to v7
xLexip Oct 30, 2025
b92b717
chore(renovate): Add renovate label to merge requests
xLexip Oct 30, 2025
89c6c3e
chore(config): migrate config renovate.json
renovate[bot] Oct 31, 2025
cc6291a
chore(deps): update gradle to v9
renovate[bot] Oct 31, 2025
af3d5aa
#45: feat(ui): Implement permission-error dialog
xLexip Oct 30, 2025
66870e1
feat(ux): Implement haptic feedback for the main service switch
xLexip Oct 30, 2025
4c5f4ff
#45: refactor(ux): Closable permission-error dialog
xLexip Oct 30, 2025
610d950
refactor(ui): Fix sonarqube findings
xLexip Oct 31, 2025
4157cb9
Merge pull request #46 from xLexip/#45
xLexip Oct 31, 2025
631fe59
feat: Integrate Firebase Crashlytics
xLexip Oct 31, 2025
83943e6
refactor: Update app branding
xLexip Nov 1, 2025
52fa72d
refactor(ui): Organize service toggling logic
xLexip Nov 1, 2025
c1e0a90
chore(ci): Limit build job permissions
xLexip Nov 1, 2025
4d59e88
chore: Add PRIVACY_POLICY.md
xLexip Oct 31, 2025
1f87280
feat(data): Implement a threshold lux enum
xLexip Nov 2, 2025
98eb161
feat(services): Consider live threshold changes while the service is …
xLexip Nov 2, 2025
5b432e3
feat(ui): Implement lux threshold slider
xLexip Nov 2, 2025
4c51f92
chore(ci): Run SonarCloud analysis on all branches
xLexip Nov 2, 2025
713f7d4
refactor: Address SonarCloud findings
xLexip Nov 2, 2025
d33278c
Merge branch '#31' into develop
xLexip Nov 2, 2025
90c1a5a
chore(deps): update CI actions to v5
renovate[bot] Nov 2, 2025
e1de37a
fix: Catch mysterious unsupported Android versions lower than minSDK
xLexip Dec 5, 2025
07bc8a2
feat: Add translations for 48 languages
xLexip Dec 5, 2025
5d4545f
feat(res): Optimize strings and DE translation
xLexip Dec 5, 2025
fad4927
feat: Implement per-app language preferences
xLexip Dec 5, 2025
b6df622
chore(gradle): Create beta build type
xLexip Dec 5, 2025
f4e20ea
chore: Don't support tablets
xLexip Dec 5, 2025
b4448e3
chore(analytics): Add analytics to the setup/permission dialog
xLexip Dec 5, 2025
c662c50
chore(gradle): Set NDK debug symbol level to full
xLexip Dec 5, 2025
c72a723
chore: Disable language splitting
xLexip Dec 5, 2025
29d79f9
Revert "fix: Remove translations for Indonesian and Hebrew"
xLexip Dec 5, 2025
35bcdb6
feat: Implement in-app update prompts
xLexip Dec 5, 2025
13eada5
fix: Make adaptive theme screen scrollable on small devices
xLexip Dec 5, 2025
9976fcb
feat: Implement setup wizard
xLexip Dec 5, 2025
dc2dbb7
fix(deps): apply minor dependency updates
renovate[bot] Dec 5, 2025
a399965
fix: Remove translations for Indonesian and Hebrew
xLexip Dec 5, 2025
6d6c247
chore: Minor updates and v0.6.0 beta
xLexip Dec 5, 2025
70fa87f
feat: Implement custom thresholds
xLexip Dec 5, 2025
e4d4117
chore: Create FUNDING.yml
xLexip Dec 5, 2025
7fc3d78
chore(analytics): Log system theme changes
xLexip Dec 5, 2025
1413a9b
refactor: Change about action to changeable URL
xLexip Dec 5, 2025
5ae683a
style: Optimize custom threshold dialog
xLexip Dec 5, 2025
ff5b71e
refactor: Remove out-of-context toasts
xLexip Dec 5, 2025
fe52cf1
feat(ui): Implement current brightness indication
xLexip Dec 5, 2025
508c955
fix(util): Use SENSOR_DELAY_FASTEST to reduce theme switching delay
xLexip Dec 5, 2025
dea15cd
refactor(data): Use a perfect logarithmic scale for brightness steps
xLexip Dec 5, 2025
b0f7f71
feat(broadcasts): Implement a 10% hyteresis ratio to avoid rapid toggles
xLexip Dec 5, 2025
cd7b522
fix(ui): Fix horizontal screen margin / window insets
xLexip Dec 5, 2025
87697bc
style: Improve main switch card behavior for longer texts
xLexip Dec 5, 2025
5aa148d
reafctor: Address lint findings
xLexip Dec 5, 2025
508a756
feat: Implement "send feedback" action
xLexip Dec 5, 2025
0caf799
docs: Add README
xLexip Dec 5, 2025
ffb54a7
chore(analytics): Integrate firebase analytics
xLexip Dec 5, 2025
7dc0df1
style: Bundle Nunito font to match system settings design
xLexip Dec 5, 2025
b069c60
feat(ui): Implement quick settings tile
xLexip Dec 5, 2025
babe82e
chore: v0.7.0-beta
xLexip Dec 5, 2025
cc47236
chore(analytics): Only log events in production
xLexip Dec 5, 2025
920dca1
style: Minor UI/UX tweaks
xLexip Dec 5, 2025
3464565
chore(test): Remove example tests
xLexip Dec 5, 2025
72ffd39
chore(mainfest): Add special use FGS justification
xLexip Dec 5, 2025
523daee
build: Set app version to v0.2.0
xLexip Dec 5, 2025
2c32e2c
refactor: Refine service notification
xLexip Dec 5, 2025
5412c9f
build: Enable optimized resource shrinking & NDK debug symbols
xLexip Dec 5, 2025
3d9825b
style: Update launcher icon colors
xLexip Dec 5, 2025
925333a
fix(manifest): Disable backup
xLexip Dec 5, 2025
50db3a1
feat: Show app version toast on about action
xLexip Dec 5, 2025
d77327d
chore: Add analytics to the three-dot menu actions
xLexip Dec 5, 2025
ede8c50
feat: Offer a webapp for easy app setup
xLexip Dec 5, 2025
11e58a0
chore(analytics): Disable AdID collection for Firebase Analytics
xLexip Dec 5, 2025
4b7254d
build: Set app version to v0.3.0
xLexip Dec 5, 2025
8b5a9d2
feat: Add beta feedback action
xLexip Dec 5, 2025
4a87c9a
fix: Increase service robustness
xLexip Dec 5, 2025
e291db0
About dialog
xLexip Dec 5, 2025
b0b0d67
refactor: Use Android Sharesheet for URL sharing
xLexip Dec 5, 2025
9036c71
Revert "feat(broadcasts): Implement a 10% hyteresis ratio to avoid ra…
xLexip Dec 5, 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
27 changes: 21 additions & 6 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,36 @@
name: SonarCloud
on:
push:
branches:
- main
- develop
push: { }
pull_request:
types: [ opened, synchronize, reopened ]
jobs:
build:
name: Build and analyze
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis

- name: Create google-services.json from secret
run: |
set -e
mkdir -p app
printf '%s' "$GOOGLE_SERVICES_JSON" > google-services.json
printf '%s' "$GOOGLE_SERVICES_JSON" > app/google-services.json
env:
GOOGLE_SERVICES_JSON: ${{ secrets.GOOGLE_SERVICES_JSON }}
- name: Create mock DarkThemeHandler
run: |
set -e
mkdir -p app/src/main/java/dev/lexip/hecate/util || true
printf '%s' "$MOCK_DARK_THEME_HANDLER" > app/src/main/java/dev/lexip/hecate/util/DarkThemeHandler.kt
env:
MOCK_DARK_THEME_HANDLER: ${{ secrets.MOCK_DARK_THEME_HANDLER }}
- name: Set up JDK 23
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: 23
distribution: 'zulu'
Expand Down
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ render.experimental.xml
# Google Services (e.g. APIs or Firebase)
google-services.json

# Trade secret
app/src/main/java/dev/lexip/hecate/util/DarkThemeHandler.kt

# Android Profiling
*.hprof

app/release/*
# Builds
app/release/
app/debug/
app/beta/
1 change: 1 addition & 0 deletions FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
buy_me_a_coffee: lexip
87 changes: 87 additions & 0 deletions PRIVACY_POLICY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Privacy Policy — Adaptive Theme (aka. Hecate)

**Last updated:** 2025-10-31

## 1. Overview

Adaptive Theme (`dev.lexip.hecate`) (the “App”). This Privacy Policy explains what
information the App collects, how it is used and shared, and your choices. This policy
applies to the App distributed via Google Play and any in-app disclosures.

- App name: Adaptive Theme
- Package name: `dev.lexip.hecate`
- Publisher: X. Lexip — x@lexip.dev

## 2. Core principles

We follow a minimal-data principle. The App is designed to work without collecting personal
information. When we do collect technical or diagnostic data it is strictly limited to what is
necessary to run, diagnose, and improve the App.

## 3. Data we may collect and why

### 3.1 No personal information

The App does not collect personal information (name, email, account ID, payment information) as part
of normal usage. The only time personal information may be provided is if you voluntarily send it to
us (for example, an email when contacting support).

### 3.2 Device, diagnostics, and usage data

We use Firebase services (Firebase Analytics and Firebase Crashlytics) to collect aggregated and
diagnostic usage information. Examples of data sent to Firebase include:

- Crash stacks and related device metadata (Android version, device model, app version)
- Anonymous analytics events for app usage patterns

This data helps us fix crashes, improve stability, and improve user experience. Firebase is operated
by Google; data processing by Firebase is subject to Google’s privacy policies. You can review
Firebase’s privacy docs here: https://firebase.google.com/support/privacy

By using the App you explicitly consent to the collection, processing, and transfer of the
analytics and diagnostic data described above by Firebase Analytics and Firebase Crashlytics. If you
do not agree to this processing, please do not use the App; alternatively contact us at
x@lexip.dev and we'll advise on available options.

### 3.3 Sensitive capability: WRITE_SECURE_SETTINGS

The App implements features that require the Android capability `WRITE_SECURE_SETTINGS`. Important
points:

- `WRITE_SECURE_SETTINGS` cannot be granted by a normal runtime permission prompt. It must be
granted externally (for example, with ADB or via an enterprise device-management policy).
- The App does not request or obtain this capability silently. If the capability is not granted the
App will disable the related features and show an explanatory message to the user.
- We use this capability only to perform the specific feature described in the UI: modify the system
theme (adaptive theme behavior). We do not use it to collect or transmit personal data.

## 4. Analytics, crash reporting, and third parties

We use Firebase Analytics and Firebase Crashlytics. These services process data on our behalf and
are contractually limited to that purpose. They may collect aggregated and device-level diagnostic
information. We do not sell user data or share it with other third parties for their own independent
use.

## 5. Data retention and deletion

- Crash reports and analytics data are retained by Firebase according to Firebase retention
policies. Aggregated or anonymized analytics data may be kept indefinitely for product
improvement.
- If you wish to request deletion of data associated with you, contact us at x@lexip.dev with a
clear description of the request. We'll respond and take reasonable steps to comply, subject to
any legal obligations to retain certain records.

## 6. Security

We implement reasonable technical and organizational measures to protect data we process. However,
no method of transmission or storage is completely secure. If you believe your data has been
compromised, contact us immediately at x@lexip.dev.

## 7. International data transfers

Firebase (Google) and other service providers may process data in countries outside your own. By
using the App you consent to the transfer and processing described in this policy.

## 8. Changes to this privacy policy

We may update this Privacy Policy. When we do, we will update the “Last updated” date above.
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,42 @@
[![SonarCloud](https://sonarcloud.io/api/project_badges/quality_gate?project=xLexip_Hecate)](https://sonarcloud.io/summary/new_code?id=xLexip_Hecate)
[![GooglePlay](https://upload.wikimedia.org/wikipedia/commons/7/78/Google_Play_Store_badge_EN.svg)](https://lexip.dev/hecate/play)

![feature_graphic](https://i.ibb.co/G38P9b9V/adaptive-theme.jpg)

## Adaptive Theme

Adaptive Theme intelligently switches your device between Light and Dark mode based on your
environment.

Get the readability of Light mode in bright daylight and the comfort of Dark mode in low light —
going easy on your eyes and your battery.

---

### Highlights

🌤️ **Smart Detection**: Uses your ambient light sensor to switch themes automatically.

⚙️ **Full Control**: Fully customizable brightness threshold and a Quick Settings tile to
pause/resume the service.

🔒 **Free & Open**: Free to use, no ads and open source.

🚀 **Native Design**: Modern architecture, built with Jetpack Compose and Material You for a seamless
Android experience.

🚫 **No Flickering**: The theme only changes when you turn on screen and the device is uncovered.

---

### One-Time Setup

To toggle the system theme, Android requires the permission `WRITE_SECURE_SETTINGS`. This is safe,
transparent and fully reversible. The app will guide you through the setup process.

---

That’s it! Set your preference, and never worry about your light/dark mode again.

🇩🇪 Made with 🥨 🍺 in Germany.

[![SonarCloud](https://sonarcloud.io/api/project_badges/quality_gate?project=xLexip_Hecate)](https://sonarcloud.io/summary/new_code?id=xLexip_Hecate)
53 changes: 44 additions & 9 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import org.gradle.api.JavaVersion.VERSION_23

plugins {
id("com.google.gms.google-services")
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.google.firebase.crashlytics)
}

android {
namespace = "dev.lexip.hecate"
compileSdk = 36
buildToolsVersion = "36.0.0"

defaultConfig {
applicationId = "dev.lexip.hecate"
minSdk = 31
targetSdk = 36
versionCode = 1
versionName = "0.1.0"
versionCode = 36
versionName = "0.7.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

Expand All @@ -27,19 +28,48 @@ android {
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
ndk {
debugSymbolLevel = "FULL"
}
}
debug {
versionNameSuffix = "-debug"
isDebuggable = true
ndk {
debugSymbolLevel = "FULL"
}
}
create("beta") {
initWith(getByName("release"))
versionNameSuffix = "-beta"
isDebuggable = false
}

}

compileOptions {
sourceCompatibility = VERSION_23
targetCompatibility = VERSION_23
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "23"

kotlin {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
}
}

bundle {
language {
@Suppress("UnstableApiUsage")
enableSplit = false
}
}

buildFeatures {
compose = true
buildConfig = true
}
buildToolsVersion = "35.0.0"

sourceSets {
getByName("main") {
resources {
Expand All @@ -50,6 +80,9 @@ android {
}

dependencies {
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.analytics)
implementation(libs.firebase.crashlytics)
implementation(libs.androidx.localbroadcastmanager)
implementation(libs.androidx.core.splashscreen.v100)
implementation(libs.androidx.activity.compose)
Expand All @@ -65,6 +98,8 @@ dependencies {
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.material)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.material.icons.extended)
implementation(libs.app.update.ktx)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
Expand Down

This file was deleted.

Loading