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 @@ - - -