Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: CI

on:
pull_request:
branches: [ "**" ]
push:
branches: [ "main" ]

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

Comment on lines +16 to +24
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Android builds typically require SDK installation; add setup-android step if AGP modules are present.

The project appears to include Android modules (Compose app). Without an SDK, ./gradlew build often fails on CI.

First, confirm AGP usage:

If present, add before Build:

      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '21'

+     - name: Set up Android SDK
+       uses: android-actions/setup-android@v3
+       with:
+         api-level: 34
+         ndk: 26.1.10909125

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v4

🏁 Script executed:

#!/bin/bash
# Detect Android Gradle Plugin usage and required compileSdk.
rg -nP -C2 'com\.android\.(application|library)|compileSdk|namespace' --type=gradle.kts

Length of output: 124


🏁 Script executed:

#!/bin/bash
# Search for Android Gradle Plugin usage in all Gradle scripts
rg -nP 'com\.android\.(application|library)|compileSdk|namespace' -g '*.gradle*' -n -C2

Length of output: 3348


🏁 Script executed:

#!/bin/bash
# Locate ConfigData declaration
rg -n 'object ConfigData' -g '*.kt' -n -C2

# Find compileSdkVersion definition
rg -n 'compileSdkVersion' -g '*.kt' -n -C2

Length of output: 656


Add Android SDK setup to CI workflow

This repository defines multiple Android modules (application and library plugins detected in build.gradle.kts) all targeting compileSdkVersion = 36 (see buildSrc/src/main/java/ConfigData.kt) and will fail to build on CI without the Android SDK installed. Insert an Android SDK setup step before the Gradle setup:

      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '21'

+     - name: Set up Android SDK
+       uses: android-actions/setup-android@v3
+       with:
+         api-level: 36           # matches ConfigData.compileSdkVersion
+         components:             # install required SDK components
+           - build-tools;36.0.0
+           - platform-tools
+         # ndk: <version>        # add if you have native code

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v4
  • api-level must match ConfigData.compileSdkVersion (36) to satisfy the AGP modules.
  • Include any additional components (e.g., NDK) only if your modules require native builds.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
- name: Set up Android SDK
uses: android-actions/setup-android@v3
with:
api-level: 36 # matches ConfigData.compileSdkVersion
components: # install required SDK components
- build-tools;36.0.0
- platform-tools
# ndk: <version> # add if you have native code
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
🤖 Prompt for AI Agents
.github/workflows/ci.yml around lines 16 to 24: the CI workflow currently sets
up JDK and then Gradle but does not install the Android SDK, causing Android
modules targeting compileSdkVersion 36 to fail; add a step before the "Setup
Gradle" step that installs the Android SDK platform 36 and required components
(platform-tools, build-tools matching the platform, and any optional components
like NDK only if native builds are needed), accept licenses, and export
ANDROID_SDK_ROOT/ANDROID_HOME so Gradle/AGP can find the SDK; ensure the
api-level installed matches ConfigData.compileSdkVersion (36) and that the step
runs on the same runner environment as the rest of the job.

- name: Deep clean (project-local only)
run: ./gradlew --no-daemon deepClean

- name: Build
run: ./gradlew --no-daemon build

- name: Spotless check (soft enforcement for initial adoption)
run: ./gradlew --no-daemon spotlessCheckAll
continue-on-error: true

- name: Detekt (soft enforcement for initial adoption)
run: ./gradlew --no-daemon detektAll
continue-on-error: true

# Notes:
# - Spotless and Detekt steps are set to soft-fail initially to avoid breaking CI before the first formatting commit.
# After running `./gradlew applyCodeCleanup` and committing the formatting changes, set `continue-on-error: false`
# (or remove those lines) to enforce style and static analysis strictly on PRs.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@

A modern weather application built with Jetpack Compose that provides current weather conditions, forecasts, and air quality information.

+[![Download APK](https://img.shields.io/badge/download-APK-22272E.svg?style=for-the-badge&logo=android&logoColor=47954A)](https://github.com/bosankus/Compose-Weatherify/releases/latest)
[![Download APK](https://img.shields.io/badge/download-APK-22272E.svg?style=for-the-badge&logo=android&logoColor=47954A)](https://github.com/bosankus/Compose-Weatherify/releases/latest)

## 📱 Features

- **Current Weather**: View today's temperature and weather conditions
- **5-Day Forecast**: See weather predictions for the next 4 days
- **5-Day Forecast**: See weather predictions for the next 5 days
- **Air Quality Index**: Monitor air pollution levels
- **Multiple Cities**: Search and save your favorite locations
- **Multi-language Support**: Available in English, Hindi, and Hebrew
Expand Down
48 changes: 30 additions & 18 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag
import org.jetbrains.kotlin.gradle.dsl.JvmTarget

plugins {
id("com.android.application")
Expand All @@ -23,7 +23,10 @@ android {
versionName = ConfigData.versionName
multiDexEnabled = ConfigData.multiDexEnabled
testInstrumentationRunner = "bose.ankush.weatherify.helper.HiltTestRunner"
resourceConfigurations.addAll(listOf("en", "hi", "iw"))
@Suppress("UnstableApiUsage")
androidResources {
localeFilters.addAll(listOf("en", "hi", "iw"))
}
Comment on lines +26 to +29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

androidResources.localeFilters placement and language code

  • The androidResources { localeFilters } block is inside defaultConfig. AGP expects androidResources under the top-level android {} scope. Also, use “he” instead of the deprecated “iw”.
   defaultConfig {
     ...
-    @Suppress("UnstableApiUsage")
-    androidResources {
-        localeFilters.addAll(listOf("en", "hi", "iw"))
-    }
   }

+  @Suppress("UnstableApiUsage")
+  androidResources {
+      // Prefer modern BCP-47 tags; 'iw' (Hebrew) is deprecated -> use 'he'
+      localeFilters.addAll(listOf("en", "hi", "he"))
+  }

Please confirm your AGP version supports localeFilters (AGP 8.3+). If not, fall back to defaultConfig { resourceConfigurations += listOf("en","hi","he") }.


🏁 Script executed:

#!/bin/bash
# AGP plugin version in buildscript dependencies
echo "=== AGP plugin version in buildscript dependencies ==="
grep -R "com.android.tools.build:gradle" -n .

echo ""
echo "=== AGP plugin version in plugins DSL ==="
grep -R 'id("com.android.application")' -n .

echo ""
echo "=== defaultConfig context ==="
grep -n -C5 'defaultConfig' app/build.gradle.kts

echo ""
echo "=== androidResources context ==="
grep -n -C5 'androidResources' app/build.gradle.kts

Length of output: 1623


🏁 Script executed:

#!/bin/bash
# Locate AGP plugin version constant in buildSrc
grep -R -n -C3 "buildGradle" buildSrc

Length of output: 1074


Move androidResources block out of defaultConfig and update deprecated language tag

The project is using AGP 8.12.0 (≥ 8.3), so androidResources.localeFilters is supported at the top-level android {} scope—but not inside defaultConfig. Additionally, "iw" is the deprecated alias for Hebrew; use the modern BCP-47 tag "he" instead.

Please apply the following mandatory refactor in app/build.gradle.kts:

  • Remove the androidResources { … } block (lines 26–29) from inside defaultConfig.
  • Add it under the top-level android {} scope (e.g. immediately after defaultConfig { … }), updating "iw""he".
 app/build.gradle.kts
 android {
     compileSdk = ConfigData.compileSdkVersion

     defaultConfig {
         applicationId = "bose.ankush.weatherify"
         minSdk = ConfigData.minSdkVersion
         targetSdk = ConfigData.targetSdkVersion
         versionCode = ConfigData.versionCode
         versionName = ConfigData.versionName
         multiDexEnabled = ConfigData.multiDexEnabled
         testInstrumentationRunner = "bose.ankush.weatherify.helper.HiltTestRunner"
-        @Suppress("UnstableApiUsage")
-        androidResources {
-            localeFilters.addAll(listOf("en", "hi", "iw"))
-        }
     }

+    @Suppress("UnstableApiUsage")
+    androidResources {
+        // Use modern BCP-47 tags; 'he' replaces deprecated 'iw'
+        localeFilters.addAll(listOf("en", "hi", "he"))
+    }

     kapt {
         …
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Suppress("UnstableApiUsage")
androidResources {
localeFilters.addAll(listOf("en", "hi", "iw"))
}
android {
compileSdk = ConfigData.compileSdkVersion
defaultConfig {
applicationId = "bose.ankush.weatherify"
minSdk = ConfigData.minSdkVersion
targetSdk = ConfigData.targetSdkVersion
versionCode = ConfigData.versionCode
versionName = ConfigData.versionName
multiDexEnabled = ConfigData.multiDexEnabled
testInstrumentationRunner = "bose.ankush.weatherify.helper.HiltTestRunner"
}
@Suppress("UnstableApiUsage")
androidResources {
// Use modern BCP-47 tags; 'he' replaces deprecated 'iw'
localeFilters.addAll(listOf("en", "hi", "he"))
}
kapt {
}
}
🤖 Prompt for AI Agents
In app/build.gradle.kts around lines 26–29, the androidResources { … } block is
incorrectly placed inside defaultConfig and uses the deprecated locale tag "iw";
remove the entire androidResources block from inside defaultConfig and add it at
the top-level android { } scope (for example immediately after the closing brace
of defaultConfig), preserving the @Suppress("UnstableApiUsage") if needed and
updating the locale list to use "he" instead of "iw" (e.g.
localeFilters.addAll(listOf("en", "hi", "he"))).

}

kapt {
Expand Down Expand Up @@ -58,17 +61,6 @@ android {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}

kotlin {
sourceSets.all {
languageSettings {
languageVersion = Versions.kotlinCompiler
}
}
}

lint {
abortOnError = false
Expand All @@ -77,10 +69,9 @@ android {
namespace = "bose.ankush.weatherify"
}

composeCompiler {
featureFlags = setOf(
ComposeFeatureFlag.StrongSkipping.disabled()
)

kapt {
correctErrorTypes = true
}

dependencies {
Expand Down Expand Up @@ -111,6 +102,7 @@ dependencies {
debugImplementation(Deps.composeUiTooling)
implementation(Deps.composeUiToolingPreview)
implementation(Deps.composeMaterial3)
implementation(Deps.composeIconsExtended)

// Unit Testing
testImplementation(Deps.junit)
Expand All @@ -133,11 +125,16 @@ dependencies {
// Networking
implementation(Deps.gson)

// Room runtime for providing WeatherDatabase from app DI
implementation(Deps.room)
implementation(Deps.roomKtx)

Comment on lines +129 to 132
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Room compiler missing — annotation processing will fail at build time

You’ve added Room runtime but not the processor. Without room-compiler, DAOs/entities won’t generate. Add the kapt (or KSP) dependency.

 dependencies {
@@
-    // Room runtime for providing WeatherDatabase from app DI
-    implementation(Deps.room)
-    implementation(Deps.roomKtx)
+    // Room
+    implementation(Deps.room)
+    implementation(Deps.roomKtx)
+    kapt(Deps.roomCompiler)
 }

If you’re migrating to KSP, replace kapt with ksp(Deps.roomCompiler) and apply the KSP plugin instead of kapt.

🤖 Prompt for AI Agents
In app/build.gradle.kts around lines 127 to 130, you added Room runtime
(implementation(Deps.room) and implementation(Deps.roomKtx)) but omitted the
Room annotation processor; this will cause DAO/entity generation to fail at
build time. Add the Room compiler as an annotation processor (either
kapt(Deps.roomCompiler) and apply the kapt plugin in the Gradle file, or if
using KSP use ksp(Deps.roomCompiler) and apply the KSP plugin), ensuring the
chosen processor configuration matches the rest of the project (kapt vs ksp) and
remove any conflicting processor config.

// Firebase
implementation(platform(Deps.firebaseBom))
implementation(Deps.firebaseConfig)
implementation(Deps.firebaseAnalytics)
implementation(Deps.firebasePerformanceMonitoring)
implementation(Deps.firebaseMessaging)

// Coroutines
implementation(Deps.coroutinesCore)
Expand All @@ -147,12 +144,27 @@ dependencies {
implementation(Deps.hilt)
implementation(Deps.hiltNavigationCompose)
kapt(Deps.hiltDaggerAndroidCompiler)
kapt(Deps.hiltAndroidXCompiler)

// Miscellaneous
implementation(Deps.timber)
implementation(Deps.lottieCompose)
// Removed Lottie dependency as per requirements
implementation(Deps.coilCompose)

// Memory leak
debugImplementation(Deps.leakCanary)

// Payment SDK moved to app module
implementation(Deps.razorPay)
}


kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
freeCompilerArgs.addAll(
"-Xopt-in=kotlin.RequiresOptIn",
"-Xopt-in=androidx.compose.animation.ExperimentalAnimationApi"
)
}
}
3 changes: 2 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<application
Expand All @@ -22,6 +22,7 @@
android:localeConfig="@xml/locales_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:usesCleartextTraffic="false"
android:theme="@style/Theme.Weatherify.Starting"
tools:ignore="AllowBackup,UnusedAttribute">
<activity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,22 @@ package bose.ankush.storage.di

import android.content.Context
import androidx.room.Room
import bose.ankush.network.repository.WeatherRepository as NetworkWeatherRepository
import bose.ankush.network.auth.storage.TokenStorage
import bose.ankush.storage.api.WeatherStorage
import bose.ankush.storage.common.WEATHER_DATABASE_NAME
import bose.ankush.storage.impl.TokenStorageImpl
import bose.ankush.storage.impl.WeatherStorageImpl
import bose.ankush.storage.room.JsonParser
import bose.ankush.storage.room.WeatherDatabase
import bose.ankush.storage.room.WeatherDataModelConverters
import bose.ankush.storage.room.WeatherDatabase
import com.google.gson.Gson
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
import bose.ankush.network.repository.WeatherRepository as NetworkWeatherRepository

@Module
@InstallIn(SingletonComponent::class)
Expand All @@ -24,7 +26,6 @@ object StorageModule {
@Provides
@Singleton
fun provideGson(): Gson = Gson()

@Provides
@Singleton
fun provideJsonParser(gson: Gson): JsonParser = JsonParser(gson)
Expand All @@ -47,7 +48,7 @@ object StorageModule {
WEATHER_DATABASE_NAME
)
.addTypeConverter(converters)
.fallbackToDestructiveMigration()
.fallbackToDestructiveMigration(false)
.build()
}

Expand All @@ -59,4 +60,12 @@ object StorageModule {
): WeatherStorage {
return WeatherStorageImpl(networkRepository, weatherDatabase)
}
}

@Provides
@Singleton
fun provideTokenStorage(
weatherDatabase: WeatherDatabase
): TokenStorage {
return TokenStorageImpl(weatherDatabase)
}
}
15 changes: 12 additions & 3 deletions app/src/main/java/bose/ankush/weatherify/WeatherifyApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package bose.ankush.weatherify

import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import bose.ankush.weatherify.base.location.LocationService.Companion.NOTIFICATION_CHANNEL_ID
import bose.ankush.weatherify.base.location.LocationService.Companion.NOTIFICATION_NAME
import bose.ankush.weatherify.domain.remote_config.RemoteConfigService
Expand Down Expand Up @@ -33,7 +32,17 @@ class WeatherifyApplication : WeatherifyApplicationCore() {
}

private fun enableTimber() {
Timber.plant(Timber.DebugTree())
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
} else {
Timber.plant(object : Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
// Only log WARN, ERROR, and WTF in release; avoid verbose/debug/info
if (priority == android.util.Log.VERBOSE || priority == android.util.Log.DEBUG || priority == android.util.Log.INFO) return
android.util.Log.println(priority, tag, message)
}
})
}
}

private fun createNotificationChannel() {
Expand All @@ -43,7 +52,7 @@ class WeatherifyApplication : WeatherifyApplicationCore() {
NotificationManager.IMPORTANCE_HIGH
)
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import android.annotation.SuppressLint
Author: Ankush Bose
Date: 05,May,2021
**/

/*General constants*/
const val WEATHER_BASE_URL = "https://data.androidplay.in/"
const val WEATHER_IMG_URL = "https://openweathermap.org/img/wn/"

const val APP_UPDATE_REQ_CODE = 111
Expand All @@ -22,7 +19,6 @@ const val DEFAULT_CITY_NAME = "New Delhi"
/* Permission constants */
const val ACCESS_FINE_LOCATION = android.Manifest.permission.ACCESS_FINE_LOCATION
const val ACCESS_COARSE_LOCATION = android.Manifest.permission.ACCESS_COARSE_LOCATION
const val ACCESS_PHONE_CALL = android.Manifest.permission.CALL_PHONE
@SuppressLint("InlinedApi")
const val ACCESS_NOTIFICATION = android.Manifest.permission.POST_NOTIFICATIONS

Expand All @@ -31,10 +27,5 @@ val PERMISSIONS_TO_REQUEST = arrayOf(
ACCESS_COARSE_LOCATION
)

/*Room central db name*/
const val WEATHER_DATABASE_NAME = "central_weather_table"
const val AQ_DATABASE_NAME = "central_aq_table"
const val PHONE_NUMBER = "tel:+91XXXXXXXXX"

/*Remote keys*/
const val ENABLE_NOTIFICATION = "enable_notification"
Loading
Loading