Refactor: More widget stuff and fixing of files.
Signed-off-by: HeCodes2Much <wayne6324@gmail.com>
This commit is contained in:
15
.github/workflows/android-branch_ci.yml
vendored
15
.github/workflows/android-branch_ci.yml
vendored
@@ -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
|
||||
|
||||
15
.github/workflows/android-main_ci.yml
vendored
15
.github/workflows/android-main_ci.yml
vendored
@@ -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
|
||||
|
||||
15
.github/workflows/android-pr_ci.yml
vendored
15
.github/workflows/android-pr_ci.yml
vendored
@@ -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
|
||||
|
||||
35
.github/workflows/android-release_ci-forced.yml
vendored
35
.github/workflows/android-release_ci-forced.yml
vendored
@@ -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
|
||||
33
.github/workflows/android-release_ci.yml
vendored
33
.github/workflows/android-release_ci.yml
vendored
@@ -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
|
||||
|
||||
1
app/.gitignore
vendored
Normal file
1
app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -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)
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
<dist:module dist:instant="true" />
|
||||
|
||||
<uses-permission android:name="${internetPermission}" />
|
||||
|
||||
<uses-permission android:name="android.permission.SET_WALLPAPER" />
|
||||
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||
@@ -18,6 +20,9 @@
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@@ -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">
|
||||
<activity
|
||||
android:name=".ui.activities.LauncherActivity"
|
||||
android:enabled="false"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.github.droidworksstudio.common
|
||||
|
||||
import android.Manifest
|
||||
import android.app.SearchManager
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.ComponentName
|
||||
@@ -432,6 +433,12 @@ private fun isExternalStorageReadable(): Boolean {
|
||||
return Environment.MEDIA_MOUNTED == state || Environment.MEDIA_MOUNTED_READ_ONLY == state
|
||||
}
|
||||
|
||||
fun Context.hasInternetPermission(): Boolean {
|
||||
val permission = Manifest.permission.INTERNET
|
||||
val result = ContextCompat.checkSelfPermission(this, permission)
|
||||
return result == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
fun Context.isPackageInstalled(
|
||||
packageName: String,
|
||||
userHandle: UserHandle = android.os.Process.myUserHandle()
|
||||
|
||||
@@ -9,7 +9,6 @@ import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
@@ -20,11 +19,19 @@ import androidx.navigation.NavOptions
|
||||
import com.github.droidworksstudio.common.backupSharedPreferences
|
||||
import com.github.droidworksstudio.common.restoreSharedPreferences
|
||||
import com.github.droidworksstudio.common.showLongToast
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.github.droidworksstudio.launcher.BuildConfig
|
||||
import com.github.droidworksstudio.launcher.utils.Constants
|
||||
import com.github.droidworksstudio.launcher.R
|
||||
import com.github.droidworksstudio.launcher.accessibility.ActionService
|
||||
import com.github.droidworksstudio.launcher.helper.weather.WeatherResponse
|
||||
import com.github.droidworksstudio.launcher.utils.WeatherApiService
|
||||
import com.google.gson.Gson
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import java.util.Calendar
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
class AppHelper @Inject constructor() {
|
||||
@@ -64,7 +71,7 @@ class AppHelper @Inject constructor() {
|
||||
Configuration.UI_MODE_NIGHT_YES -> {
|
||||
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)
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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<Weather>
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
@@ -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<out String>,
|
||||
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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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<WidgetAdapter.WidgetViewHolder>() {
|
||||
|
||||
private val widgetItems = mutableListOf<WidgetItem>()
|
||||
|
||||
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<Int> = 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<FrameLayout>(R.id.widget_frame).addView(hostView)
|
||||
|
||||
itemView.setOnClickListener {
|
||||
onWidgetClick(widgetItem.appWidgetId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.github.droidworksstudio.launcher.ui.widgetmanager
|
||||
|
||||
import android.appwidget.AppWidgetProviderInfo
|
||||
|
||||
data class WidgetItem(val appWidgetId: Int, val appWidgetInfo: AppWidgetProviderInfo?)
|
||||
@@ -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<Intent>
|
||||
private lateinit var createWidgetLauncher: ActivityResultLauncher<Intent>
|
||||
|
||||
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<Int>) {
|
||||
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<Int> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<WeatherResponse>
|
||||
}
|
||||
@@ -27,6 +27,8 @@ class PreferenceViewModel @Inject constructor(
|
||||
private val timeColorLiveData: MutableLiveData<Int> = MutableLiveData()
|
||||
private val batteryColorLiveData: MutableLiveData<Int> = MutableLiveData()
|
||||
private val dailyWordColorLiveData: MutableLiveData<Int> = MutableLiveData()
|
||||
private val widgetBackgroundColorLiveData: MutableLiveData<Int> = MutableLiveData()
|
||||
private val widgetTextColorLiveData: MutableLiveData<Int> = MutableLiveData()
|
||||
private val appColorLiveData: MutableLiveData<Int> = MutableLiveData()
|
||||
private val dateTextSizeLiveData: MutableLiveData<Float> = MutableLiveData()
|
||||
private val timeTextSizeLiveData: MutableLiveData<Float> = 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)
|
||||
|
||||
10
app/src/main/res/drawable/icon_refresh.xml
Normal file
10
app/src/main/res/drawable/icon_refresh.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24.0dp"
|
||||
android:height="24.0dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M17.65,6.35c-1.63,-1.63 -3.94,-2.57 -6.48,-2.31c-3.67,0.37 -6.69,3.35 -7.1,7.02C3.52,15.91 7.27,20 12,20c3.19,0 5.93,-1.87 7.21,-4.57c0.31,-0.66 -0.16,-1.43 -0.89,-1.43h-0.01c-0.37,0 -0.72,0.2 -0.88,0.53c-1.13,2.43 -3.84,3.97 -6.81,3.32c-2.22,-0.49 -4.01,-2.3 -4.49,-4.52C5.31,9.44 8.26,6 12,6c1.66,0 3.14,0.69 4.22,1.78l-2.37,2.37C13.54,10.46 13.76,11 14.21,11H19c0.55,0 1,-0.45 1,-1V5.21c0,-0.45 -0.54,-0.67 -0.85,-0.35L17.65,6.35z" />
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/widget_background.xml
Normal file
10
app/src/main/res/drawable/widget_background.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#ffffff" />
|
||||
<corners android:radius="8dp" />
|
||||
<padding
|
||||
android:left="16dp"
|
||||
android:top="16dp"
|
||||
android:right="16dp"
|
||||
android:bottom="16dp" />
|
||||
</shape>
|
||||
@@ -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" />
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/bottom_color_date_view"
|
||||
@@ -56,12 +56,11 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="RtlHardcoded"
|
||||
style="@style/TextDefaultStyle"/>
|
||||
style="@style/TextDefaultStyle" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/bottom_color_time_view"
|
||||
android:layout_width="match_parent"
|
||||
@@ -94,7 +93,7 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="RtlHardcoded"
|
||||
style="@style/TextDefaultStyle"/>
|
||||
style="@style/TextDefaultStyle" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
@@ -130,7 +129,7 @@
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:ignore="RtlHardcoded"
|
||||
style="@style/TextDefaultStyle"/>
|
||||
style="@style/TextDefaultStyle" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
@@ -166,7 +165,7 @@
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:ignore="RtlHardcoded"
|
||||
style="@style/TextDefaultStyle"/>
|
||||
style="@style/TextDefaultStyle" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
@@ -202,7 +201,79 @@
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:ignore="RtlHardcoded"
|
||||
style="@style/TextDefaultStyle"/>
|
||||
style="@style/TextDefaultStyle" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/bottom_color_widget_background_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/select_widget_background_title"
|
||||
style="@style/TextDefaultStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="20dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:gravity="left|center"
|
||||
android:inputType="number"
|
||||
android:text="@string/appearance_bottom_dialog_desc_widget_background"
|
||||
android:textSize="@dimen/text_large"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="RtlHardcoded,TouchTargetSizeCheck" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/select_widget_background_color"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="20dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:textSize="@dimen/text_large"
|
||||
android:gravity="left|center"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:ignore="RtlHardcoded"
|
||||
style="@style/TextDefaultStyle" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/bottom_color_widget_text_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/select_widget_text_title"
|
||||
style="@style/TextDefaultStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="20dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:gravity="left|center"
|
||||
android:inputType="number"
|
||||
android:text="@string/appearance_bottom_dialog_desc_widget_text"
|
||||
android:textSize="@dimen/text_large"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="RtlHardcoded,TouchTargetSizeCheck" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/select_widget_text_color"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="20dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:textSize="@dimen/text_large"
|
||||
android:gravity="left|center"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:ignore="RtlHardcoded"
|
||||
style="@style/TextDefaultStyle" />
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/touchArea"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_marginVertical="20dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/touchArea"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_marginVertical="20dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/touchArea"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_marginVertical="20dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/touchArea"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_marginVertical="20dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/draw_background"
|
||||
tools:context=".ui.widgetmanager.WidgetManagerFragment">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/widget_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:longClickable="true"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/widget_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp" />
|
||||
</FrameLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
150
app/src/main/res/layout/fragment_widgets.xml
Normal file
150
app/src/main/res/layout/fragment_widgets.xml
Normal file
@@ -0,0 +1,150 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/mainLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.widgets.WidgetFragment">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/touchArea"
|
||||
android:layout_marginVertical="20dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<com.github.droidworksstudio.launcher.view.GestureNestedScrollView
|
||||
android:id="@+id/nestScrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fillViewport="true"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:fadingEdgeLength="48dp"
|
||||
android:overScrollMode="never"
|
||||
android:requiresFadingEdge="vertical"
|
||||
android:scrollbars="none">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/weather_root"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:layout_marginVertical="50dp"
|
||||
android:background="@drawable/widget_background">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:id="@+id/topHeader"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/weather_city"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="2"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:lines="1"
|
||||
android:textSize="18sp"
|
||||
tools:text="Kayamkulam, IN" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/weather_button_refresh"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="@android:color/transparent"
|
||||
android:contentDescription="@string/widget_weather_refresh"
|
||||
android:src="@drawable/icon_refresh" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:layout_below="@id/topHeader"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:paddingStart="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/weather_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_vertical|center_horizontal"
|
||||
android:layout_weight="0.3"
|
||||
android:contentDescription="@string/widget_weather_description" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.7"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/weather_temperature"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:textAlignment="viewStart"
|
||||
android:lines="1"
|
||||
android:text="@string/widget_weather_temp"
|
||||
android:textSize="30sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/weather_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:paddingTop="2dp"
|
||||
android:text="@string/widget_weather_description"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/weather_wind"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:paddingTop="2dp"
|
||||
android:text="@string/widget_weather_wind"
|
||||
android:lines="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/weather_humidity"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:paddingTop="2dp"
|
||||
android:text="@string/widget_weather_humidity"
|
||||
android:lines="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/weather_pressure"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="2dp"
|
||||
android:ellipsize="end"
|
||||
android:text="@string/widget_weather_pressure"
|
||||
android:lines="1" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</com.github.droidworksstudio.launcher.view.GestureNestedScrollView>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -1,29 +0,0 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_open_app"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Open App"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:backgroundTint="?attr/colorAccent"
|
||||
app:backgroundTint="?attr/colorAccent"
|
||||
app:rippleColor="?attr/colorAccent" />
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_remove_widget"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Remove Widget"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:backgroundTint="?attr/colorAccent"
|
||||
app:backgroundTint="?attr/colorAccent"
|
||||
app:rippleColor="?attr/colorAccent" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -39,9 +39,9 @@
|
||||
|
||||
<fragment
|
||||
android:id="@+id/WidgetsFragment"
|
||||
android:name="com.github.droidworksstudio.launcher.ui.widgetmanager.WidgetManagerFragment"
|
||||
android:name="com.github.droidworksstudio.launcher.ui.widgets.WidgetFragment"
|
||||
android:label="@string/widgets_fragment_label"
|
||||
tools:layout="@layout/fragment_widget_manager" />
|
||||
tools:layout="@layout/fragment_widgets" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/DrawFragment"
|
||||
|
||||
30
app/src/main/res/values-night/colors.xml
Normal file
30
app/src/main/res/values-night/colors.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
|
||||
<color name="search_view_hint">#FFFFFFFF</color>
|
||||
<color name="search_button_background">#56A1E6</color>
|
||||
|
||||
<color name="dialog_background">#333333</color>
|
||||
|
||||
<!-- Black -->
|
||||
<color name="black">#000000</color>
|
||||
<color name="blackTrans10">#1A000000</color>
|
||||
<color name="blackTrans25">#30000000</color>
|
||||
<color name="blackTrans50">#80000000</color>
|
||||
<color name="blackTrans80">#CC000000</color>
|
||||
<color name="blackTrans90">#E6000000</color>
|
||||
|
||||
<!-- White -->
|
||||
<color name="white">#FFFFFF</color>
|
||||
<color name="whiteTrans10">#1AFFFFFF</color>
|
||||
<color name="whiteTrans25">#30FFFFFF</color>
|
||||
<color name="whiteTrans50">#65FFFFFF</color>
|
||||
<color name="whiteTrans80">#CCFFFFFF</color>
|
||||
<color name="whiteTrans90">#E6FFFFFF</color>
|
||||
|
||||
<color name="green">#4CAF50</color>
|
||||
<color name="orange">#AF684C</color>
|
||||
<color name="red">#AF4C4C</color>
|
||||
</resources>
|
||||
20
app/src/main/res/values/nontranslatable.xml
Normal file
20
app/src/main/res/values/nontranslatable.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<resources>
|
||||
<string name="battery_level" translatable="false">%s%%</string>
|
||||
|
||||
<string name="widget_weather_refresh" translatable="false">%1$s</string>
|
||||
<string name="widget_weather_location" translatable="false">%1$s</string>
|
||||
<string name="widget_weather_temp" translatable="false">%1$.2f %2$s</string>
|
||||
<string name="widget_weather_description" translatable="false">%1$s</string>
|
||||
|
||||
<string name="weather_mps" translatable="false">m/s</string>
|
||||
<string name="weather_mph" translatable="false">mil/h</string>
|
||||
<string name="weather_mm" translatable="false">mm</string>
|
||||
|
||||
<string name="weather_c" translatable="false">°C</string>
|
||||
<string name="weather_f" translatable="false">°F</string>
|
||||
|
||||
<string name="widget_weather_feels_like" translatable="false">Feels Like : %s%</string>
|
||||
<string name="widget_weather_wind" translatable="false">Wind : %1$.2f %2$s</string>
|
||||
<string name="widget_weather_humidity" translatable="false">Humidity : %d %%</string>
|
||||
<string name="widget_weather_pressure" translatable="false">Pressure : %d hPa</string>
|
||||
</resources>
|
||||
@@ -13,8 +13,7 @@
|
||||
<!-- Strings used for fragments header labels -->
|
||||
<string name="favorite_fragment_name">Favorite Apps</string>
|
||||
<string name="hidden_fragment_name">Hidden Apps</string>
|
||||
|
||||
<string name="battery_level">%s%%</string>
|
||||
|
||||
<string name="search">Search</string>
|
||||
|
||||
<string name="bottom_dialog_app_rename">Rename</string>
|
||||
@@ -72,6 +71,9 @@
|
||||
<string name="appearance_bottom_dialog_desc_word">Word\u0020:\u0020</string>
|
||||
<string name="appearance_bottom_dialog_desc_battery">Battery\u0020:\u0020 </string>
|
||||
|
||||
<string name="appearance_bottom_dialog_desc_widget_background">Widget Background\u0020:\u0020</string>
|
||||
<string name="appearance_bottom_dialog_desc_widget_text">Widget Text\u0020:\u0020</string>
|
||||
|
||||
<string name="settings_manager_favorite">Favorite Apps</string>
|
||||
<string name="settings_manager_hidden">Hidden Apps</string>
|
||||
|
||||
@@ -107,6 +109,9 @@
|
||||
<string name="settings_actions_take_a_screenshot">Take a ScreenShot</string>
|
||||
<string name="settings_actions_disabled">Disabled</string>
|
||||
|
||||
<string name="settings_units_metric">Metric</string>
|
||||
<string name="settings_units_imperial">Imperial</string>
|
||||
|
||||
<string name="authentication_title">Authentication</string>
|
||||
<string name="authentication_subtitle">Log in using your biometric credentials.</string>
|
||||
<string name="authentication_cancel">Authentication Cancel</string>
|
||||
|
||||
@@ -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" }
|
||||
|
||||
1
weather.properties
Normal file
1
weather.properties
Normal file
@@ -0,0 +1 @@
|
||||
WEATHER_API_KEY=ba0442331f7232028a3efc3f90c1fa84
|
||||
Reference in New Issue
Block a user