Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(deps): update dependency gradle to v8.13 - abandoned #3

Closed
wants to merge 11 commits into from
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@ android {
}

dependencies {
implementation(libs.androidx.localbroadcastmanager)
implementation(libs.androidx.core.splashscreen.v100)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.datastore.preferences)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.material3)
Expand Down
64 changes: 49 additions & 15 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,26 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2024 xLexip <https://lexip.dev>
~
~ Licensed under the GNU General Public License, Version 3.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.gnu.org/licenses/gpl-3.0
~
~ Please see the License for specific terms regarding permissions and limitations.
-->

<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
tools:ignore="ProtectedPermissions">

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.App.Starting"
android:usesCleartextTraffic="false"
android:windowSoftInputMode="adjustResize">
android:name="dev.lexip.hecate.HecateApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.App.Starting"
android:usesCleartextTraffic="false"
android:windowSoftInputMode="adjustResize">

<activity
android:name="MainActivity"
android:exported="true"
android:theme="@style/Theme.App.Starting">
android:name=".ui.MainActivity"
android:exported="true"
android:theme="@style/Theme.App.Starting">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<service
android:name=".services.BroadcastReceiverService"
android:foregroundServiceType="specialUse" />

<receiver
android:name=".broadcasts.BootCompletedReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>

</application>

</manifest>
31 changes: 31 additions & 0 deletions app/src/main/java/dev/lexip/hecate/HecateApplication.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2024 xLexip <https://lexip.dev>
*
* Licensed under the GNU General Public License, Version 3.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.gnu.org/licenses/gpl-3.0
*
* Please see the License for specific terms regarding permissions and limitations.
*/

package dev.lexip.hecate

import android.app.Application
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore

const val USER_PREFERENCES_NAME = "user_preferences"
private val Context.dataStore by preferencesDataStore(USER_PREFERENCES_NAME)

class HecateApplication : Application() {
/**
* Top level data store to ensure it is maintained as a singleton
* but still accessible in both the app and service.
*/
val userPreferencesDataStore: DataStore<Preferences>
get() = this.dataStore
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2024 xLexip <https://lexip.dev>
*
* Licensed under the GNU General Public License, Version 3.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.gnu.org/licenses/gpl-3.0
*
* Please see the License for specific terms regarding permissions and limitations.
*/

package dev.lexip.hecate.broadcasts

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import dev.lexip.hecate.services.BroadcastReceiverService

private const val TAG = "BootCompletedReceiver"

class BootCompletedReceiver : BroadcastReceiver() {

override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
Log.i(TAG, "Boot completed, starting broadcast receiver service...")
val serviceIntent = Intent(context, BroadcastReceiverService::class.java)
context.startService(serviceIntent)
}
}

}
55 changes: 55 additions & 0 deletions app/src/main/java/dev/lexip/hecate/broadcasts/ScreenOnReceiver.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2024-2025 xLexip <https://lexip.dev>
*
* Licensed under the GNU General Public License, Version 3.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.gnu.org/licenses/gpl-3.0
*
* Please see the License for specific terms regarding permissions and limitations.
*/

package dev.lexip.hecate.broadcasts

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import dev.lexip.hecate.util.DarkThemeHandler
import dev.lexip.hecate.util.LightSensorManager
import dev.lexip.hecate.util.ProximitySensorManager

private const val TAG = "ScreenOnReceiver"

/**
* Adaptive theme switching logic. Executes when the screen is turned on.
* The theme is switched based on the environment brightness and proximity sensor values.
*/
class ScreenOnReceiver(
private val proximitySensorManager: ProximitySensorManager,
private val lightSensorManager: LightSensorManager,
private val darkThemeHandler: DarkThemeHandler,
private val adaptiveThemeThresholdLux: Float
) : BroadcastReceiver() {

override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_SCREEN_ON) {
Log.d(TAG, "Screen turned on, checking adaptive theme conditions...")

// Check if the device is covered using the proximity sensor
proximitySensorManager.startListening { distance ->
proximitySensorManager.stopListening()

// If the device is not covered, change the device theme based on the environment
if (distance >= 5f) {
lightSensorManager.startListening { lightValue ->
lightSensorManager.stopListening()
darkThemeHandler.setDarkTheme(lightValue < adaptiveThemeThresholdLux)
}
} else Log.d(TAG, "Device is covered, skipping adaptive theme checks.")

}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (C) 2024 xLexip <https://lexip.dev>
*
* Licensed under the GNU General Public License, Version 3.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.gnu.org/licenses/gpl-3.0
*
* Please see the License for specific terms regarding permissions and limitations.
*/

package dev.lexip.hecate.data

import android.util.Log
import androidx.datastore.core.DataStore
import androidx.datastore.core.IOException
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.core.floatPreferencesKey
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map

private const val TAG = "UserPreferencesRepository"

data class UserPreferences(
val adaptiveThemeEnabled: Boolean,
val adaptiveThemeThresholdLux: Float
)

class UserPreferencesRepository(private val dataStore: DataStore<Preferences>) {

private object PreferencesKeys {
val ADAPTIVE_THEME_ENABLED = booleanPreferencesKey("adaptive_theme_enabled")
val ADAPTIVE_THEME_THRESHOLD_LUX = floatPreferencesKey("adaptive_theme_threshold_lux")
}

val userPreferencesFlow: Flow<UserPreferences> = dataStore.data
.catch { exception ->
// dataStore.data throws an IOException when an error is encountered when reading data
if (exception is IOException) {
Log.e(TAG, "Error reading user preferences.", exception)
emit(emptyPreferences())
} else {
throw exception
}
}.map { preferences ->
mapUserPreferences(preferences)
}

suspend fun fetchInitialPreferences() =
mapUserPreferences(dataStore.data.first().toPreferences())

private fun mapUserPreferences(preferences: Preferences): UserPreferences {
// Get our show completed value, defaulting to false if not set:
preferences[PreferencesKeys.ADAPTIVE_THEME_ENABLED] == true
val adaptiveThemeEnabled = preferences[PreferencesKeys.ADAPTIVE_THEME_ENABLED] == true
val adaptiveThemeThresholdLux =
preferences[PreferencesKeys.ADAPTIVE_THEME_THRESHOLD_LUX] ?: 100f
return UserPreferences(adaptiveThemeEnabled, adaptiveThemeThresholdLux)
}

suspend fun updateAdaptiveThemeEnabled(enabled: Boolean) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.ADAPTIVE_THEME_ENABLED] = enabled
}
}

suspend fun updateAdaptiveThemeThresholdLux(lux: Float) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.ADAPTIVE_THEME_THRESHOLD_LUX] = lux
}
}

}
Loading