Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d5aa5e2
refactor: Overcast integration
sameerasw Oct 1, 2026
8db539e
feat: integrate Overcast weather content observation and synchronizat…
sameerasw Oct 1, 2026
be1c9bc
feat: add package genuineness validation to OvercastWeather availability
sameerasw Oct 1, 2026
f60ab94
feat: add wallpaper staging feature with dedicated activity, screen, …
sameerasw Oct 1, 2026
22b1cd0
refactor: redesign wallpaper staging action buttons and prioritize st…
sameerasw Oct 1, 2026
94c1c40
chore: bump version to 18.5-beta.1 and whatsNewCounter to 5
sameerasw Oct 1, 2026
70a6316
feat: integrate Overcast weather app into What's New screen and manif…
sameerasw Oct 1, 2026
40b7b22
Community translations by @feuersternX
feuersternX Sep 30, 2026
2c052bf
Community translations by @OrdinaryPerson0
OrdinaryPerson0 Sep 30, 2026
90aafb4
Community translations by @towa100122
towa100122 Sep 30, 2026
51eba91
Community translations by @towa100122
towa100122 Sep 30, 2026
e4aafe4
Community translations by @towa100122
towa100122 Sep 30, 2026
d933f52
Community translations by @amirkarls
amirkarls Sep 30, 2026
f76b5b9
Community translations by @amirkarls
amirkarls Sep 30, 2026
4b703cd
Community translations by @elchumy6960
elchumy6960 Sep 30, 2026
f79654f
Community translations by @elchumy6960
elchumy6960 Oct 1, 2026
12c93ff
Community translations by @elchumy6960
elchumy6960 Oct 1, 2026
96717e0
fix: scrolling in sound mdoe tile settings
sameerasw Oct 4, 2026
fb629ee
fix: Duo auto show on start even when disabled
sameerasw Oct 4, 2026
59ff19a
Community translations by @Riccardo-png
Riccardo-png Oct 4, 2026
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
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ android {
minSdk = 26
targetSdk = 37
versionCode = 67
versionName = "18.4"
versionName = "18.5-beta.1"

val whatsNewCounter = 4
val whatsNewCounter = 5
buildConfigField("int", "WHATS_NEW_COUNTER", whatsNewCounter.toString())
buildConfigField("int", "REQUIRED_WEAR_VERSION_CODE", "7")

Expand Down
34 changes: 12 additions & 22 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,14 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="com.sameerasw.overcast.permission.READ_WEATHER" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />

<queries>
<package android:name="com.sameerasw.overcast" />
<provider android:authorities="com.sameerasw.overcast.weather" />
</queries>

<application
android:name=".EssentialsApp"
android:allowBackup="true"
Expand Down Expand Up @@ -314,28 +320,6 @@
android:exported="false"
android:label="@string/tab_app_updates_title"
android:theme="@style/Theme.Essentials"></activity>
<activity
android:name=".ui.activities.WeatherDetailActivity"
android:exported="true"
android:icon="@drawable/rounded_partly_cloudy_day_24"
android:label="@string/weather_activity_label"
android:launchMode="singleTask"
android:taskAffinity="com.sameerasw.essentials.weather"
android:theme="@style/Theme.Essentials.Weather" />

<activity-alias
android:name=".WeatherLauncher"
android:enabled="true"
android:exported="true"
android:icon="@drawable/rounded_partly_cloudy_day_24"
android:label="@string/weather_activity_label"
android:targetActivity=".ui.activities.WeatherDetailActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>

<activity
android:name=".ui.activities.ColorPickerActivity"
android:excludeFromRecents="true"
Expand Down Expand Up @@ -627,6 +611,12 @@
android:label="@string/feat_daily_wallpaper_title"
android:theme="@style/Theme.Essentials" />

<activity
android:name=".ui.activities.WallpaperStagingActivity"
android:exported="false"
android:label="@string/feat_wallpaper_staging_title"
android:theme="@style/Theme.Essentials" />

<service
android:name=".services.tiles.ScreenOffAccessibilityService"
android:exported="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ class EssentialsApp : Application() {
super.onCreate()
context = applicationContext
com.sameerasw.essentials.utils.HapticUtil.initialize(this)
com.sameerasw.essentials.utils.WeatherLauncher.apply(this)

try {
resources?.configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -504,10 +504,6 @@ class SettingsRepository(
const val KEY_ISLAND_DISMISS_ON_OUTSIDE = "island_dismiss_on_outside"
const val KEY_ISLAND_CAMERA_POSITION = "island_camera_position"
const val KEY_ISLAND_PREVIEW_RING = "island_preview_ring"
const val KEY_DEBUG_SIMULATED_WEATHER = "debug_simulated_weather"
const val KEY_DEBUG_WEATHER_EXPERIMENTAL = "debug_weather_experimental"
const val KEY_DEBUG_SIMULATED_TIME = "debug_simulated_time"
const val KEY_DEBUG_SIMULATED_TEMP = "debug_simulated_temp"
const val KEY_ISLAND_PREVIEW_STAGE = "island_preview_stage"
const val ISLAND_PREVIEW_STAGE_AUTO = "auto"
const val ISLAND_PREVIEW_STAGE_PEEK = "peek"
Expand All @@ -528,15 +524,8 @@ class SettingsRepository(
const val ISLAND_WEATHER_MODE_COMPACT = "compact"
const val ISLAND_WEATHER_MODE_ALERTS = "alerts"

const val KEY_WEATHER_PROVIDER = "weather_provider"
const val KEY_WEATHER_API_KEY = "weather_api_key"
const val KEY_WEATHER_SHOW_IN_LAUNCHER = "weather_show_in_launcher"
const val KEY_WEATHER_OPENMETEO_MODEL = "weather_openmeteo_model"
fun weatherApiKeyName(providerId: String) = "${KEY_WEATHER_API_KEY}_$providerId"
const val KEY_WEATHER_LOCATION_MODE = "weather_location_mode"
const val KEY_WEATHER_MANUAL_LOCATION = "weather_manual_location"
const val KEY_WEATHER_UNITS = "weather_units"
const val KEY_WEATHER_REFRESH_MINUTES = "weather_refresh_minutes"
private const val LEGACY_WEATHER_API_KEY_PREFIX = "weather_api_key"
const val WEATHER_UNITS_SYSTEM = "system"
const val WEATHER_UNITS_CELSIUS = "celsius"
const val WEATHER_UNITS_FAHRENHEIT = "fahrenheit"
Expand Down Expand Up @@ -676,6 +665,17 @@ class SettingsRepository(
const val KEY_BUBBLE_WEB_FULLSCREEN = "bubble_web_fullscreen"
const val KEY_SIM_NAMES_APPLY_ON_BOOT = "sim_names_apply_on_boot"
const val KEY_POWER_SAVING_APPLY_ON_BOOT = "power_saving_apply_on_boot"
const val KEY_UNSPLASH_ACCESS_KEY = "unsplash_access_key"
}

fun getUnsplashAccessKey(): String? = prefs.getString(KEY_UNSPLASH_ACCESS_KEY, null)

fun setUnsplashAccessKey(key: String?) {
if (key.isNullOrBlank()) {
prefs.edit().remove(KEY_UNSPLASH_ACCESS_KEY).apply()
} else {
prefs.edit().putString(KEY_UNSPLASH_ACCESS_KEY, key.trim()).apply()
}
}

fun isSimNamesApplyOnBootEnabled(): Boolean = getBoolean(KEY_SIM_NAMES_APPLY_ON_BOOT, false)
Expand Down Expand Up @@ -1634,7 +1634,7 @@ class SettingsRepository(
if (key == KEY_GITHUB_ACCESS_TOKEN ||
key == KEY_GITHUB_WORKFLOW_TOKEN ||
key == KEY_SHIZUKU_AUTH_TOKEN ||
key.startsWith(KEY_WEATHER_API_KEY) ||
key.startsWith(LEGACY_WEATHER_API_KEY_PREFIX) ||
key.startsWith("mac_battery_") ||
key == "airsync_mac_connected" ||
key == KEY_SNOOZE_DISCOVERED_CHANNELS ||
Expand Down Expand Up @@ -1711,8 +1711,7 @@ class SettingsRepository(
KEY_GITHUB_ACCESS_TOKEN,
KEY_GITHUB_WORKFLOW_TOKEN,
KEY_SHIZUKU_AUTH_TOKEN,
KEY_WEATHER_API_KEY,
*com.sameerasw.essentials.weather.provider.WeatherProviders.all.map { weatherApiKeyName(it.id) }.toTypedArray(),
LEGACY_WEATHER_API_KEY_PREFIX,
"airsync_mac_connected",
KEY_SNOOZE_DISCOVERED_CHANNELS,
KEY_MAPS_DISCOVERED_CHANNELS,
Expand Down Expand Up @@ -1752,7 +1751,7 @@ class SettingsRepository(
if (key == KEY_GITHUB_ACCESS_TOKEN ||
key == KEY_GITHUB_WORKFLOW_TOKEN ||
key == KEY_SHIZUKU_AUTH_TOKEN ||
key.startsWith(KEY_WEATHER_API_KEY)
key.startsWith(LEGACY_WEATHER_API_KEY_PREFIX)
) {
return@forEach
}
Expand Down Expand Up @@ -3747,58 +3746,9 @@ class SettingsRepository(
fun isIslandWeatherHapticsEnabled(): Boolean = getBoolean(KEY_ISLAND_WEATHER_HAPTICS, true)
fun setIslandWeatherHapticsEnabled(enabled: Boolean) = putBoolean(KEY_ISLAND_WEATHER_HAPTICS, enabled)

fun getWeatherProvider(): String? = getString(KEY_WEATHER_PROVIDER, null)
fun setWeatherProvider(id: String) = putString(KEY_WEATHER_PROVIDER, id)

// Keys are stored per provider; the single key from before multiple sources belongs to WeatherAPI.com.
fun isWeatherExperimentalEnabled(): Boolean = getBoolean(KEY_DEBUG_WEATHER_EXPERIMENTAL, false)

fun getSimulatedWeather(): com.sameerasw.essentials.weather.effects.WeatherSimulationPreset? =
if (isWeatherExperimentalEnabled()) {
com.sameerasw.essentials.weather.effects.WeatherSimulation.find(
getString(KEY_DEBUG_SIMULATED_WEATHER, com.sameerasw.essentials.weather.effects.WeatherSimulation.OFF),
)
} else {
null
}

fun getSimulatedTimeOfDay(): String? =
if (isWeatherExperimentalEnabled()) getString(KEY_DEBUG_SIMULATED_TIME, "auto")?.takeIf { it != "auto" } else null

fun getSimulatedTempC(): Double? =
if (isWeatherExperimentalEnabled()) getString(KEY_DEBUG_SIMULATED_TEMP, "auto")?.toDoubleOrNull() else null

fun getWeatherOpenMeteoModel(): String? = getString(KEY_WEATHER_OPENMETEO_MODEL, null)?.takeIf { it.isNotBlank() }
fun setWeatherOpenMeteoModel(model: String?) = putString(KEY_WEATHER_OPENMETEO_MODEL, model.orEmpty())

fun getWeatherApiKey(providerId: String): String? {
val own = getString(weatherApiKeyName(providerId), null)
if (own != null) return own.takeIf { it.isNotBlank() }
return if (providerId == "weatherapi") getString(KEY_WEATHER_API_KEY, null)?.takeIf { it.isNotBlank() } else null
}
fun setWeatherApiKey(providerId: String, key: String?) = putString(weatherApiKeyName(providerId), key?.trim().orEmpty())

fun getWeatherLocationMode(): String = getString(KEY_WEATHER_LOCATION_MODE, "device") ?: "device"
fun setWeatherLocationMode(mode: String) = putString(KEY_WEATHER_LOCATION_MODE, mode)

// Stored as "lat|lon|name".
fun getWeatherManualLocation(): Triple<Double, Double, String>? {
val parts = getString(KEY_WEATHER_MANUAL_LOCATION, null)?.split("|", limit = 3) ?: return null
if (parts.size != 3) return null
val lat = parts[0].toDoubleOrNull() ?: return null
val lon = parts[1].toDoubleOrNull() ?: return null
return Triple(lat, lon, parts[2])
}

fun setWeatherManualLocation(latitude: Double, longitude: Double, name: String) =
putString(KEY_WEATHER_MANUAL_LOCATION, "$latitude|$longitude|${name.replace("|", " ")}")

fun getWeatherUnits(): String = getString(KEY_WEATHER_UNITS, WEATHER_UNITS_SYSTEM) ?: WEATHER_UNITS_SYSTEM
fun setWeatherUnits(units: String) = putString(KEY_WEATHER_UNITS, units)

fun getWeatherRefreshMinutes(): Int = getInt(KEY_WEATHER_REFRESH_MINUTES, 60)
fun setWeatherRefreshMinutes(minutes: Int) = putInt(KEY_WEATHER_REFRESH_MINUTES, minutes)

fun isIslandShowTimersEnabled(): Boolean = getBoolean(KEY_ISLAND_SHOW_TIMERS, true)
fun setIslandShowTimersEnabled(enabled: Boolean) = putBoolean(KEY_ISLAND_SHOW_TIMERS, enabled)

Expand Down
Loading