diff --git a/.github/workflows/android-branch_ci.yml b/.github/workflows/android-branch_ci.yml
index 4890194..fd8b971 100644
--- a/.github/workflows/android-branch_ci.yml
+++ b/.github/workflows/android-branch_ci.yml
@@ -39,11 +39,18 @@ jobs:
run: chmod +x gradlew
- name: Build with Gradle
- run: ./gradlew clean && ./gradlew assembleDebug
+ run: ./gradlew clean && ./gradlew assembleAppWithInternetDebug && ./gradlew assembleAppWithoutInternetDebug
- - name: Upload Artifact
+ - name: Upload Artifact - WithInternet
uses: actions/upload-artifact@v4
with:
- name: Signed app bundle
- path: app/build/outputs/apk/debug/*.apk
+ name: Signed app bundle - WithInternet
+ path: app/build/outputs/apk/withInternet/debug/*.apk
+ retention-days: 3
+
+ - name: Upload Artifact - WithoutInternet
+ uses: actions/upload-artifact@v4
+ with:
+ name: Signed app bundle - WithoutInternet
+ path: app/build/outputs/apk/withoutInternet/debug/*.apk
retention-days: 3
diff --git a/.github/workflows/android-main_ci.yml b/.github/workflows/android-main_ci.yml
index 69a8cd7..8428919 100644
--- a/.github/workflows/android-main_ci.yml
+++ b/.github/workflows/android-main_ci.yml
@@ -37,11 +37,18 @@ jobs:
run: chmod +x gradlew
- name: Build with Gradle
- run: ./gradlew clean && ./gradlew assembleDebug
+ run: ./gradlew clean && ./gradlew assembleAppWithInternetDebug && ./gradlew assembleAppWithoutInternetDebug
- - name: Upload Artifact
+ - name: Upload Artifact - WithInternet
uses: actions/upload-artifact@v4
with:
- name: Signed app bundle
- path: app/build/outputs/apk/debug/*.apk
+ name: Signed app bundle - WithInternet
+ path: app/build/outputs/apk/withInternet/debug/*.apk
+ retention-days: 3
+
+ - name: Upload Artifact - WithoutInternet
+ uses: actions/upload-artifact@v4
+ with:
+ name: Signed app bundle - WithoutInternet
+ path: app/build/outputs/apk/withoutInternet/debug/*.apk
retention-days: 3
diff --git a/.github/workflows/android-pr_ci.yml b/.github/workflows/android-pr_ci.yml
index 8c6ca01..b721916 100644
--- a/.github/workflows/android-pr_ci.yml
+++ b/.github/workflows/android-pr_ci.yml
@@ -37,11 +37,18 @@ jobs:
run: chmod +x gradlew
- name: Build with Gradle
- run: ./gradlew clean && ./gradlew assembleDebug
+ run: ./gradlew clean && ./gradlew assembleAppWithInternetDebug && ./gradlew assembleAppWithoutInternetDebug
- - name: Upload Artifact
+ - name: Upload Artifact - WithInternet
uses: actions/upload-artifact@v4
with:
- name: Signed app bundle
- path: app/build/outputs/apk/debug/*.apk
+ name: Signed app bundle - WithInternet
+ path: app/build/outputs/apk/withInternet/debug/*.apk
+ retention-days: 3
+
+ - name: Upload Artifact - WithoutInternet
+ uses: actions/upload-artifact@v4
+ with:
+ name: Signed app bundle - WithoutInternet
+ path: app/build/outputs/apk/withoutInternet/debug/*.apk
retention-days: 3
diff --git a/.github/workflows/android-release_ci-forced.yml b/.github/workflows/android-release_ci-forced.yml
index 5908043..9a77979 100644
--- a/.github/workflows/android-release_ci-forced.yml
+++ b/.github/workflows/android-release_ci-forced.yml
@@ -33,24 +33,43 @@ jobs:
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- - name: Build
- run: ./gradlew clean && ./gradlew assembleRelease
+ - name: Build with Gradle
+ run: ./gradlew clean && ./gradlew assembleAppWithInternetRelease && ./gradlew assembleAppWithoutInternetRelease
- - name: Sign APK
+ - name: Sign APK - WithInternet
uses: ilharp/sign-android-release@v1
# ID used to access action output
- id: sign_app
+ id: sign_app_withInternet
with:
- releaseDir: app/build/outputs/apk/release
+ releaseDir: app/build/outputs/apk/withInternet/release
signingKey: ${{ secrets.SIGNINGKEY_BASE64 }}
keyAlias: ${{ secrets.KEY_ALIAS }}
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
keyPassword: ${{ secrets.KEY_PASSWORD }}
buildToolsVersion: 33.0.0
- - name: Upload Artifact
+ - name: Upload Artifact - WithInternet
uses: actions/upload-artifact@v4
with:
- name: Signed app bundle
- path: app/build/outputs/apk/release/*.apk
+ name: Signed app bundle - WithInternet
+ path: app/build/outputs/apk/withInternet/release/*.apk
retention-days: 3
+
+ - name: Sign APK - WithInternet
+ uses: ilharp/sign-android-release@v1
+ # ID used to access action output
+ id: sign_app_withoutInternet
+ with:
+ releaseDir: app/build/outputs/apk/withoutInternet/release
+ signingKey: ${{ secrets.SIGNINGKEY_BASE64 }}
+ keyAlias: ${{ secrets.KEY_ALIAS }}
+ keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
+ keyPassword: ${{ secrets.KEY_PASSWORD }}
+ buildToolsVersion: 33.0.0
+
+ - name: Upload Artifact - WithoutInternet
+ uses: actions/upload-artifact@v4
+ with:
+ name: Signed app bundle - WithoutInternet
+ path: app/build/outputs/apk/withoutInternet/release/*.apk
+ retention-days: 3
\ No newline at end of file
diff --git a/.github/workflows/android-release_ci.yml b/.github/workflows/android-release_ci.yml
index a1da161..b097c4e 100644
--- a/.github/workflows/android-release_ci.yml
+++ b/.github/workflows/android-release_ci.yml
@@ -35,15 +35,15 @@ jobs:
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- - name: Build
- run: ./gradlew clean && ./gradlew assembleRelease
+ - name: Build with Gradle
+ run: ./gradlew clean && ./gradlew assembleAppWithInternetRelease && ./gradlew assembleAppWithoutInternetRelease
- - name: Sign APK
+ - name: Sign APK - WithInternet
uses: ilharp/sign-android-release@v1
# ID used to access action output
- id: sign_app
+ id: sign_app_withInternet
with:
- releaseDir: app/build/outputs/apk/release
+ releaseDir: app/build/outputs/apk/withInternet/release
signingKey: ${{ secrets.SIGNINGKEY_BASE64 }}
keyAlias: ${{ secrets.KEY_ALIAS }}
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
@@ -54,7 +54,28 @@ jobs:
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GIT_BOT_TOKEN }}
- file: ${{steps.sign_app.outputs.signedFile}}
+ file: ${{steps.sign_app_withInternet.outputs.signedFile}}
+ asset_name: EasyLauncher-Internet-${{ github.ref_name }}-Signed.apk
+ tag: ${{ github.ref }}
+ overwrite: true
+
+ - name: Sign APK - WithInternet
+ uses: ilharp/sign-android-release@v1
+ # ID used to access action output
+ id: sign_app_withoutInternet
+ with:
+ releaseDir: app/build/outputs/apk/withoutInternet/release
+ signingKey: ${{ secrets.SIGNINGKEY_BASE64 }}
+ keyAlias: ${{ secrets.KEY_ALIAS }}
+ keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
+ keyPassword: ${{ secrets.KEY_PASSWORD }}
+ buildToolsVersion: 33.0.0
+
+ - name: Release to GitHub
+ uses: svenstaro/upload-release-action@v2
+ with:
+ repo_token: ${{ secrets.GIT_BOT_TOKEN }}
+ file: ${{steps.sign_app_withoutInternet.outputs.signedFile}}
asset_name: EasyLauncher-${{ github.ref_name }}-Signed.apk
tag: ${{ github.ref }}
overwrite: true
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 4ef6d66..0ea40be 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,3 +1,5 @@
+import java.util.Properties
+
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
@@ -21,6 +23,17 @@ android {
versionName = "0.1.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ manifestPlaceholders["internetPermission"] = "android.permission.INTERNET"
+
+ val weatherFile = project.rootProject.file("weather.properties")
+ val properties = Properties()
+ properties.load(weatherFile.inputStream())
+ val apiKey = properties.getProperty("WEATHER_API_KEY") ?: ""
+ buildConfigField(
+ type = "String",
+ name = "API_KEY",
+ value = "\"$apiKey\""
+ )
}
buildTypes {
@@ -65,6 +78,21 @@ android {
buildConfig = true
}
+ flavorDimensions.add("internet")
+ productFlavors {
+ create("withInternet") {
+ dimension = "internet"
+ manifestPlaceholders["hasInternetPermission"] = true
+ manifestPlaceholders["internetPermission"] = "android.permission.INTERNET"
+ }
+
+ create("withoutInternet") {
+ dimension = "internet"
+ manifestPlaceholders["hasInternetPermission"] = false
+ manifestPlaceholders["internetPermission"] = "REMOVE"
+ }
+ }
+
composeOptions {
kotlinCompilerExtensionVersion = "1.5.14"
}
@@ -96,6 +124,8 @@ dependencies {
implementation(libs.core.ktx)
implementation(libs.appcompat)
implementation(libs.material)
+ implementation(libs.retrofit)
+ implementation(libs.converter.gson)
implementation(libs.constraintlayout)
implementation(libs.navigation.fragment.ktx)
implementation(libs.navigation.ui.ktx)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 675a468..a2d606b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,6 +5,8 @@
+
+
@@ -18,6 +20,9 @@
tools:ignore="ProtectedPermissions" />
+
+
+
@@ -32,7 +37,9 @@
android:icon="@drawable/app_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
- android:theme="@style/Theme.Launcher">
+ android:theme="@style/Theme.Launcher"
+ android:enableOnBackInvokedCallback="true"
+ tools:targetApi="tiramisu">
{
view.setBackgroundColor(
context.resources.getColor(
- R.color.blackTrans25,
+ R.color.blackTrans10,
context.theme
)
)
@@ -73,7 +80,7 @@ class AppHelper @Inject constructor() {
Configuration.UI_MODE_NIGHT_NO -> {
view.setBackgroundColor(
context.resources.getColor(
- R.color.whiteTrans25,
+ R.color.whiteTrans10,
context.theme
)
)
@@ -242,4 +249,73 @@ class AppHelper @Inject constructor() {
}
}
}
+
+ suspend fun fetchWeatherData(
+ context: Context,
+ latitude: Float,
+ longitude: Float
+ ): WeatherResponse {
+ // Check if cached data is available and not expired
+ val cachedWeatherData = context.getWeatherDataFromCache()
+ if (cachedWeatherData != null && System.currentTimeMillis() - cachedWeatherData.timestamp < TimeUnit.MINUTES.toMillis(
+ 5
+ )
+ ) {
+ return cachedWeatherData.weatherResponse
+ }
+
+ // Fetch weather data from the network
+ val apiKey = BuildConfig.API_KEY
+ val units = "metric"
+
+ val retrofit = Retrofit.Builder()
+ .baseUrl("https://api.openweathermap.org/data/2.5/")
+ .addConverterFactory(GsonConverterFactory.create())
+ .build()
+
+ val service = retrofit.create(WeatherApiService::class.java)
+
+ return withContext(Dispatchers.IO) {
+ val response = service.getWeather("$latitude", "$longitude", units, apiKey).execute()
+ if (response.isSuccessful) {
+ val weatherResponse = response.body()
+ if (weatherResponse != null) {
+ // Cache the fetched weather data
+ context.cacheWeatherData(weatherResponse)
+ weatherResponse
+ } else {
+ throw NullPointerException("Weather response body is null")
+ }
+ } else {
+ throw Exception("Failed to fetch weather data: ${response.errorBody()}")
+ }
+ }
+ }
+
+ // Function to cache weather data
+ private fun Context.cacheWeatherData(weatherResponse: WeatherResponse) {
+ val timestamp = System.currentTimeMillis()
+ val cachedData = CachedWeatherData(timestamp, weatherResponse)
+ // Save cached data to SharedPreferences
+ val sharedPreferences = getSharedPreferences(Constants.WEATHER_PREFS, Context.MODE_PRIVATE)
+ sharedPreferences.edit()
+ .putLong("cachedDataTimestamp", cachedData.timestamp)
+ .putString("weatherResponse", Gson().toJson(cachedData.weatherResponse))
+ .apply()
+ }
+
+ // Function to retrieve weather data from cache
+ private fun Context.getWeatherDataFromCache(): CachedWeatherData? {
+ val sharedPreferences = getSharedPreferences("WeatherCache", Context.MODE_PRIVATE)
+ val timestamp = sharedPreferences.getLong("timestamp", -1)
+ val weatherResponseJson = sharedPreferences.getString("weatherResponse", null)
+ if (timestamp != -1L && weatherResponseJson != null) {
+ val weatherResponse = Gson().fromJson(weatherResponseJson, WeatherResponse::class.java)
+ return CachedWeatherData(timestamp, weatherResponse)
+ }
+ return null
+ }
+
+ // Data class to hold cached weather data along with timestamp
+ data class CachedWeatherData(val timestamp: Long, val weatherResponse: WeatherResponse)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/github/droidworksstudio/launcher/helper/PreferenceHelper.kt b/app/src/main/java/com/github/droidworksstudio/launcher/helper/PreferenceHelper.kt
index 84ced7d..d1a424d 100644
--- a/app/src/main/java/com/github/droidworksstudio/launcher/helper/PreferenceHelper.kt
+++ b/app/src/main/java/com/github/droidworksstudio/launcher/helper/PreferenceHelper.kt
@@ -50,6 +50,14 @@ class PreferenceHelper @Inject constructor(@ApplicationContext context: Context)
get() = prefs.getInt(Constants.DAILY_WORD_COLOR, 0xFFFFFFFF.toInt())
set(value) = prefs.edit().putInt(Constants.DAILY_WORD_COLOR, value).apply()
+ var widgetBackgroundColor: Int
+ get() = prefs.getInt(Constants.WIDGET_BACKGROUND_COLOR, 0xFF000000.toInt())
+ set(value) = prefs.edit().putInt(Constants.WIDGET_BACKGROUND_COLOR, value).apply()
+
+ var widgetTextColor: Int
+ get() = prefs.getInt(Constants.WIDGET_TEXT_COLOR, 0xFFFFFFFF.toInt())
+ set(value) = prefs.edit().putInt(Constants.WIDGET_TEXT_COLOR, value).apply()
+
var appColor: Int
get() = prefs.getInt(Constants.APP_COLOR, 0xFFFFFFFF.toInt())
set(value) = prefs.edit().putInt(Constants.APP_COLOR, value).apply()
@@ -145,6 +153,21 @@ class PreferenceHelper @Inject constructor(@ApplicationContext context: Context)
get() = loadAction(Constants.DOUBLE_TAP_ACTION, Constants.Action.LockScreen)
set(value) = storeAction(Constants.DOUBLE_TAP_ACTION, value)
+ var weatherUnits: Constants.Units
+ get() {
+ return try {
+ Constants.Units.valueOf(
+ prefs.getString(
+ Constants.WEATHER_UNITS,
+ Constants.Units.Metric.name
+ ).toString()
+ )
+ } catch (_: Exception) {
+ Constants.Units.Metric
+ }
+ }
+ set(value) = prefs.edit().putString(Constants.WEATHER_UNITS, value.name).apply()
+
private fun loadAction(prefString: String, default: Constants.Action): Constants.Action {
val string = prefs.getString(
prefString,
diff --git a/app/src/main/java/com/github/droidworksstudio/launcher/helper/weather/WeatherResponse.kt b/app/src/main/java/com/github/droidworksstudio/launcher/helper/weather/WeatherResponse.kt
new file mode 100644
index 0000000..ddc55de
--- /dev/null
+++ b/app/src/main/java/com/github/droidworksstudio/launcher/helper/weather/WeatherResponse.kt
@@ -0,0 +1,26 @@
+package com.github.droidworksstudio.launcher.helper.weather
+
+data class WeatherResponse(
+ val name: String,
+ val main: Main,
+ val wind: Wind,
+ val weather: List
+)
+
+data class Wind(
+ val speed: Double,
+ val deg: Int
+)
+
+data class Main(
+ val temp: Double,
+ val feels_like: Double,
+ val temp_min: Double,
+ val temp_max: Double,
+ val pressure: Int,
+ val humidity: Int
+)
+
+data class Weather(
+ val description: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/github/droidworksstudio/launcher/ui/activities/MainActivity.kt b/app/src/main/java/com/github/droidworksstudio/launcher/ui/activities/MainActivity.kt
index 0551897..5665910 100644
--- a/app/src/main/java/com/github/droidworksstudio/launcher/ui/activities/MainActivity.kt
+++ b/app/src/main/java/com/github/droidworksstudio/launcher/ui/activities/MainActivity.kt
@@ -1,26 +1,42 @@
package com.github.droidworksstudio.launcher.ui.activities
+import android.Manifest
import android.annotation.SuppressLint
+import android.content.Context
+import android.content.SharedPreferences
import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.location.Location
+import android.location.LocationListener
+import android.location.LocationManager
import android.os.Build
import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+import android.widget.Toast
import androidx.activity.viewModels
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
+import com.github.droidworksstudio.common.hasInternetPermission
import com.github.droidworksstudio.common.isTablet
+import com.github.droidworksstudio.launcher.BuildConfig
import com.github.droidworksstudio.launcher.R
import com.github.droidworksstudio.launcher.databinding.ActivityMainBinding
import com.github.droidworksstudio.launcher.helper.AppHelper
import com.github.droidworksstudio.launcher.helper.PreferenceHelper
+import com.github.droidworksstudio.launcher.utils.Constants
import com.github.droidworksstudio.launcher.viewmodel.AppViewModel
import com.github.droidworksstudio.launcher.viewmodel.PreferenceViewModel
import dagger.hilt.android.AndroidEntryPoint
@@ -29,7 +45,13 @@ import javax.inject.Inject
@AndroidEntryPoint
-class MainActivity : AppCompatActivity() {
+class MainActivity : AppCompatActivity(), LocationListener {
+
+ private lateinit var locationManager: LocationManager
+
+ companion object {
+ private const val REQUEST_LOCATION_PERMISSION_CODE = 1001
+ }
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var binding: ActivityMainBinding
@@ -45,15 +67,78 @@ class MainActivity : AppCompatActivity() {
@Inject
lateinit var appHelper: AppHelper
+ private lateinit var sharedPreferences: SharedPreferences
+ private lateinit var handler: Handler
+
+ private var lastKnownLocation: Location? = null
+ private val saveLocationRunnable = object : Runnable {
+ override fun run() {
+ lastKnownLocation?.let { location ->
+ saveLocation(location.latitude.toFloat(), location.longitude.toFloat())
+ }
+ handler.postDelayed(this, 30000) // Run every 30 seconds
+ }
+ }
+
+ private fun saveLocation(latitude: Float = 0f, longitude: Float = 0f) {
+ with(sharedPreferences.edit()) {
+ putFloat(Constants.LATITUDE, latitude)
+ putFloat(Constants.LONGITUDE, longitude)
+ apply()
+ }
+ }
+
+ @SuppressLint("InlinedApi")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ Log.d("BuildConfig.FLAVOR", BuildConfig.FLAVOR)
+
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
+ locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
+ sharedPreferences = getSharedPreferences(Constants.WEATHER_PREFS, Context.MODE_PRIVATE)
+ handler = Handler(Looper.getMainLooper())
+
initializeDependencies()
setupNavController()
setupOrientation()
+
+ if (applicationContext.hasInternetPermission()) {
+ checkLocationPermission()
+ }
+ }
+
+ override fun onPause() {
+ super.onPause()
+ if (applicationContext.hasInternetPermission()) {
+ if (ContextCompat.checkSelfPermission(
+ this,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ )
+ == PackageManager.PERMISSION_GRANTED
+ ) {
+ locationManager.removeUpdates(this)
+ }
+ handler.removeCallbacks(saveLocationRunnable)
+ }
+
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ if (applicationContext.hasInternetPermission()) {
+ if (ContextCompat.checkSelfPermission(
+ this,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ )
+ == PackageManager.PERMISSION_GRANTED
+ ) {
+ locationManager.removeUpdates(this)
+ }
+ handler.removeCallbacks(saveLocationRunnable)
+ }
}
private fun initializeDependencies() {
@@ -98,6 +183,74 @@ class MainActivity : AppCompatActivity() {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
+ private fun checkLocationPermission() {
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
+ != PackageManager.PERMISSION_GRANTED
+ ) {
+ ActivityCompat.requestPermissions(
+ this,
+ arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
+ REQUEST_LOCATION_PERMISSION_CODE
+ )
+ } else {
+ // Permission already granted, proceed with getting location
+ getLocation()
+ }
+ }
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array,
+ grantResults: IntArray
+ ) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ if (requestCode == REQUEST_LOCATION_PERMISSION_CODE) {
+ if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ // Permission granted, proceed with getting location
+ getLocation()
+ } else {
+ // Permission denied, show a message to the user
+ Toast.makeText(this, "Location permission denied", Toast.LENGTH_SHORT).show()
+ }
+ }
+ }
+
+ private fun getLocation() {
+ try {
+ // Check if permission is granted
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
+ == PackageManager.PERMISSION_GRANTED
+ ) {
+ locationManager.requestLocationUpdates(
+ LocationManager.GPS_PROVIDER,
+ 0,
+ 0f,
+ this
+ )
+ locationManager.requestLocationUpdates(
+ LocationManager.NETWORK_PROVIDER,
+ 0,
+ 0f,
+ this
+ )
+ // Start the handler to save location every 30 seconds
+ handler.post(saveLocationRunnable)
+ }
+ } catch (e: SecurityException) {
+ e.printStackTrace()
+ }
+ }
+
+ override fun onLocationChanged(location: Location) {
+ // Get latitude and longitude
+ val latitude = location.latitude.toFloat()
+ val longitude = location.longitude.toFloat()
+
+ // Save the location data immediately
+ saveLocation(latitude, longitude)
+ lastKnownLocation = location
+ }
+
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return true
diff --git a/app/src/main/java/com/github/droidworksstudio/launcher/ui/bottomsheetdialog/ColorBottomSheetDialogFragment.kt b/app/src/main/java/com/github/droidworksstudio/launcher/ui/bottomsheetdialog/ColorBottomSheetDialogFragment.kt
index 060a073..fcf923c 100644
--- a/app/src/main/java/com/github/droidworksstudio/launcher/ui/bottomsheetdialog/ColorBottomSheetDialogFragment.kt
+++ b/app/src/main/java/com/github/droidworksstudio/launcher/ui/bottomsheetdialog/ColorBottomSheetDialogFragment.kt
@@ -79,6 +79,16 @@ class ColorBottomSheetDialogFragment : BottomSheetDialogFragment() {
text = bottomDialogHelper.getColorText(preferenceHelper.dailyWordColor)
setTextColor(preferenceHelper.dailyWordColor)
}
+
+ binding.selectWidgetBackgroundColor.apply {
+ text = bottomDialogHelper.getColorText(preferenceHelper.widgetBackgroundColor)
+ setTextColor(preferenceHelper.widgetBackgroundColor)
+ }
+
+ binding.selectWidgetTextColor.apply {
+ text = bottomDialogHelper.getColorText(preferenceHelper.widgetTextColor)
+ setTextColor(preferenceHelper.widgetTextColor)
+ }
}
@@ -122,6 +132,22 @@ class ColorBottomSheetDialogFragment : BottomSheetDialogFragment() {
preferenceHelper.dailyWordColor
)
}
+
+ binding.bottomColorWidgetBackgroundView.setOnClickListener {
+ showColorPickerDialog(
+ binding.selectWidgetBackgroundColor,
+ REQUEST_KEY_WIDGET_BACKGROUND_COLOR,
+ preferenceHelper.widgetBackgroundColor
+ )
+ }
+
+ binding.bottomColorWidgetTextView.setOnClickListener {
+ showColorPickerDialog(
+ binding.selectWidgetTextColor,
+ REQUEST_KEY_WIDGET_TEXT_COLOR,
+ preferenceHelper.widgetTextColor
+ )
+ }
}
private fun showColorPickerDialog(view: View, requestCode: String, color: Int) {
@@ -163,6 +189,16 @@ class ColorBottomSheetDialogFragment : BottomSheetDialogFragment() {
preferenceViewModel.setTimeColor(pickedColor)
Log.d("Tag", "Settings Time Color: ${Integer.toHexString(color)}")
}
+
+ REQUEST_KEY_WIDGET_BACKGROUND_COLOR -> {
+ preferenceViewModel.setWidgetBackgroundColor(pickedColor)
+ Log.d("Tag", "Settings Widget Background Color: ${Integer.toHexString(color)}")
+ }
+
+ REQUEST_KEY_WIDGET_TEXT_COLOR -> {
+ preferenceViewModel.setWidgetTextColor(pickedColor)
+ Log.d("Tag", "Settings Widget Text Color: ${Integer.toHexString(color)}")
+ }
}
}) {
context?.showLongToast("onCancel")
@@ -175,5 +211,7 @@ class ColorBottomSheetDialogFragment : BottomSheetDialogFragment() {
private const val REQUEST_KEY_DAILY_WORD_COLOR = "REQUEST_DAILY_WORD_COLOR"
private const val REQUEST_KEY_APP_COLOR = "REQUEST_APP_COLOR"
private const val REQUEST_KEY_BATTERY_COLOR = "REQUEST_BATTERY_COLOR"
+ private const val REQUEST_KEY_WIDGET_BACKGROUND_COLOR = "REQUEST_WIDGET_BACKGROUND_COLOR"
+ private const val REQUEST_KEY_WIDGET_TEXT_COLOR = "REQUEST_WIDGET_TEXT_COLOR"
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/github/droidworksstudio/launcher/ui/widgetmanager/WidgetAdapter.kt b/app/src/main/java/com/github/droidworksstudio/launcher/ui/widgetmanager/WidgetAdapter.kt
deleted file mode 100644
index 67c6041..0000000
--- a/app/src/main/java/com/github/droidworksstudio/launcher/ui/widgetmanager/WidgetAdapter.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.github.droidworksstudio.launcher.ui.widgetmanager
-
-import android.appwidget.AppWidgetHost
-import android.appwidget.AppWidgetManager
-import android.content.Context
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.FrameLayout
-import androidx.recyclerview.widget.RecyclerView
-import com.github.droidworksstudio.launcher.R
-
-class WidgetAdapter(
- private val context: Context,
- private val appWidgetHost: AppWidgetHost,
- private val onWidgetClick: (Int) -> Unit
-) : RecyclerView.Adapter() {
-
- private val widgetItems = mutableListOf()
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WidgetViewHolder {
- val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_widget, parent, false)
- return WidgetViewHolder(itemView)
- }
-
- override fun onBindViewHolder(holder: WidgetViewHolder, position: Int) {
- val widgetItem = widgetItems[position]
- holder.bind(widgetItem)
- }
-
- override fun getItemCount(): Int = widgetItems.size
-
- fun addWidget(widgetItem: WidgetItem) {
- widgetItems.add(widgetItem)
- notifyItemInserted(widgetItems.size - 1)
- }
-
- fun removeWidget(appWidgetId: Int) {
- val position = widgetItems.indexOfFirst { it.appWidgetId == appWidgetId }
- if (position != -1) {
- widgetItems.removeAt(position)
- notifyItemRemoved(position)
- }
- }
-
- fun getWidgetIds(): List = widgetItems.map { it.appWidgetId }
-
- inner class WidgetViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
- fun bind(widgetItem: WidgetItem) {
- val hostView = appWidgetHost.createView(context, widgetItem.appWidgetId, widgetItem.appWidgetInfo)
- itemView.findViewById(R.id.widget_frame).addView(hostView)
-
- itemView.setOnClickListener {
- onWidgetClick(widgetItem.appWidgetId)
- }
- }
- }
-}
diff --git a/app/src/main/java/com/github/droidworksstudio/launcher/ui/widgetmanager/WidgetItem.kt b/app/src/main/java/com/github/droidworksstudio/launcher/ui/widgetmanager/WidgetItem.kt
deleted file mode 100644
index 3748501..0000000
--- a/app/src/main/java/com/github/droidworksstudio/launcher/ui/widgetmanager/WidgetItem.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.github.droidworksstudio.launcher.ui.widgetmanager
-
-import android.appwidget.AppWidgetProviderInfo
-
-data class WidgetItem(val appWidgetId: Int, val appWidgetInfo: AppWidgetProviderInfo?)
diff --git a/app/src/main/java/com/github/droidworksstudio/launcher/ui/widgetmanager/WidgetManagerFragment.kt b/app/src/main/java/com/github/droidworksstudio/launcher/ui/widgetmanager/WidgetManagerFragment.kt
deleted file mode 100644
index 47562f3..0000000
--- a/app/src/main/java/com/github/droidworksstudio/launcher/ui/widgetmanager/WidgetManagerFragment.kt
+++ /dev/null
@@ -1,208 +0,0 @@
-package com.github.droidworksstudio.launcher.ui.widgetmanager
-
-import android.annotation.SuppressLint
-import android.app.Activity
-import android.appwidget.AppWidgetHost
-import android.appwidget.AppWidgetManager
-import android.content.Context
-import android.content.Intent
-import android.os.Bundle
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.activity.result.ActivityResultLauncher
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.fragment.app.Fragment
-import androidx.navigation.fragment.findNavController
-import androidx.recyclerview.widget.LinearLayoutManager
-import com.github.droidworksstudio.launcher.utils.Constants
-import com.github.droidworksstudio.launcher.databinding.FragmentWidgetManagerBinding
-import com.github.droidworksstudio.launcher.helper.AppHelper
-import com.github.droidworksstudio.launcher.listener.OnSwipeTouchListener
-import dagger.hilt.android.AndroidEntryPoint
-import javax.inject.Inject
-
-/**
- * A simple [Fragment] subclass as the second destination in the navigation.
- */
-@AndroidEntryPoint
-class WidgetManagerFragment : Fragment(),
- WidgetOptionsDialogFragment.WidgetOptionsListener {
-
- private var _binding: FragmentWidgetManagerBinding? = null
-
- private val binding get() = _binding!!
-
- private lateinit var pickWidgetLauncher: ActivityResultLauncher
- private lateinit var createWidgetLauncher: ActivityResultLauncher
-
- private lateinit var appWidgetManager: AppWidgetManager
- private lateinit var appWidgetHost: AppWidgetHost
- private lateinit var widgetAdapter: WidgetAdapter
-
- @Inject
- lateinit var appHelper: AppHelper
-
- private lateinit var context: Context
-
- override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
-
- _binding = FragmentWidgetManagerBinding.inflate(inflater, container, false)
-
- return binding.root
-
- }
-
- @SuppressLint("ClickableViewAccessibility")
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- appHelper.dayNightMod(requireContext(), binding.drawBackground)
- super.onViewCreated(view, savedInstanceState)
-
- context = requireContext()
-
- appWidgetManager = AppWidgetManager.getInstance(requireContext())
- appWidgetHost = AppWidgetHost(requireContext(), Constants.APP_WIDGET_HOST_ID)
-
- widgetAdapter = WidgetAdapter(requireContext(), appWidgetHost) { appWidgetId ->
- showWidgetOptionsDialog(appWidgetId)
- }
-
- binding.widgetContainer.apply {
- layoutManager = LinearLayoutManager(requireContext())
- adapter = widgetAdapter
- }
-
- // Load saved widgets
- val savedWidgetIds = loadWidgetIds()
- for (widgetId in savedWidgetIds) {
- addWidget(widgetId)
- }
-
- // Initialize the ActivityResultLaunchers
- pickWidgetLauncher =
- registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
- if (result.resultCode == Activity.RESULT_OK) {
- val data = result.data
- data?.let {
- val appWidgetId = it.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
- if (appWidgetId != -1) {
- val appWidgetInfo = appWidgetManager.getAppWidgetInfo(appWidgetId)
- if (appWidgetInfo?.configure != null) {
- val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE)
- intent.component = appWidgetInfo.configure
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
- createWidgetLauncher.launch(intent)
- } else {
- addWidget(appWidgetId)
- }
- }
- }
- } else if (result.resultCode == Activity.RESULT_CANCELED) {
- val appWidgetId =
- result.data?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
- if (appWidgetId != null && appWidgetId != -1) {
- appWidgetHost.deleteAppWidgetId(appWidgetId)
- }
- }
- }
-
- createWidgetLauncher =
- registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
- if (result.resultCode == Activity.RESULT_OK) {
- val data = result.data
- data?.let {
- val appWidgetId = it.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
- if (appWidgetId != -1) {
- addWidget(appWidgetId)
- }
- }
- } else if (result.resultCode == Activity.RESULT_CANCELED) {
- val appWidgetId =
- result.data?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
- if (appWidgetId != null && appWidgetId != -1) {
- appWidgetHost.deleteAppWidgetId(appWidgetId)
- }
- }
- }
-
- binding.widgetParent.setOnTouchListener(getSwipeGestureListener(context))
- }
-
- private fun getSwipeGestureListener(context: Context): View.OnTouchListener {
- return object : OnSwipeTouchListener(context) {
- override fun onLongClick() {
- super.onLongClick()
- selectWidget()
- }
-
- override fun onSwipeLeft() {
- super.onSwipeLeft()
- findNavController().popBackStack()
- }
-
- override fun onSwipeRight() {
- super.onSwipeRight()
- findNavController().popBackStack()
- }
- }
- }
-
- private fun selectWidget() {
- val appWidgetId = appWidgetHost.allocateAppWidgetId()
- val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_PICK)
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
- pickWidgetLauncher.launch(intent)
- }
-
-
- private fun addWidget(appWidgetId: Int) {
- val appWidgetInfo = appWidgetManager.getAppWidgetInfo(appWidgetId)
- appWidgetInfo?.let {
- val widgetItem = WidgetItem(appWidgetId, it)
- widgetAdapter.addWidget(widgetItem)
- saveWidgetIds(widgetAdapter.getWidgetIds()) // Save the updated widget IDs
- } ?: Log.e("WidgetManagerFragment", "Failed to get app widget info for ID: $appWidgetId")
- }
-
- override fun onRemoveWidget(appWidgetId: Int) {
- appWidgetHost.deleteAppWidgetId(appWidgetId)
- widgetAdapter.removeWidget(appWidgetId)
- saveWidgetIds(widgetAdapter.getWidgetIds())
- }
-
- private fun showWidgetOptionsDialog(appWidgetId: Int) {
- val dialog = WidgetOptionsDialogFragment.newInstance(appWidgetId)
- dialog.show(childFragmentManager, "WidgetOptionsDialog")
- }
-
- override fun onOpenAppWidget(appWidgetId: Int) {
- // Implement the logic to open the widget's configuration or app
- // This is typically dependent on the widget and its associated app
- }
-
- private fun saveWidgetIds(widgetIds: List) {
- val sharedPreferences =
- requireContext().getSharedPreferences(Constants.WIDGETS_PREFS, Activity.MODE_PRIVATE)
- val editor = sharedPreferences.edit()
- val widgetIdsSet = widgetIds.map { it.toString() }.toSet()
- editor.putStringSet(Constants.APP_WIDGETS_ID, widgetIdsSet)
- editor.apply()
- }
-
- private fun loadWidgetIds(): List {
- val sharedPreferences =
- requireContext().getSharedPreferences(Constants.WIDGETS_PREFS, Activity.MODE_PRIVATE)
- val widgetIdsSet = sharedPreferences.getStringSet(Constants.APP_WIDGETS_ID, emptySet())
- return widgetIdsSet?.map { it.toInt() } ?: emptyList()
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- _binding = null
- }
-}
-
diff --git a/app/src/main/java/com/github/droidworksstudio/launcher/ui/widgetmanager/WidgetOptionsDialogFragment.kt b/app/src/main/java/com/github/droidworksstudio/launcher/ui/widgetmanager/WidgetOptionsDialogFragment.kt
deleted file mode 100644
index 156b965..0000000
--- a/app/src/main/java/com/github/droidworksstudio/launcher/ui/widgetmanager/WidgetOptionsDialogFragment.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.github.droidworksstudio.launcher.ui.widgetmanager
-
-import android.app.Dialog
-import android.os.Bundle
-import androidx.appcompat.app.AlertDialog
-import androidx.fragment.app.DialogFragment
-import com.github.droidworksstudio.launcher.utils.Constants
-
-class WidgetOptionsDialogFragment : DialogFragment() {
-
- interface WidgetOptionsListener {
- fun onRemoveWidget(appWidgetId: Int)
- fun onOpenAppWidget(appWidgetId: Int)
- }
-
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
- val appWidgetId =
- arguments?.getInt(ARG_APP_WIDGET_ID) ?: return super.onCreateDialog(savedInstanceState)
-
- val builder = AlertDialog.Builder(requireContext())
- builder.setTitle("Widget Options")
- .setItems(arrayOf("Remove Widget", "Open Widget")) { _, which ->
- val listener = parentFragment as? WidgetOptionsListener
- when (which) {
- 0 -> listener?.onRemoveWidget(appWidgetId)
- 1 -> listener?.onOpenAppWidget(appWidgetId)
- }
- }
- return builder.create()
- }
-
- companion object {
- private const val ARG_APP_WIDGET_ID = Constants.APP_WIDGETS_ID
-
- fun newInstance(appWidgetId: Int): WidgetOptionsDialogFragment {
- val fragment = WidgetOptionsDialogFragment()
- val args = Bundle().apply {
- putInt(ARG_APP_WIDGET_ID, appWidgetId)
- }
- fragment.arguments = args
- return fragment
- }
- }
-}
diff --git a/app/src/main/java/com/github/droidworksstudio/launcher/ui/widgets/WidgetFragment.kt b/app/src/main/java/com/github/droidworksstudio/launcher/ui/widgets/WidgetFragment.kt
new file mode 100644
index 0000000..23393d7
--- /dev/null
+++ b/app/src/main/java/com/github/droidworksstudio/launcher/ui/widgets/WidgetFragment.kt
@@ -0,0 +1,158 @@
+package com.github.droidworksstudio.launcher.ui.widgets
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.drawable.GradientDrawable
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.lifecycleScope
+import androidx.navigation.NavController
+import androidx.navigation.fragment.findNavController
+import com.github.droidworksstudio.common.hideKeyboard
+import com.github.droidworksstudio.launcher.R
+import com.github.droidworksstudio.launcher.databinding.FragmentWidgetsBinding
+import com.github.droidworksstudio.launcher.helper.AppHelper
+import com.github.droidworksstudio.launcher.helper.PreferenceHelper
+import com.github.droidworksstudio.launcher.listener.OnSwipeTouchListener
+import com.github.droidworksstudio.launcher.listener.ScrollEventListener
+import com.github.droidworksstudio.launcher.utils.Constants
+import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class WidgetFragment : Fragment(),
+ ScrollEventListener {
+ private var _binding: FragmentWidgetsBinding? = null
+ private val binding get() = _binding!!
+
+ @Inject
+ lateinit var preferenceHelper: PreferenceHelper
+
+ @Inject
+ lateinit var appHelper: AppHelper
+
+ private lateinit var navController: NavController
+
+ private lateinit var context: Context
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ // Inflate the layout for this fragment
+ _binding = FragmentWidgetsBinding.inflate(inflater, container, false)
+ _binding = binding
+
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ navController = findNavController()
+ // Set according to the system theme mode
+ appHelper.dayNightMod(requireContext(), binding.mainLayout)
+ super.onViewCreated(view, savedInstanceState)
+
+ initializeInjectedDependencies()
+ setupWeatherWidget()
+ observeSwipeTouchListener()
+ observeClickListener()
+ }
+
+ private fun initializeInjectedDependencies() {
+ context = requireContext()
+ binding.nestScrollView.hideKeyboard()
+
+ binding.nestScrollView.scrollEventListener = this
+ }
+
+ private fun setupWeatherWidget() {
+ val sharedPreferences =
+ context.getSharedPreferences(Constants.WEATHER_PREFS, Context.MODE_PRIVATE)
+ val latitude = sharedPreferences.getFloat(Constants.LATITUDE, 0f)
+ val longitude = sharedPreferences.getFloat(Constants.LONGITUDE, 0f)
+ lifecycleScope.launch {
+ try {
+ binding.weatherRoot.visibility = View.VISIBLE
+ val weatherResponse = appHelper.fetchWeatherData(context, latitude, longitude)
+ val temperatureScale =
+ if (preferenceHelper.weatherUnits == Constants.Units.Metric) getString(R.string.weather_c) else getString(
+ R.string.weather_f
+ )
+ val speedScale =
+ if (preferenceHelper.weatherUnits == Constants.Units.Metric) getString(R.string.weather_mps) else getString(
+ R.string.weather_mph
+ )
+
+ binding.weatherCity.setTextColor(preferenceHelper.widgetTextColor)
+ binding.weatherTemperature.setTextColor(preferenceHelper.widgetTextColor)
+ binding.weatherDescription.setTextColor(preferenceHelper.widgetTextColor)
+ binding.weatherWind.setTextColor(preferenceHelper.widgetTextColor)
+ binding.weatherHumidity.setTextColor(preferenceHelper.widgetTextColor)
+ binding.weatherPressure.setTextColor(preferenceHelper.widgetTextColor)
+
+ binding.weatherCity.text =
+ getString(R.string.widget_weather_location, weatherResponse.name)
+ binding.weatherTemperature.text =
+ getString(
+ R.string.widget_weather_temp,
+ weatherResponse.main.temp,
+ temperatureScale
+ )
+ binding.weatherDescription.text =
+ getString(
+ R.string.widget_weather_description,
+ weatherResponse.weather[0].description
+ )
+ binding.weatherWind.text =
+ getString(R.string.widget_weather_wind, weatherResponse.wind.speed, speedScale)
+ binding.weatherHumidity.text =
+ getString(R.string.widget_weather_humidity, weatherResponse.main.humidity)
+ binding.weatherPressure.text =
+ getString(R.string.widget_weather_pressure, weatherResponse.main.pressure)
+
+ val weatherWidgetDrawable = binding.weatherButtonRefresh.background
+ if (weatherWidgetDrawable is GradientDrawable) {
+ weatherWidgetDrawable.setColor(preferenceHelper.widgetBackgroundColor)
+ }
+
+ binding.weatherButtonRefresh.setColorFilter(preferenceHelper.widgetTextColor)
+ } catch (e: Exception) {
+ binding.weatherRoot.visibility = View.GONE
+ Log.e("Weather", "Failed to fetch weather data: ${e.message}")
+ }
+ }
+ }
+
+
+ @SuppressLint("ClickableViewAccessibility")
+ private fun observeSwipeTouchListener() {
+ binding.touchArea.setOnTouchListener(getSwipeGestureListener(context))
+ binding.nestScrollView.setOnTouchListener(getSwipeGestureListener(context))
+ }
+
+ private fun getSwipeGestureListener(context: Context): View.OnTouchListener {
+ return object : OnSwipeTouchListener(context) {
+ override fun onSwipeLeft() {
+ super.onSwipeLeft()
+ findNavController().popBackStack()
+ }
+
+ override fun onSwipeRight() {
+ super.onSwipeRight()
+ findNavController().popBackStack()
+ }
+ }
+ }
+
+ private fun observeClickListener() {
+ binding.weatherButtonRefresh.setOnClickListener {
+ setupWeatherWidget()
+ }
+ }
+}
+
diff --git a/app/src/main/java/com/github/droidworksstudio/launcher/utils/Constants.kt b/app/src/main/java/com/github/droidworksstudio/launcher/utils/Constants.kt
index 3eda27a..8a3ef17 100644
--- a/app/src/main/java/com/github/droidworksstudio/launcher/utils/Constants.kt
+++ b/app/src/main/java/com/github/droidworksstudio/launcher/utils/Constants.kt
@@ -1,7 +1,6 @@
package com.github.droidworksstudio.launcher.utils
import android.content.Context
-import androidx.compose.ui.res.stringResource
import com.github.droidworksstudio.launcher.R
object Constants {
@@ -9,7 +8,10 @@ object Constants {
const val PACKAGE_NAME_DEBUG = "$PACKAGE_NAME.debug"
const val WIDGETS_PREFS = "EasyLauncherWidgets.pref"
- const val APP_WIDGETS_ID = "APP_WIDGETS_ID"
+
+ const val WEATHER_PREFS = "EasyWeather.pref"
+ const val LATITUDE = "LATITUDE"
+ const val LONGITUDE = "LONGITUDE"
const val PREFS_FILENAME = "EasyLauncher.pref"
const val FIRST_LAUNCH = "FIRST_LAUNCH"
@@ -23,6 +25,8 @@ object Constants {
const val TIME_COLOR = "TIME_COLOR"
const val BATTERY_COLOR = "BATTERY_COLOR"
const val DAILY_WORD_COLOR = "DAILY_WORD_COLOR"
+ const val WIDGET_BACKGROUND_COLOR = "WIDGET_BACKGROUND_COLOR"
+ const val WIDGET_TEXT_COLOR = "WIDGET_TEXT_COLOR"
const val APP_COLOR = "APP_COLOR"
const val BATTERY_TEXT_SIZE = "BATTERY_TEXT_SIZE"
@@ -63,7 +67,8 @@ object Constants {
const val URL_GOOGLE_PLAY_STORE = "https://play.google.com/store/search?c=apps&q"
const val APP_GOOGLE_PLAY_STORE = "market://search?c=apps&q"
- const val APP_WIDGET_HOST_ID = 1024
+ const val WEATHER_UNITS = "WEATHER_UNITS"
+
const val TRIPLE_TAP_DELAY_MS = 300
const val LONG_PRESS_DELAY_MS = 500
@@ -130,4 +135,16 @@ object Constants {
Up,
Down;
}
+
+ enum class Units {
+ Metric,
+ Imperial;
+
+ fun getString(context: Context): String {
+ return when (this) {
+ Metric -> context.getString(R.string.settings_units_metric)
+ Imperial -> context.getString(R.string.settings_units_imperial)
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/github/droidworksstudio/launcher/utils/WeatherApiService.kt b/app/src/main/java/com/github/droidworksstudio/launcher/utils/WeatherApiService.kt
new file mode 100644
index 0000000..a6308b4
--- /dev/null
+++ b/app/src/main/java/com/github/droidworksstudio/launcher/utils/WeatherApiService.kt
@@ -0,0 +1,16 @@
+package com.github.droidworksstudio.launcher.utils
+
+import com.github.droidworksstudio.launcher.helper.weather.WeatherResponse
+import retrofit2.Call
+import retrofit2.http.GET
+import retrofit2.http.Query
+
+interface WeatherApiService {
+ @GET("weather")
+ fun getWeather(
+ @Query("lat") latitude: String,
+ @Query("lon") longitude: String,
+ @Query("units") units: String,
+ @Query("apiKey") apiKey: String
+ ): Call
+}
diff --git a/app/src/main/java/com/github/droidworksstudio/launcher/viewmodel/PreferenceViewModel.kt b/app/src/main/java/com/github/droidworksstudio/launcher/viewmodel/PreferenceViewModel.kt
index 647b312..6a4ec57 100644
--- a/app/src/main/java/com/github/droidworksstudio/launcher/viewmodel/PreferenceViewModel.kt
+++ b/app/src/main/java/com/github/droidworksstudio/launcher/viewmodel/PreferenceViewModel.kt
@@ -27,6 +27,8 @@ class PreferenceViewModel @Inject constructor(
private val timeColorLiveData: MutableLiveData = MutableLiveData()
private val batteryColorLiveData: MutableLiveData = MutableLiveData()
private val dailyWordColorLiveData: MutableLiveData = MutableLiveData()
+ private val widgetBackgroundColorLiveData: MutableLiveData = MutableLiveData()
+ private val widgetTextColorLiveData: MutableLiveData = MutableLiveData()
private val appColorLiveData: MutableLiveData = MutableLiveData()
private val dateTextSizeLiveData: MutableLiveData = MutableLiveData()
private val timeTextSizeLiveData: MutableLiveData = MutableLiveData()
@@ -99,6 +101,16 @@ class PreferenceViewModel @Inject constructor(
timeColorLiveData.postValue(preferenceHelper.timeColor)
}
+ fun setWidgetBackgroundColor(timeColor: Int) {
+ preferenceHelper.widgetBackgroundColor = timeColor
+ widgetBackgroundColorLiveData.postValue(preferenceHelper.widgetBackgroundColor)
+ }
+
+ fun setWidgetTextColor(timeColor: Int) {
+ preferenceHelper.widgetTextColor = timeColor
+ widgetTextColorLiveData.postValue(preferenceHelper.widgetTextColor)
+ }
+
fun setBatteryColor(batteryColor: Int) {
preferenceHelper.batteryColor = batteryColor
batteryColorLiveData.postValue(preferenceHelper.batteryColor)
diff --git a/app/src/main/res/drawable/icon_refresh.xml b/app/src/main/res/drawable/icon_refresh.xml
new file mode 100644
index 0000000..04abe48
--- /dev/null
+++ b/app/src/main/res/drawable/icon_refresh.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/widget_background.xml b/app/src/main/res/drawable/widget_background.xml
new file mode 100644
index 0000000..fd0e43a
--- /dev/null
+++ b/app/src/main/res/drawable/widget_background.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/bottomsheetdialog_color_settings.xml b/app/src/main/res/layout/bottomsheetdialog_color_settings.xml
index 4a89a14..f97690e 100644
--- a/app/src/main/res/layout/bottomsheetdialog_color_settings.xml
+++ b/app/src/main/res/layout/bottomsheetdialog_color_settings.xml
@@ -22,7 +22,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
- style="@style/TextDefaultStyle"/>
+ style="@style/TextDefaultStyle" />
+ style="@style/TextDefaultStyle" />
-
+ style="@style/TextDefaultStyle" />
@@ -130,7 +129,7 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:ignore="RtlHardcoded"
- style="@style/TextDefaultStyle"/>
+ style="@style/TextDefaultStyle" />
@@ -166,7 +165,7 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:ignore="RtlHardcoded"
- style="@style/TextDefaultStyle"/>
+ style="@style/TextDefaultStyle" />
@@ -202,7 +201,79 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:ignore="RtlHardcoded"
- style="@style/TextDefaultStyle"/>
+ style="@style/TextDefaultStyle" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_draw.xml b/app/src/main/res/layout/fragment_draw.xml
index 1d68b42..4279b55 100644
--- a/app/src/main/res/layout/fragment_draw.xml
+++ b/app/src/main/res/layout/fragment_draw.xml
@@ -9,8 +9,7 @@
diff --git a/app/src/main/res/layout/fragment_hidden.xml b/app/src/main/res/layout/fragment_hidden.xml
index 584edb9..0c58a14 100644
--- a/app/src/main/res/layout/fragment_hidden.xml
+++ b/app/src/main/res/layout/fragment_hidden.xml
@@ -9,8 +9,7 @@
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
index 3c899ac..0023c8d 100644
--- a/app/src/main/res/layout/fragment_home.xml
+++ b/app/src/main/res/layout/fragment_home.xml
@@ -11,8 +11,7 @@
diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml
index 02056a6..ad68cad 100644
--- a/app/src/main/res/layout/fragment_settings.xml
+++ b/app/src/main/res/layout/fragment_settings.xml
@@ -8,8 +8,7 @@
diff --git a/app/src/main/res/layout/fragment_widget_manager.xml b/app/src/main/res/layout/fragment_widget_manager.xml
deleted file mode 100644
index b4ba0f4..0000000
--- a/app/src/main/res/layout/fragment_widget_manager.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_widgets.xml b/app/src/main/res/layout/fragment_widgets.xml
new file mode 100644
index 0000000..b54e946
--- /dev/null
+++ b/app/src/main/res/layout/fragment_widgets.xml
@@ -0,0 +1,150 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/widget_options_dialog.xml b/app/src/main/res/layout/widget_options_dialog.xml
deleted file mode 100644
index 16207b8..0000000
--- a/app/src/main/res/layout/widget_options_dialog.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml
index d431e02..abb0ed1 100644
--- a/app/src/main/res/navigation/nav_graph.xml
+++ b/app/src/main/res/navigation/nav_graph.xml
@@ -39,9 +39,9 @@
+ tools:layout="@layout/fragment_widgets" />
+
+ #FF03DAC5
+ #FF018786
+
+ #FFFFFFFF
+ #56A1E6
+
+ #333333
+
+
+ #000000
+ #1A000000
+ #30000000
+ #80000000
+ #CC000000
+ #E6000000
+
+
+ #FFFFFF
+ #1AFFFFFF
+ #30FFFFFF
+ #65FFFFFF
+ #CCFFFFFF
+ #E6FFFFFF
+
+ #4CAF50
+ #AF684C
+ #AF4C4C
+
\ No newline at end of file
diff --git a/app/src/main/res/values/nontranslatable.xml b/app/src/main/res/values/nontranslatable.xml
new file mode 100644
index 0000000..22c93bd
--- /dev/null
+++ b/app/src/main/res/values/nontranslatable.xml
@@ -0,0 +1,20 @@
+
+ %s%%
+
+ %1$s
+ %1$s
+ %1$.2f %2$s
+ %1$s
+
+ m/s
+ mil/h
+ mm
+
+ °C
+ °F
+
+ Feels Like : %s%
+ Wind : %1$.2f %2$s
+ Humidity : %d %%
+ Pressure : %d hPa
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f822b9c..a233e75 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -13,8 +13,7 @@
Favorite Apps
Hidden Apps
-
- %s%%
+
Search
Rename
@@ -72,6 +71,9 @@
Word\u0020:\u0020
Battery\u0020:\u0020
+ Widget Background\u0020:\u0020
+ Widget Text\u0020:\u0020
+
Favorite Apps
Hidden Apps
@@ -107,6 +109,9 @@
Take a ScreenShot
Disabled
+ Metric
+ Imperial
+
Authentication
Log in using your biometric credentials.
Authentication Cancel
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 7cd67f1..7b87608 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -14,6 +14,7 @@ dagger = "2.51.1"
work = "2.9.0"
recyclerview = "1.3.2"
preference = "1.2.1"
+retrofit = "2.11.0"
room = "2.6.1"
biometric = "1.2.0-alpha05"
color-chooser = "0.7.3"
@@ -35,6 +36,9 @@ preference = { group = "androidx.preference", name = "preference-ktx", version.r
biometric-ktx = { group = "androidx.biometric", name = "biometric-ktx", version.ref = "biometric" }
color-chooser = { group = "net.mm2d.color-chooser", name = "color-chooser", version.ref = "color-chooser" }
+retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
+converter-gson = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit" }
+
# Room Packages
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
diff --git a/weather.properties b/weather.properties
new file mode 100644
index 0000000..1bf7ea1
--- /dev/null
+++ b/weather.properties
@@ -0,0 +1 @@
+WEATHER_API_KEY=ba0442331f7232028a3efc3f90c1fa84
\ No newline at end of file