Compare commits
20 Commits
v2.3.3-pat
...
v2.3.3-pat
Author | SHA1 | Date | |
---|---|---|---|
64ff404eac | |||
b95a9e8e7f | |||
1667d9c22c | |||
fb3f28d035 | |||
d8e204c5d9 | |||
388653f62b | |||
5763a18421 | |||
5dcf0afe02 | |||
94b1eec757 | |||
c5b41d0886 | |||
4e5bf62e23 | |||
77864cbef4 | |||
32c580bac7 | |||
85fa0cae11 | |||
05f2a745c2 | |||
ef2e89b6ff | |||
a306d92282 | |||
da9fc362af | |||
ff83cfd953 | |||
5d9dcd9701 |
3
.idea/gradle.xml
generated
@ -4,7 +4,7 @@
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="PLATFORM" />
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
@ -14,7 +14,6 @@
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveModulePerSourceSet" value="false" />
|
||||
<option name="useQualifiedModuleNames" value="true" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
|
2
.idea/misc.xml
generated
@ -61,7 +61,7 @@
|
||||
</profile-state>
|
||||
</entry>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
13
.idea/runConfigurations.xml
generated
@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
@ -7,8 +7,6 @@ apply plugin: 'com.google.firebase.crashlytics'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
apply plugin: 'realm-android'
|
||||
|
||||
def apikeyPropertiesFile = rootProject.file("apikey.properties")
|
||||
def apikeyProperties = new Properties()
|
||||
apikeyProperties.load(new FileInputStream(apikeyPropertiesFile))
|
||||
@ -16,7 +14,6 @@ apikeyProperties.load(new FileInputStream(apikeyPropertiesFile))
|
||||
android {
|
||||
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion "29.0.3"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.tommasoberlose.anotherwidget"
|
||||
@ -70,7 +67,7 @@ dependencies {
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||
|
||||
// UI
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
implementation 'com.google.android.material:material:1.3.0'
|
||||
implementation 'androidx.browser:browser:1.3.0'
|
||||
@ -88,6 +85,10 @@ dependencies {
|
||||
// EventBus
|
||||
implementation 'org.greenrobot:eventbus:3.2.0'
|
||||
|
||||
// Room
|
||||
implementation "androidx.room:room-runtime:2.3.0"
|
||||
kapt "androidx.room:room-compiler:2.3.0"
|
||||
|
||||
// Navigation
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
|
||||
@ -115,10 +116,10 @@ dependencies {
|
||||
implementation 'com.android.billingclient:billing-ktx:3.0.3'
|
||||
|
||||
// KTX
|
||||
implementation "androidx.core:core-ktx:1.3.2"
|
||||
implementation "androidx.core:core-ktx:1.5.0"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
|
||||
implementation "androidx.palette:palette-ktx:1.0.0"
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
implementation 'androidx.core:core-ktx:1.5.0'
|
||||
|
||||
//Retrofit
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||
@ -131,7 +132,7 @@ dependencies {
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
|
||||
|
||||
// Add the Firebase SDK for Crashlytics.
|
||||
implementation 'com.google.firebase:firebase-crashlytics:17.4.1'
|
||||
implementation 'com.google.firebase:firebase-crashlytics:18.0.0'
|
||||
|
||||
// Preferences
|
||||
implementation 'com.chibatching.kotpref:kotpref:2.13.1'
|
||||
|
@ -6,15 +6,15 @@
|
||||
<uses-permission android:name="android.permission.READ_CALENDAR" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="com.android.vending.BILLING" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
|
||||
<uses-permission android:name="android.gms.permission.ACTIVITY_RECOGNITION"/>
|
||||
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@ -26,13 +26,13 @@
|
||||
android:usesCleartextTraffic="true"
|
||||
android:theme="@style/AppTheme"
|
||||
tools:ignore="LockedOrientationActivity">
|
||||
<activity android:name=".ui.activities.MainActivity" android:theme="@style/AppTheme.Main" android:screenOrientation="portrait">
|
||||
<activity android:name=".ui.activities.SplashActivity" android:exported="true" android:theme="@style/AppTheme.Main" android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".ui.activities.MainActivity" android:theme="@style/AppTheme" android:screenOrientation="portrait" />
|
||||
<activity android:name=".ui.activities.tabs.ChooseApplicationActivity" android:screenOrientation="portrait" />
|
||||
<activity android:name=".ui.activities.tabs.CustomFontActivity" android:screenOrientation="portrait" />
|
||||
<activity android:name=".ui.activities.tabs.CustomLocationActivity" android:screenOrientation="portrait" />
|
||||
@ -45,11 +45,10 @@
|
||||
<activity android:name=".ui.activities.tabs.MediaInfoFormatActivity" android:screenOrientation="portrait" />
|
||||
<activity android:name=".ui.activities.tabs.TimeZoneSelectorActivity" android:screenOrientation="portrait" />
|
||||
|
||||
<receiver android:name=".ui.widgets.MainWidget">
|
||||
<receiver android:name=".ui.widgets.MainWidget" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/the_widget_info" />
|
||||
@ -75,8 +74,8 @@
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="com.tommasoberlose.anotherwidget.action.ACTION_CALENDAR_UPDATE" />
|
||||
<action android:name="com.tommasoberlose.anotherwidget.action.ACTION_TIME_UPDATE" />
|
||||
<action android:name="com.tommasoberlose.anotherwidget.action.CALENDAR_UPDATE" />
|
||||
<action android:name="com.tommasoberlose.anotherwidget.action.TIME_UPDATE" />
|
||||
<action android:name="com.sec.android.widgetapp.APPWIDGET_RESIZE" />
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||
<action android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" />
|
||||
@ -91,8 +90,7 @@
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.tommasoberlose.anotherwidget.action.ACTION_WEATHER_UPDATE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<action android:name="com.tommasoberlose.anotherwidget.action.WEATHER_UPDATE" />
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.TIME_SET" />
|
||||
@ -104,26 +102,13 @@
|
||||
<receiver
|
||||
android:name=".receivers.WidgetClickListenerReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false">
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.tommasoberlose.anotherwidget.action.ACTION_OPEN_WEATHER_INTENT" />
|
||||
<action android:name="com.tommasoberlose.anotherwidget.action.OPEN_WEATHER_INTENT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".receivers.CrashlyticsReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.tommasoberlose.anotherwidget.action.ACTION_REPORT_CRASH" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service android:name=".services.EventListenerJob" android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
|
||||
<service android:name=".services.BatteryListenerJob" android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
|
||||
<service android:name=".receivers.NotificationListener"
|
||||
<service android:name=".receivers.NotificationListener" android:exported="true"
|
||||
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.notification.NotificationListenerService" />
|
||||
@ -143,7 +128,7 @@
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".receivers.ActivityDetectionReceiver"
|
||||
android:exported="false"
|
||||
android:exported="true"
|
||||
android:permission="com.google.android.gms.permission.ACTIVITY_RECOGNITION">
|
||||
<intent-filter>
|
||||
<action android:name="com.mypackage.ACTION_PROCESS_ACTIVITY_TRANSITIONS" />
|
||||
@ -151,17 +136,6 @@
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name=".services.UpdateCalendarService"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="dataSync" />
|
||||
<service
|
||||
android:name=".services.LocationService"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="location" />
|
||||
</application>
|
||||
|
||||
<queries>
|
||||
|
@ -7,10 +7,6 @@ import androidx.appcompat.app.AppCompatDelegate
|
||||
import com.chibatching.kotpref.Kotpref
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import net.danlew.android.joda.JodaTimeAndroid
|
||||
|
||||
class AWApplication : Application() {
|
||||
override fun onCreate() {
|
||||
@ -24,12 +20,5 @@ class AWApplication : Application() {
|
||||
|
||||
// Dark theme
|
||||
AppCompatDelegate.setDefaultNightMode(Preferences.darkThemePreference)
|
||||
|
||||
// Realm
|
||||
Realm.init(this)
|
||||
val config = RealmConfiguration.Builder()
|
||||
.deleteRealmIfMigrationNeeded()
|
||||
.build()
|
||||
Realm.setDefaultConfiguration(config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ class FixedFocusScrollView @JvmOverloads constructor(
|
||||
var isScrollable = true
|
||||
|
||||
override fun scrollTo(x: Int, y: Int) {
|
||||
if (isScrollable) {
|
||||
if (isScrollable || !isLaidOut) {
|
||||
super.scrollTo(x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -177,6 +177,8 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
|
||||
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
|
||||
Preferences.showNextAlarm = isChecked
|
||||
checkNextAlarm()
|
||||
if (!isChecked)
|
||||
AlarmHelper.clearTimeout(context)
|
||||
}
|
||||
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
|
||||
Preferences.showBatteryCharging = isChecked
|
||||
@ -184,6 +186,8 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
|
||||
Constants.GlanceProviderId.NOTIFICATIONS -> {
|
||||
Preferences.showNotifications = isChecked
|
||||
checkLastNotificationsPermission()
|
||||
if (!isChecked)
|
||||
ActiveNotificationsHelper.clearLastNotification(context)
|
||||
}
|
||||
Constants.GlanceProviderId.GREETINGS -> {
|
||||
Preferences.showGreetings = isChecked
|
||||
@ -217,6 +221,12 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
|
||||
}
|
||||
Constants.GlanceProviderId.EVENTS -> {
|
||||
Preferences.showEventsAsGlanceProvider = isChecked
|
||||
if (isChecked) {
|
||||
com.tommasoberlose.anotherwidget.db.EventRepository(context).run {
|
||||
resetNextEventData()
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
Constants.GlanceProviderId.WEATHER -> {
|
||||
Preferences.showWeatherAsGlanceProvider = isChecked
|
||||
@ -238,8 +248,6 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
|
||||
}
|
||||
|
||||
private fun checkNextAlarm() {
|
||||
if (!Preferences.showNextAlarm)
|
||||
AlarmHelper.clearTimeout(context)
|
||||
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||
val alarm = nextAlarmClock
|
||||
if (alarm != null && alarm.showIntent != null) {
|
||||
|
@ -3,52 +3,51 @@ package com.tommasoberlose.anotherwidget.db
|
||||
import android.content.Context
|
||||
import android.provider.CalendarContract
|
||||
import android.util.Log
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Database
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import com.chibatching.kotpref.bulk
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.applyFilters
|
||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.sortEvents
|
||||
import com.tommasoberlose.anotherwidget.models.Event
|
||||
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmResults
|
||||
import java.util.*
|
||||
import kotlin.Comparator
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class EventRepository(val context: Context) {
|
||||
private val realm by lazy { Realm.getDefaultInstance() }
|
||||
private val db by lazy { EventDatabase.getDatabase(context) }
|
||||
|
||||
fun saveEvents(eventList: List<Event>) {
|
||||
realm.executeTransaction { realm ->
|
||||
realm.where(Event::class.java).findAll().deleteAllFromRealm()
|
||||
realm.copyToRealm(eventList)
|
||||
db.runInTransaction{
|
||||
db.dao().run {
|
||||
deleteAll()
|
||||
insert(eventList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clearEvents() {
|
||||
realm.executeTransaction { realm ->
|
||||
realm.where(Event::class.java).findAll().deleteAllFromRealm()
|
||||
}
|
||||
db.dao().deleteAll()
|
||||
}
|
||||
|
||||
fun resetNextEventData() {
|
||||
Preferences.bulk {
|
||||
remove(Preferences::nextEventId)
|
||||
remove(Preferences::nextEventName)
|
||||
remove(Preferences::nextEventStartDate)
|
||||
remove(Preferences::nextEventAllDay)
|
||||
remove(Preferences::nextEventLocation)
|
||||
remove(Preferences::nextEventEndDate)
|
||||
remove(Preferences::nextEventCalendarId)
|
||||
}
|
||||
}
|
||||
|
||||
fun saveNextEventData(event: Event) {
|
||||
Preferences.nextEventId = event.eventID
|
||||
Preferences.nextEventId = event.id
|
||||
}
|
||||
|
||||
fun getNextEvent(): Event? {
|
||||
val nextEvent = getEventByEventId(Preferences.nextEventId)
|
||||
val nextEvent = getEventById(Preferences.nextEventId)
|
||||
val now = Calendar.getInstance().timeInMillis
|
||||
val limit = Calendar.getInstance().apply {
|
||||
timeInMillis = now
|
||||
@ -64,43 +63,33 @@ class EventRepository(val context: Context) {
|
||||
else -> add(Calendar.HOUR, 6)
|
||||
}
|
||||
}
|
||||
val event = if (nextEvent != null && nextEvent.endDate > now && nextEvent.startDate <= limit.timeInMillis) {
|
||||
return if (nextEvent != null && nextEvent.endDate > now && nextEvent.startDate <= limit.timeInMillis) {
|
||||
nextEvent
|
||||
} else {
|
||||
val events = getEvents()
|
||||
if (events.isNotEmpty()) {
|
||||
val newNextEvent = events.first()
|
||||
Preferences.nextEventId = newNextEvent.eventID
|
||||
saveNextEventData(newNextEvent)
|
||||
newNextEvent
|
||||
} else {
|
||||
resetNextEventData()
|
||||
null
|
||||
}
|
||||
}
|
||||
return try {
|
||||
realm.copyFromRealm(event!!)
|
||||
} catch (ex: Exception) {
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
fun getEventByEventId(id: Long): Event? {
|
||||
val event = realm.where(Event::class.java).equalTo("eventID", id).findFirst()
|
||||
return try {
|
||||
realm.copyFromRealm(event!!)
|
||||
} catch (ex: Exception) {
|
||||
event
|
||||
}
|
||||
fun getEventById(id: Long): Event? {
|
||||
return db.dao().findById(id)
|
||||
}
|
||||
|
||||
fun goToNextEvent() {
|
||||
val eventList = getEvents()
|
||||
if (eventList.isNotEmpty()) {
|
||||
val index = eventList.indexOfFirst { it.eventID == Preferences.nextEventId }
|
||||
val index = eventList.indexOfFirst { it.id == Preferences.nextEventId }
|
||||
if (index > -1 && index < eventList.size - 1) {
|
||||
Preferences.nextEventId = eventList[index + 1].eventID
|
||||
saveNextEventData(eventList[index + 1])
|
||||
} else {
|
||||
Preferences.nextEventId = eventList.first().eventID
|
||||
saveNextEventData(eventList.first())
|
||||
}
|
||||
} else {
|
||||
resetNextEventData()
|
||||
@ -114,11 +103,11 @@ class EventRepository(val context: Context) {
|
||||
fun goToPreviousEvent() {
|
||||
val eventList = getEvents()
|
||||
if (eventList.isNotEmpty()) {
|
||||
val index = eventList.indexOfFirst { it.eventID == Preferences.nextEventId }
|
||||
val index = eventList.indexOfFirst { it.id == Preferences.nextEventId }
|
||||
if (index > 0) {
|
||||
Preferences.nextEventId = eventList[index - 1].eventID
|
||||
saveNextEventData(eventList[index - 1])
|
||||
} else {
|
||||
Preferences.nextEventId = eventList.last().eventID
|
||||
saveNextEventData(eventList.last())
|
||||
}
|
||||
} else {
|
||||
resetNextEventData()
|
||||
@ -130,13 +119,7 @@ class EventRepository(val context: Context) {
|
||||
}
|
||||
|
||||
fun getFutureEvents(): List<Event> {
|
||||
val now = Calendar.getInstance().timeInMillis
|
||||
realm.refresh()
|
||||
return realm
|
||||
.where(Event::class.java)
|
||||
.greaterThan("endDate", now)
|
||||
.findAll()
|
||||
.applyFilters()
|
||||
return db.dao().find(Calendar.getInstance().timeInMillis).sortEvents()
|
||||
}
|
||||
|
||||
private fun getEvents(): List<Event> {
|
||||
@ -155,18 +138,54 @@ class EventRepository(val context: Context) {
|
||||
else -> add(Calendar.HOUR, 6)
|
||||
}
|
||||
}
|
||||
realm.refresh()
|
||||
return realm
|
||||
.where(Event::class.java)
|
||||
.greaterThan("endDate", now)
|
||||
.lessThanOrEqualTo("startDate", limit.timeInMillis)
|
||||
.findAll()
|
||||
.applyFilters()
|
||||
return db.dao().find(now, limit.timeInMillis).sortEvents()
|
||||
}
|
||||
|
||||
fun getEventsCount(): Int = getEvents().size
|
||||
|
||||
fun close() {
|
||||
realm.close()
|
||||
// db.close()
|
||||
}
|
||||
}
|
||||
|
||||
@Dao
|
||||
interface EventDao {
|
||||
@Query("SELECT * FROM events WHERE id = :id LIMIT 1")
|
||||
fun findById(id: Long): Event?
|
||||
|
||||
@Query("SELECT * FROM events WHERE end_date > :from")
|
||||
fun find(from: Long): List<Event>
|
||||
|
||||
@Query("SELECT * FROM events WHERE end_date > :from AND start_date <= :to")
|
||||
fun find(from: Long, to: Long): List<Event>
|
||||
|
||||
@Insert
|
||||
fun insert(events: List<Event>)
|
||||
|
||||
@Query("DELETE FROM events")
|
||||
fun deleteAll()
|
||||
}
|
||||
|
||||
@Database(entities = arrayOf(Event::class), version = 1, exportSchema = false)
|
||||
abstract class EventDatabase : RoomDatabase() {
|
||||
abstract fun dao(): EventDao
|
||||
|
||||
companion object {
|
||||
private var INSTANCE: EventDatabase? = null
|
||||
|
||||
fun getDatabase(context: Context): EventDatabase {
|
||||
// if the INSTANCE is not null, then return it,
|
||||
// if it is, then create the database
|
||||
return INSTANCE ?: synchronized(this) {
|
||||
val instance = Room.databaseBuilder(
|
||||
context.applicationContext,
|
||||
EventDatabase::class.java,
|
||||
"events"
|
||||
).allowMainThreadQueries().build()
|
||||
INSTANCE = instance
|
||||
// return instance
|
||||
instance
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,12 +23,6 @@ object Preferences : KotprefModel() {
|
||||
var calendarFilter by stringPref(key = "PREF_CALENDAR_FILTER", default = "")
|
||||
|
||||
var nextEventId by longPref(key = "PREF_NEXT_EVENT_ID", default = -1)
|
||||
var nextEventName by stringPref(key = "PREF_NEXT_EVENT_NAME")
|
||||
var nextEventStartDate by longPref(key = "PREF_NEXT_EVENT_START_DATE")
|
||||
var nextEventAllDay by booleanPref(key = "PREF_NEXT_EVENT_ALL_DAY")
|
||||
var nextEventLocation by stringPref(key = "PREF_NEXT_EVENT_LOCATION")
|
||||
var nextEventEndDate by longPref(key = "PREF_NEXT_EVENT_END_DATE")
|
||||
var nextEventCalendarId by intPref(key = "PREF_NEXT_EVENT_CALENDAR_ID")
|
||||
var customLocationLat by stringPref(key = "PREF_CUSTOM_LOCATION_LAT", default = "")
|
||||
var customLocationLon by stringPref(key = "PREF_CUSTOM_LOCATION_LON", default = "")
|
||||
var customLocationAdd by stringPref(key = "PREF_CUSTOM_LOCATION_ADD", default = "")
|
||||
@ -85,7 +79,7 @@ object Preferences : KotprefModel() {
|
||||
var weatherIconPack by intPref(default = Constants.WeatherIconPack.DEFAULT.rawValue)
|
||||
|
||||
// UI
|
||||
var widgetMargin by floatPref(default = Constants.Dimension.SMALL.rawValue)
|
||||
var widgetMargin by floatPref(default = Constants.Dimension.NONE.rawValue)
|
||||
var widgetPadding by floatPref(default = Constants.Dimension.SMALL.rawValue)
|
||||
|
||||
// Clock
|
||||
@ -97,7 +91,7 @@ object Preferences : KotprefModel() {
|
||||
var textSecondSize by floatPref(key = "PREF_TEXT_SECOND_SIZE", default = 16f)
|
||||
var clockTextSize by floatPref(key = "PREF_TEXT_CLOCK_SIZE", default = 72f)
|
||||
var clockBottomMargin by intPref(default = Constants.ClockBottomMargin.MEDIUM.rawValue)
|
||||
var secondRowTopMargin by intPref(default = Constants.SecondRowTopMargin.NONE.rawValue)
|
||||
var secondRowTopMargin by intPref(default = Constants.SecondRowTopMargin.SMALL.rawValue)
|
||||
var showClock by booleanPref(key = "PREF_SHOW_CLOCK", default = false)
|
||||
var clockAppName by stringPref(key = "PREF_CLOCK_APP_NAME", default = "")
|
||||
var clockAppPackage by stringPref(key = "PREF_CLOCK_APP_PACKAGE", default = "")
|
||||
|
@ -25,6 +25,7 @@ object ActiveNotificationsHelper {
|
||||
remove(Preferences::lastNotificationIcon)
|
||||
}
|
||||
MainWidget.updateWidget(context)
|
||||
NotificationListener.clearTimeout(context)
|
||||
}
|
||||
|
||||
fun checkNotificationAccess(context: Context): Boolean {
|
||||
|
@ -9,6 +9,7 @@ import android.util.Log
|
||||
import com.tommasoberlose.anotherwidget.global.Actions
|
||||
import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver
|
||||
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
|
||||
import com.tommasoberlose.anotherwidget.utils.setExactIfCanSchedule
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@ -44,14 +45,14 @@ object AlarmHelper {
|
||||
val intent = Intent(context, UpdatesReceiver::class.java).apply {
|
||||
action = Actions.ACTION_ALARM_UPDATE
|
||||
}
|
||||
setExact(
|
||||
setExactIfCanSchedule(
|
||||
AlarmManager.RTC,
|
||||
trigger,
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
ALARM_UPDATE_ID,
|
||||
intent,
|
||||
0
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -62,7 +63,7 @@ object AlarmHelper {
|
||||
val intent = Intent(context, UpdatesReceiver::class.java).apply {
|
||||
action = Actions.ACTION_ALARM_UPDATE
|
||||
}
|
||||
cancel(PendingIntent.getBroadcast(context, ALARM_UPDATE_ID, intent, 0))
|
||||
cancel(PendingIntent.getBroadcast(context, ALARM_UPDATE_ID, intent, PendingIntent.FLAG_IMMUTABLE))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,10 +4,9 @@ import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.provider.CalendarContract
|
||||
import com.tommasoberlose.anotherwidget.services.EventListenerJob
|
||||
import com.tommasoberlose.anotherwidget.models.Event
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.services.UpdateCalendarService
|
||||
import com.tommasoberlose.anotherwidget.services.UpdateCalendarWorker
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import me.everything.providers.android.calendar.CalendarProvider
|
||||
import java.util.*
|
||||
@ -19,7 +18,7 @@ import kotlin.collections.ArrayList
|
||||
|
||||
object CalendarHelper {
|
||||
fun updateEventList(context: Context) {
|
||||
UpdateCalendarService.enqueueWork(context)
|
||||
UpdateCalendarWorker.enqueue(context)
|
||||
}
|
||||
|
||||
fun getCalendarList(context: Context): List<me.everything.providers.android.calendar.Calendar> {
|
||||
@ -50,11 +49,11 @@ object CalendarHelper {
|
||||
}
|
||||
|
||||
fun setEventUpdatesAndroidN(context: Context) {
|
||||
EventListenerJob.schedule(context)
|
||||
UpdateCalendarWorker.enqueueTrigger(context)
|
||||
}
|
||||
|
||||
fun removeEventUpdatesAndroidN(context: Context) {
|
||||
EventListenerJob.remove(context)
|
||||
UpdateCalendarWorker.cancelTrigger(context)
|
||||
}
|
||||
|
||||
fun List<Event>.applyFilters() : List<Event> {
|
||||
|
@ -37,7 +37,7 @@ object GreetingsHelper {
|
||||
Intent(context, UpdatesReceiver::class.java).apply {
|
||||
action = Actions.ACTION_UPDATE_GREETINGS
|
||||
},
|
||||
0)
|
||||
PendingIntent.FLAG_IMMUTABLE)
|
||||
)
|
||||
|
||||
setRepeating(
|
||||
@ -51,7 +51,7 @@ object GreetingsHelper {
|
||||
Intent(context, UpdatesReceiver::class.java).apply {
|
||||
action = Actions.ACTION_UPDATE_GREETINGS
|
||||
},
|
||||
0)
|
||||
PendingIntent.FLAG_IMMUTABLE)
|
||||
)
|
||||
|
||||
setRepeating(
|
||||
@ -65,7 +65,7 @@ object GreetingsHelper {
|
||||
Intent(context, UpdatesReceiver::class.java).apply {
|
||||
action = Actions.ACTION_UPDATE_GREETINGS
|
||||
},
|
||||
0)
|
||||
PendingIntent.FLAG_IMMUTABLE)
|
||||
)
|
||||
|
||||
setRepeating(
|
||||
@ -79,14 +79,14 @@ object GreetingsHelper {
|
||||
Intent(context, UpdatesReceiver::class.java).apply {
|
||||
action = Actions.ACTION_UPDATE_GREETINGS
|
||||
},
|
||||
0)
|
||||
PendingIntent.FLAG_IMMUTABLE)
|
||||
)
|
||||
} else {
|
||||
listOf(MORNING_TIME, MORNING_TIME_END, EVENING_TIME, NIGHT_TIME).forEach {
|
||||
cancel(PendingIntent.getBroadcast(context, it, Intent(context,
|
||||
UpdatesReceiver::class.java).apply {
|
||||
action = Actions.ACTION_UPDATE_GREETINGS
|
||||
}, 0))
|
||||
}, PendingIntent.FLAG_IMMUTABLE))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -102,9 +102,9 @@ object GreetingsHelper {
|
||||
val array = when {
|
||||
hour in 5..8 -> context.resources.getStringArray(R.array.morning_greetings)
|
||||
hour in 19..21 -> context.resources.getStringArray(R.array.evening_greetings)
|
||||
hour >= 22 && hour < 5 -> context.resources.getStringArray(R.array.night_greetings)
|
||||
hour >= 22 || hour < 5 -> context.resources.getStringArray(R.array.night_greetings)
|
||||
else -> emptyArray()
|
||||
}
|
||||
return if (array.isNotEmpty()) array[Random().nextInt(array.size)] else ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ object ImageHelper {
|
||||
0 -> 0f * factor
|
||||
1 -> 8f * factor
|
||||
2 -> 16f * factor
|
||||
else -> 0f * factor
|
||||
else -> 8f * factor
|
||||
}, resources.displayMetrics)
|
||||
|
||||
if (originalView.drawable != null && originalView.drawable.intrinsicWidth > 0 && originalView.drawable.intrinsicHeight > 0) {
|
||||
@ -58,7 +58,7 @@ object ImageHelper {
|
||||
0 -> 0f * factor
|
||||
1 -> 0.8f * factor
|
||||
2 -> 1f * factor
|
||||
else -> 0f
|
||||
else -> 0.8f * factor
|
||||
}))
|
||||
|
||||
colorMatrixScript.setColorMatrix(matrix)
|
||||
|
@ -32,7 +32,7 @@ object IntentHelper {
|
||||
return if (intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK == Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
PendingIntent.getActivity(context, requestCode, intent, flags)
|
||||
else
|
||||
PendingIntent.getBroadcast(context, requestCode, intent, 0)
|
||||
PendingIntent.getBroadcast(context, requestCode, intent, flags)
|
||||
}
|
||||
|
||||
fun getWidgetUpdateIntent(context: Context): Intent {
|
||||
|
@ -7,7 +7,7 @@ import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.global.Constants
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
|
||||
import com.tommasoberlose.anotherwidget.services.LocationService
|
||||
import com.tommasoberlose.anotherwidget.services.WeatherWorker
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
|
||||
@ -19,18 +19,10 @@ import com.tommasoberlose.anotherwidget.utils.isDarkTheme
|
||||
|
||||
object WeatherHelper {
|
||||
|
||||
suspend fun updateWeather(context: Context) {
|
||||
Kotpref.init(context)
|
||||
if (Preferences.customLocationAdd != "") {
|
||||
WeatherNetworkApi(context).updateWeather()
|
||||
} else if (context.checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION) &&
|
||||
(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R ||
|
||||
context.checkGrantedPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION))
|
||||
) {
|
||||
LocationService.requestNewLocation(context)
|
||||
} else {
|
||||
Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_missing_location)
|
||||
Preferences.weatherProviderError = ""
|
||||
fun updateWeather(context: Context, force: Boolean = false) {
|
||||
if (Preferences.showWeather || force)
|
||||
WeatherWorker.enqueue(context, replace = force)
|
||||
else {
|
||||
removeWeather(context)
|
||||
org.greenrobot.eventbus.EventBus.getDefault().post(
|
||||
com.tommasoberlose.anotherwidget.ui.fragments.MainFragment.UpdateUiMessageEvent()
|
||||
@ -339,92 +331,94 @@ object WeatherHelper {
|
||||
}
|
||||
}
|
||||
|
||||
fun getWeatherGovIcon(iconString: String, isDaytime: Boolean): String = when {
|
||||
iconString.contains("skc") -> "01"
|
||||
iconString.contains("few") -> "02"
|
||||
iconString.contains("sct") -> "03"
|
||||
iconString.contains("bkn") -> "04"
|
||||
iconString.contains("ovc") -> "04"
|
||||
iconString.contains("wind_skc") -> "01"
|
||||
iconString.contains("wind_few") -> "02"
|
||||
iconString.contains("wind_sct") -> "03"
|
||||
iconString.contains("wind_bkn") -> "04"
|
||||
iconString.contains("wind_ovc") -> "04"
|
||||
iconString.contains("snow") -> "13"
|
||||
iconString.contains("rain_snow") -> "81"
|
||||
iconString.contains("rain_sleet") -> "81"
|
||||
iconString.contains("snow_sleet") -> "81"
|
||||
iconString.contains("fzra") -> "81"
|
||||
iconString.contains("rain_fzra") -> "81"
|
||||
iconString.contains("snow_fzra") -> "81"
|
||||
iconString.contains("sleet") -> "81"
|
||||
iconString.contains("rain") -> "10"
|
||||
iconString.contains("rain_showers") -> "10"
|
||||
iconString.contains("rain_showers_hi") -> "10"
|
||||
iconString.contains("tsra") -> "82"
|
||||
iconString.contains("tsra_sct") -> "82"
|
||||
iconString.contains("tsra_hi") -> "82"
|
||||
iconString.contains("tornado") -> "80"
|
||||
iconString.contains("hurricane") -> "80"
|
||||
iconString.contains("tropical_storm") -> "09"
|
||||
iconString.contains("dust") -> "Dust"
|
||||
iconString.contains("smoke") -> "Smoke"
|
||||
iconString.contains("haze") -> "50"
|
||||
iconString.contains("hot") -> "01"
|
||||
iconString.contains("cold") -> "13"
|
||||
iconString.contains("blizzard") -> "80"
|
||||
iconString.contains("fog") -> "82"
|
||||
fun getWeatherGovIcon(iconString: String, isDaytime: Boolean): String = when (
|
||||
iconString.substringBefore('?').substringAfterLast('/').substringBefore(',')
|
||||
) {
|
||||
"skc" -> "01"
|
||||
"few" -> "02"
|
||||
"sct" -> "02"
|
||||
"bkn" -> "03"
|
||||
"ovc" -> "04"
|
||||
"wind_skc" -> "01"
|
||||
"wind_few" -> "02"
|
||||
"wind_sct" -> "02"
|
||||
"wind_bkn" -> "03"
|
||||
"wind_ovc" -> "04"
|
||||
"snow" -> "13"
|
||||
"rain_snow" -> "81"
|
||||
"rain_sleet" -> "81"
|
||||
"snow_sleet" -> "81"
|
||||
"fzra" -> "81"
|
||||
"rain_fzra" -> "81"
|
||||
"snow_fzra" -> "81"
|
||||
"sleet" -> "81"
|
||||
"rain" -> "10"
|
||||
"rain_showers" -> "10"
|
||||
"rain_showers_hi" -> "10"
|
||||
"tsra" -> "09"
|
||||
"tsra_sct" -> "11"
|
||||
"tsra_hi" -> "11"
|
||||
"tornado" -> "80"
|
||||
"hurricane" -> "80"
|
||||
"tropical_storm" -> "09"
|
||||
"dust" -> "50"
|
||||
"smoke" -> "50"
|
||||
"haze" -> "50"
|
||||
"hot" -> "01"
|
||||
"cold" -> "13"
|
||||
"blizzard" -> "13"
|
||||
"fog" -> "82"
|
||||
else -> ""
|
||||
} + if (isDaytime) "d" else "n"
|
||||
|
||||
fun getWeatherBitIcon(iconString: String): String = when {
|
||||
iconString.contains("t01") -> "11"
|
||||
iconString.contains("t02") -> "09"
|
||||
iconString.contains("t03") -> "09"
|
||||
iconString.contains("t04") -> "09"
|
||||
iconString.contains("t05") -> "09"
|
||||
iconString.contains("d01") -> "10"
|
||||
iconString.contains("d02") -> "10"
|
||||
iconString.contains("d03") -> "10"
|
||||
iconString.contains("r01") -> "10"
|
||||
iconString.contains("r02") -> "10"
|
||||
iconString.contains("r03") -> "10"
|
||||
iconString.contains("f01") -> "10"
|
||||
iconString.contains("r04") -> "10"
|
||||
iconString.contains("r05") -> "10"
|
||||
iconString.contains("r06") -> "10"
|
||||
iconString.contains("s01") -> "13"
|
||||
iconString.contains("s02") -> "13"
|
||||
iconString.contains("s03") -> "13"
|
||||
iconString.contains("s04") -> "81"
|
||||
iconString.contains("s05") -> "90"
|
||||
iconString.contains("s06") -> "13"
|
||||
iconString.contains("a01") -> "82"
|
||||
iconString.contains("a02") -> "82"
|
||||
iconString.contains("a03") -> "82"
|
||||
iconString.contains("a04") -> "82"
|
||||
iconString.contains("a05") -> "82"
|
||||
iconString.contains("a06") -> "82"
|
||||
iconString.contains("c01") -> "01"
|
||||
iconString.contains("c02") -> "02"
|
||||
iconString.contains("c03") -> "04"
|
||||
iconString.contains("c04") -> "04"
|
||||
fun getWeatherBitIcon(iconString: String): String = when (iconString.substring(0, 3)) {
|
||||
"t01" -> "11"
|
||||
"t02" -> "11"
|
||||
"t03" -> "09"
|
||||
"t04" -> "11"
|
||||
"t05" -> "11"
|
||||
"d01" -> "10"
|
||||
"d02" -> "10"
|
||||
"d03" -> "10"
|
||||
"r01" -> "10"
|
||||
"r02" -> "10"
|
||||
"r03" -> "10"
|
||||
"f01" -> "10"
|
||||
"r04" -> "10"
|
||||
"r05" -> "10"
|
||||
"r06" -> "10"
|
||||
"s01" -> "13"
|
||||
"s02" -> "13"
|
||||
"s03" -> "13"
|
||||
"s04" -> "81"
|
||||
"s05" -> "81"
|
||||
"s06" -> "13"
|
||||
"a01" -> "50"
|
||||
"a02" -> "50"
|
||||
"a03" -> "50"
|
||||
"a04" -> "50"
|
||||
"a05" -> "82"
|
||||
"a06" -> "82"
|
||||
"c01" -> "01"
|
||||
"c02" -> "02"
|
||||
"c03" -> "03"
|
||||
"c04" -> "04"
|
||||
else -> ""
|
||||
} + if (iconString.contains("d")) "d" else "n"
|
||||
} + iconString.substring(3)
|
||||
|
||||
fun getWeatherApiIcon(icon: Int, isDaytime: Boolean): String = when(icon) {
|
||||
1000 -> "01"
|
||||
1003 -> "02"
|
||||
1006 -> "03"
|
||||
1009 -> "04"
|
||||
1030 -> "82"
|
||||
1030 -> "50"
|
||||
1063 -> "10"
|
||||
1066 -> "10"
|
||||
1069 -> "10"
|
||||
1066 -> "13"
|
||||
1069 -> "81"
|
||||
1072 -> "81"
|
||||
1087 -> "11"
|
||||
1114 -> "13"
|
||||
1117 -> "09"
|
||||
1117 -> "13"
|
||||
1135 -> "82"
|
||||
1147 -> "82"
|
||||
1150 -> "10"
|
||||
@ -439,8 +433,8 @@ object WeatherHelper {
|
||||
1195 -> "10"
|
||||
1198 -> "81"
|
||||
1201 -> "81"
|
||||
1204 -> "13"
|
||||
1207 -> "13"
|
||||
1204 -> "81"
|
||||
1207 -> "81"
|
||||
1210 -> "13"
|
||||
1213 -> "13"
|
||||
1216 -> "13"
|
||||
@ -451,62 +445,62 @@ object WeatherHelper {
|
||||
1240 -> "10"
|
||||
1243 -> "10"
|
||||
1246 -> "10"
|
||||
1249 -> "13"
|
||||
1252 -> "13"
|
||||
1249 -> "81"
|
||||
1252 -> "81"
|
||||
1255 -> "13"
|
||||
1258 -> "13"
|
||||
1261 -> "13"
|
||||
1264 -> "13"
|
||||
1273 -> "09"
|
||||
1273 -> "11"
|
||||
1276 -> "09"
|
||||
1279 -> "13"
|
||||
1282 -> "13"
|
||||
else -> ""
|
||||
} + if (isDaytime) "d" else "n"
|
||||
|
||||
fun getYRIcon(iconCode: String, isDaytime: Boolean): String = when {
|
||||
iconCode.contains("clearsky") -> "01"
|
||||
iconCode.contains("cloudy") -> "04"
|
||||
iconCode.contains("fair") -> "02"
|
||||
iconCode.contains("fog") -> "82"
|
||||
iconCode.contains("heavyrain") -> "10"
|
||||
iconCode.contains("heavyrainandthunder") -> "11"
|
||||
iconCode.contains("heavyrainshowers") -> "10"
|
||||
iconCode.contains("heavyrainshowersandthunder") -> "11"
|
||||
iconCode.contains("heavysleet") -> "10"
|
||||
iconCode.contains("heavysleetandthunder") -> "11"
|
||||
iconCode.contains("heavysleetshowers") -> "10"
|
||||
iconCode.contains("heavysleetshowersandthunder") -> "11"
|
||||
iconCode.contains("heavysnow") -> "13"
|
||||
iconCode.contains("heavysnowandthunder") -> "13"
|
||||
iconCode.contains("heavysnowshowers") -> "13"
|
||||
iconCode.contains("heavysnowshowersandthunder") -> "13"
|
||||
iconCode.contains("lightrain") -> "10"
|
||||
iconCode.contains("lightrainandthunder") -> "11"
|
||||
iconCode.contains("lightrainshowers") -> "10"
|
||||
iconCode.contains("lightrainshowersandthunder") -> "11"
|
||||
iconCode.contains("lightsleet") -> "10"
|
||||
iconCode.contains("lightsleetandthunder") -> "11"
|
||||
iconCode.contains("lightsleetshowers") -> "10"
|
||||
iconCode.contains("lightsnow") -> "13"
|
||||
iconCode.contains("lightsnowandthunder") -> "13"
|
||||
iconCode.contains("lightsnowshowers") -> "13"
|
||||
iconCode.contains("lightssleetshowersandthunder") -> "81"
|
||||
iconCode.contains("lightssnowshowersandthunder") -> "81"
|
||||
iconCode.contains("partlycloudy") -> "03"
|
||||
iconCode.contains("rain") -> "10"
|
||||
iconCode.contains("rainandthunder") -> "11"
|
||||
iconCode.contains("rainshowers") -> "10"
|
||||
iconCode.contains("rainshowersandthunder") -> "11"
|
||||
iconCode.contains("sleet") -> "10"
|
||||
iconCode.contains("sleetandthunder") -> "11"
|
||||
iconCode.contains("sleetshowers") -> "10"
|
||||
iconCode.contains("sleetshowersandthunder") -> "11"
|
||||
iconCode.contains("snow") -> "13"
|
||||
iconCode.contains("snowandthunder") -> "13"
|
||||
iconCode.contains("snowshowers") -> "13"
|
||||
iconCode.contains("snowshowersandthunder") -> "13"
|
||||
fun getYRIcon(iconCode: String): String = when (iconCode.substringBefore('_')) {
|
||||
"clearsky" -> "01"
|
||||
"cloudy" -> "04"
|
||||
"fair" -> "02"
|
||||
"fog" -> "82"
|
||||
"heavyrain" -> "10"
|
||||
"heavyrainandthunder" -> "09"
|
||||
"heavyrainshowers" -> "10"
|
||||
"heavyrainshowersandthunder" -> "09"
|
||||
"heavysleet" -> "81"
|
||||
"heavysleetandthunder" -> "81"
|
||||
"heavysleetshowers" -> "81"
|
||||
"heavysleetshowersandthunder" -> "81"
|
||||
"heavysnow" -> "13"
|
||||
"heavysnowandthunder" -> "13"
|
||||
"heavysnowshowers" -> "13"
|
||||
"heavysnowshowersandthunder" -> "13"
|
||||
"lightrain" -> "10"
|
||||
"lightrainandthunder" -> "11"
|
||||
"lightrainshowers" -> "10"
|
||||
"lightrainshowersandthunder" -> "11"
|
||||
"lightsleet" -> "81"
|
||||
"lightsleetandthunder" -> "81"
|
||||
"lightsleetshowers" -> "81"
|
||||
"lightsnow" -> "13"
|
||||
"lightsnowandthunder" -> "13"
|
||||
"lightsnowshowers" -> "13"
|
||||
"lightssleetshowersandthunder" -> "81"
|
||||
"lightssnowshowersandthunder" -> "81"
|
||||
"partlycloudy" -> "03"
|
||||
"rain" -> "10"
|
||||
"rainandthunder" -> "11"
|
||||
"rainshowers" -> "10"
|
||||
"rainshowersandthunder" -> "11"
|
||||
"sleet" -> "81"
|
||||
"sleetandthunder" -> "81"
|
||||
"sleetshowers" -> "81"
|
||||
"sleetshowersandthunder" -> "81"
|
||||
"snow" -> "13"
|
||||
"snowandthunder" -> "13"
|
||||
"snowshowers" -> "13"
|
||||
"snowshowersandthunder" -> "13"
|
||||
else -> ""
|
||||
} + if (isDaytime) "d" else "n"
|
||||
} + if (iconCode.substringAfter('_', "day") == "day") "d" else "n"
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,35 @@
|
||||
package com.tommasoberlose.anotherwidget.models
|
||||
|
||||
import android.provider.CalendarContract
|
||||
import io.realm.RealmObject
|
||||
import java.util.Date
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
/**
|
||||
* Created by tommaso on 05/10/17.
|
||||
*/
|
||||
|
||||
open class Event(
|
||||
var id: Long = 0,
|
||||
var eventID: Long = 0,
|
||||
var title: String = "",
|
||||
var startDate: Long = 0,
|
||||
var endDate: Long = 0,
|
||||
var calendarID: Int = 0,
|
||||
var allDay: Boolean = false,
|
||||
var address: String = "",
|
||||
var selfAttendeeStatus: Int = CalendarContract.Attendees.ATTENDEE_STATUS_NONE,
|
||||
var availability: Int = CalendarContract.EventsEntity.AVAILABILITY_BUSY
|
||||
) : RealmObject() {
|
||||
@Entity(tableName = "events")
|
||||
data class Event(
|
||||
@PrimaryKey
|
||||
val id: Long = 0,
|
||||
@ColumnInfo(name = "event_id")
|
||||
val eventID: Long = 0,
|
||||
val title: String = "",
|
||||
@ColumnInfo(name = "start_date")
|
||||
val startDate: Long = 0,
|
||||
@ColumnInfo(name = "end_date")
|
||||
val endDate: Long = 0,
|
||||
@ColumnInfo(name = "calendar_id")
|
||||
val calendarID: Long = 0,
|
||||
@ColumnInfo(name = "all_day")
|
||||
val allDay: Boolean = false,
|
||||
val address: String = "",
|
||||
@ColumnInfo(name = "self_attendee_status")
|
||||
val selfAttendeeStatus: Int = CalendarContract.Attendees.ATTENDEE_STATUS_NONE,
|
||||
val availability: Int = CalendarContract.EventsEntity.AVAILABILITY_BUSY
|
||||
)/* {
|
||||
override fun toString(): String {
|
||||
return "Event:\nEVENT ID: " + eventID + "\nTITLE: " + title + "\nSTART DATE: " + Date(startDate) + "\nEND DATE: " + Date(endDate) + "\nCAL ID: " + calendarID + "\nADDRESS: " + address
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
@ -26,9 +26,7 @@ class TimeZonesApi(val context: Context) {
|
||||
when (val response = repository.getTimeZone(lat, long)) {
|
||||
is NetworkResponse.Success -> {
|
||||
try {
|
||||
Log.d("ciao", response.body.toString())
|
||||
id = response.body["timezoneId"] as String
|
||||
|
||||
} catch(ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
}
|
||||
@ -37,4 +35,4 @@ class TimeZonesApi(val context: Context) {
|
||||
|
||||
return id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,13 +17,11 @@ import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
||||
import com.tommasoberlose.anotherwidget.network.repository.*
|
||||
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.lang.Exception
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlin.coroutines.resume
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
class WeatherNetworkApi(val context: Context) {
|
||||
suspend fun updateWeather() {
|
||||
@ -31,7 +29,7 @@ class WeatherNetworkApi(val context: Context) {
|
||||
Preferences.weatherProviderError = "-"
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
|
||||
if (Preferences.showWeather && Preferences.customLocationLat != "" && Preferences.customLocationLon != "") {
|
||||
if (Preferences.customLocationLat != "" && Preferences.customLocationLon != "") {
|
||||
when (Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) {
|
||||
Constants.WeatherProvider.OPEN_WEATHER -> useOpenWeatherMap(context)
|
||||
Constants.WeatherProvider.WEATHER_GOV -> useWeatherGov(context)
|
||||
@ -42,46 +40,67 @@ class WeatherNetworkApi(val context: Context) {
|
||||
Constants.WeatherProvider.YR -> useYrProvider(context)
|
||||
}
|
||||
} else {
|
||||
if (!Preferences.showWeather)
|
||||
Preferences.weatherProviderError = context.getString(R.string.show_weather_not_visible)
|
||||
else {
|
||||
Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_missing_location)
|
||||
Preferences.weatherProviderError = ""
|
||||
}
|
||||
Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_missing_location)
|
||||
Preferences.weatherProviderError = ""
|
||||
|
||||
WeatherHelper.removeWeather(
|
||||
context
|
||||
)
|
||||
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
|
||||
private fun useOpenWeatherMap(context: Context) {
|
||||
private suspend fun useOpenWeatherMap(context: Context) {
|
||||
if (Preferences.weatherProviderApiOpen != "") {
|
||||
val helper = OpenWeatherMapHelper(Preferences.weatherProviderApiOpen)
|
||||
helper.setUnits(if (Preferences.weatherTempUnit == "F") Units.IMPERIAL else Units.METRIC)
|
||||
helper.getCurrentWeatherByGeoCoordinates(Preferences.customLocationLat.toDouble(), Preferences.customLocationLon.toDouble(), object :
|
||||
CurrentWeatherCallback {
|
||||
override fun onSuccess(currentWeather: CurrentWeather?) {
|
||||
currentWeather?.let {
|
||||
Preferences.weatherTemp = currentWeather.main.temp.toFloat()
|
||||
Preferences.weatherIcon = currentWeather.weather[0].icon
|
||||
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||
MainWidget.updateWidget(context)
|
||||
when (val response = suspendCancellableCoroutine<Any?> { continuation ->
|
||||
helper.getCurrentWeatherByGeoCoordinates(Preferences.customLocationLat.toDouble(), Preferences.customLocationLon.toDouble(), object :
|
||||
CurrentWeatherCallback {
|
||||
override fun onSuccess(currentWeather: CurrentWeather?) {
|
||||
continuation.resume(currentWeather)
|
||||
}
|
||||
|
||||
override fun onFailure(throwable: Throwable?) {
|
||||
continuation.resume(throwable)
|
||||
}
|
||||
})
|
||||
}) {
|
||||
is CurrentWeather -> {
|
||||
Preferences.weatherTemp = response.main.temp.toFloat()
|
||||
Preferences.weatherIcon = response.weather[0].icon
|
||||
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||
MainWidget.updateWidget(context)
|
||||
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
|
||||
override fun onFailure(throwable: Throwable?) {
|
||||
is Throwable -> {
|
||||
if (response.javaClass == Throwable::class.java) {
|
||||
// server error, see [OpenWeatherMapHelper.handleCurrentWeatherResponse]
|
||||
if (response.message?.startsWith("UnAuthorized") == true) {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_invalid_key)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
else {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
WeatherHelper.removeWeather(
|
||||
context
|
||||
)
|
||||
}
|
||||
else {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
})
|
||||
}
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
} else {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
@ -121,25 +140,34 @@ class WeatherNetworkApi(val context: Context) {
|
||||
val props =
|
||||
weatherResponse.body["properties"] as LinkedTreeMap<*, *>
|
||||
val periods = props["periods"] as List<*>
|
||||
val now = periods[0] as LinkedTreeMap<*, *>
|
||||
@android.annotation.SuppressLint("SimpleDateFormat")
|
||||
val format = SimpleDateFormat(
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N)
|
||||
"yyyy-MM-dd'T'HH:mm:ssXXX"
|
||||
else
|
||||
"yyyy-MM-dd'T'HH:mm:ssZ"
|
||||
)
|
||||
for (period in periods) {
|
||||
val now = period as LinkedTreeMap<*, *>
|
||||
val endTime = format.parse(now["endTime"] as String)!!
|
||||
if (endTime.time > System.currentTimeMillis()) {
|
||||
val temp = now["temperature"] as Double
|
||||
val fullIcon = now["icon"] as String
|
||||
val isDaytime = now["isDaytime"] as Boolean
|
||||
|
||||
val temp = now["temperature"] as Double
|
||||
val fullIcon = now["icon"] as String
|
||||
val isDaytime = now["isDaytime"] as Boolean
|
||||
Preferences.weatherTemp = temp.toFloat()
|
||||
Preferences.weatherIcon = WeatherHelper.getWeatherGovIcon(fullIcon, isDaytime)
|
||||
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||
MainWidget.updateWidget(context)
|
||||
|
||||
Preferences.weatherTemp = temp.toFloat()
|
||||
Preferences.weatherIcon = WeatherHelper.getWeatherGovIcon(fullIcon, isDaytime)
|
||||
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
|
||||
MainWidget.updateWidget(context)
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
break
|
||||
}
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
} finally {
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
@ -155,14 +183,16 @@ class WeatherNetworkApi(val context: Context) {
|
||||
}
|
||||
}
|
||||
is NetworkResponse.ServerError -> {
|
||||
if (pointsResponse.body?.containsKey("status") == true && (pointsResponse.body?.get("status") as Double).toInt() == 404) {
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_wrong_location)
|
||||
} else {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
when (pointsResponse.code) {
|
||||
404 -> {
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_wrong_location)
|
||||
}
|
||||
else -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
}
|
||||
|
||||
WeatherHelper.removeWeather(
|
||||
context
|
||||
)
|
||||
@ -183,7 +213,18 @@ class WeatherNetworkApi(val context: Context) {
|
||||
when (val response = repository.getWeather()) {
|
||||
is NetworkResponse.Success -> {
|
||||
try {
|
||||
Log.d("ciao - here", response.body.toString())
|
||||
val observations = response.body["observations"] as LinkedTreeMap<*, *>
|
||||
val location = (observations["location"] as List<*>).first() as LinkedTreeMap<*, *>
|
||||
val observation = (location["observation"] as List<*>).first() as LinkedTreeMap<*, *>
|
||||
val iconName = observation["iconName"] as String
|
||||
val daylight = observation["daylight"] as String
|
||||
val temperature = observation["temperature"] as String
|
||||
|
||||
Preferences.weatherTemp = temperature.toFloat()
|
||||
Preferences.weatherIcon = repository.getWeatherIcon(iconName, daylight != "N")
|
||||
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||
MainWidget.updateWidget(context)
|
||||
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
} catch(ex: Exception) {
|
||||
@ -194,8 +235,16 @@ class WeatherNetworkApi(val context: Context) {
|
||||
}
|
||||
}
|
||||
is NetworkResponse.ServerError -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
when (response.code) {
|
||||
401 -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_invalid_key)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
else -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
}
|
||||
WeatherHelper.removeWeather(
|
||||
context
|
||||
)
|
||||
@ -225,10 +274,10 @@ class WeatherNetworkApi(val context: Context) {
|
||||
when (val response = repository.getWeather()) {
|
||||
is NetworkResponse.Success -> {
|
||||
try {
|
||||
val data = response.body["data"] as List<LinkedTreeMap<String, Any>>?
|
||||
data?.first()?.let {
|
||||
val data = response.body["data"] as List<*>?
|
||||
data?.first()?.let { it as LinkedTreeMap<*, *>
|
||||
val temp = it["temp"] as Double
|
||||
val weatherInfo = it["weather"] as LinkedTreeMap<String, Any>
|
||||
val weatherInfo = it["weather"] as LinkedTreeMap<*, *>
|
||||
val iconCode = weatherInfo["icon"] as String
|
||||
|
||||
Preferences.weatherTemp = temp.toFloat()
|
||||
@ -238,8 +287,6 @@ class WeatherNetworkApi(val context: Context) {
|
||||
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
} catch(ex: Exception) {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
@ -288,12 +335,12 @@ class WeatherNetworkApi(val context: Context) {
|
||||
when (val response = repository.getWeather()) {
|
||||
is NetworkResponse.Success -> {
|
||||
try {
|
||||
val current = response.body["current"] as LinkedTreeMap<String, Any>?
|
||||
val current = response.body["current"] as LinkedTreeMap<*, *>?
|
||||
current?.let {
|
||||
val tempC = current["temp_c"] as Double
|
||||
val tempF = current["temp_f"] as Double
|
||||
val isDay = current["is_day"] as Double
|
||||
val condition = current["condition"] as LinkedTreeMap<String, Any>
|
||||
val condition = current["condition"] as LinkedTreeMap<*, *>
|
||||
val iconCode = condition["code"] as Double
|
||||
|
||||
Preferences.weatherTemp = if (Preferences.weatherTempUnit == "F") tempF.toFloat() else tempC.toFloat()
|
||||
@ -303,8 +350,6 @@ class WeatherNetworkApi(val context: Context) {
|
||||
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
} catch(ex: Exception) {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
@ -353,28 +398,74 @@ class WeatherNetworkApi(val context: Context) {
|
||||
|
||||
private suspend fun useAccuweatherProvider(context: Context) {
|
||||
if (Preferences.weatherProviderApiAccuweather != "") {
|
||||
// val repository = AccuweatherRepository()
|
||||
val repository = AccuweatherRepository()
|
||||
|
||||
// when (val response = repository.getWeather()) {
|
||||
// is NetworkResponse.Success -> {
|
||||
// try {
|
||||
// Log.d("ciao", response.body.toString())
|
||||
// } catch(ex: Exception) {
|
||||
//
|
||||
// Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
|
||||
// Preferences.weatherProviderLocationError = ""
|
||||
// }
|
||||
// }
|
||||
// is NetworkResponse.ServerError -> {
|
||||
// WeatherHelper.removeWeather(
|
||||
// context
|
||||
// )
|
||||
// }
|
||||
// Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
|
||||
// Preferences.weatherProviderLocationError = ""
|
||||
// EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
// }
|
||||
when (val locationResponse = repository.getLocation()) {
|
||||
is NetworkResponse.Success -> {
|
||||
try {
|
||||
val key = locationResponse.body["Key"] as String
|
||||
|
||||
when (val weatherResponse = repository.getWeather(key)) {
|
||||
is NetworkResponse.Success -> {
|
||||
try {
|
||||
weatherResponse.body.first().let {
|
||||
val temp = it["Temperature"] as LinkedTreeMap<*, *>
|
||||
val tempC = (temp["Metric"] as LinkedTreeMap<*, *>)["Value"] as Double
|
||||
val tempF = (temp["Imperial"] as LinkedTreeMap<*, *>)["Value"] as Double
|
||||
val isDay = it["IsDayTime"] as Boolean
|
||||
val icon = it["WeatherIcon"] as Double
|
||||
|
||||
Preferences.weatherTemp = if (Preferences.weatherTempUnit == "F") tempF.toFloat() else tempC.toFloat()
|
||||
Preferences.weatherIcon = repository.getWeatherIcon(icon.toInt(), isDay)
|
||||
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||
MainWidget.updateWidget(context)
|
||||
}
|
||||
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
} catch (ex: Exception) {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
}
|
||||
} catch(ex: Exception) {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
} finally {
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
is NetworkResponse.ServerError -> {
|
||||
when (locationResponse.code) {
|
||||
401 -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_invalid_key)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
503 -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_expired_key)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
else -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
}
|
||||
WeatherHelper.removeWeather(
|
||||
context
|
||||
)
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
else -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
@ -393,38 +484,27 @@ class WeatherNetworkApi(val context: Context) {
|
||||
is NetworkResponse.Success -> {
|
||||
try {
|
||||
val pp = response.body["properties"] as LinkedTreeMap<*, *>
|
||||
val data = pp["timeseries"] as List<LinkedTreeMap<String, Any>>?
|
||||
data?.let {
|
||||
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
|
||||
for (item in data) {
|
||||
val time = Calendar.getInstance().apply { time = format.parse(item["time"] as String)!! }
|
||||
val now = Calendar.getInstance()
|
||||
if (time.timeInMillis >= now.timeInMillis) {
|
||||
val dd = item["data"] as LinkedTreeMap<*, *>
|
||||
val instant = dd["instant"] as LinkedTreeMap<*, *>
|
||||
val next = dd["next_1_hours"] as LinkedTreeMap<*, *>
|
||||
val data = pp["timeseries"] as List<*>?
|
||||
data?.first()?.let { it as LinkedTreeMap<*, *>
|
||||
val dd = it["data"] as LinkedTreeMap<*, *>
|
||||
val instant = dd["instant"] as LinkedTreeMap<*, *>
|
||||
val next = dd["next_1_hours"] as LinkedTreeMap<*, *>
|
||||
|
||||
val details = instant["details"] as LinkedTreeMap<*, *>
|
||||
val temp = details["air_temperature"] as Double
|
||||
val details = instant["details"] as LinkedTreeMap<*, *>
|
||||
val temp = details["air_temperature"] as Double
|
||||
|
||||
val summary = next["summary"] as LinkedTreeMap<*, *>
|
||||
val iconCode = summary["symbol_code"] as String
|
||||
val summary = next["summary"] as LinkedTreeMap<*, *>
|
||||
val iconCode = summary["symbol_code"] as String
|
||||
|
||||
Preferences.weatherTemp = temp.toFloat()
|
||||
Preferences.weatherIcon = WeatherHelper.getYRIcon(iconCode, now.get(Calendar.HOUR_OF_DAY) >= 22 || now.get(Calendar.HOUR_OF_DAY) <= 8)
|
||||
Preferences.weatherTempUnit = "C"
|
||||
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||
MainWidget.updateWidget(context)
|
||||
Preferences.weatherTemp = temp.toFloat()
|
||||
Preferences.weatherIcon = WeatherHelper.getYRIcon(iconCode)
|
||||
Preferences.weatherTempUnit = "C"
|
||||
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||
MainWidget.updateWidget(context)
|
||||
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
break
|
||||
}
|
||||
}
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
|
||||
|
||||
|
||||
} catch(ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
@ -448,4 +528,4 @@ class WeatherNetworkApi(val context: Context) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ object ApiServices {
|
||||
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||
|
||||
@Headers("User-Agent: (Another Widget, tommaso.berlose@gmail.com)")
|
||||
@GET("gridpoints/{gridId}/{gridX},{gridY}/forecast")
|
||||
@GET("gridpoints/{gridId}/{gridX},{gridY}/forecast/hourly")
|
||||
suspend fun getWeather(
|
||||
@Path("gridId") gridId: String,
|
||||
@Path("gridX") gridX: Int,
|
||||
@ -54,13 +54,17 @@ object ApiServices {
|
||||
}
|
||||
|
||||
interface AccuweatherService {
|
||||
@GET("")
|
||||
suspend fun getWeather(
|
||||
@Path("gridId") gridId: String,
|
||||
@Path("gridX") gridX: Int,
|
||||
@Path("gridY") gridY: Int,
|
||||
@Query("units") unit: String
|
||||
@GET("locations/v1/cities/geoposition/search")
|
||||
suspend fun getLocation(
|
||||
@Query("apikey") apikey: String,
|
||||
@Query("q") location: String
|
||||
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||
|
||||
@GET("currentconditions/v1/{locationKey}")
|
||||
suspend fun getWeather(
|
||||
@Path("locationKey") locationKey: String,
|
||||
@Query("apikey") apikey: String
|
||||
): NetworkResponse<List<HashMap<String, Any>>, HashMap<String, Any>>
|
||||
}
|
||||
|
||||
interface YrService {
|
||||
@ -80,4 +84,4 @@ object ApiServices {
|
||||
@Query("username") username: String = "tommaso.berlose",
|
||||
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.tommasoberlose.anotherwidget.network.repository
|
||||
|
||||
import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.network.api.ApiServices
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
@ -9,10 +10,11 @@ class AccuweatherRepository {
|
||||
|
||||
/* ACCUWEATHER */
|
||||
private val apiServiceAccu: ApiServices.AccuweatherService = getRetrofit().create(ApiServices.AccuweatherService::class.java)
|
||||
suspend fun getWeather(): Nothing = TODO()
|
||||
suspend fun getLocation() = apiServiceAccu.getLocation(Preferences.weatherProviderApiAccuweather, "${Preferences.customLocationLat},${Preferences.customLocationLon}")
|
||||
suspend fun getWeather(locationKey: String) = apiServiceAccu.getWeather(locationKey, Preferences.weatherProviderApiAccuweather)
|
||||
|
||||
companion object {
|
||||
private const val BASE_URL_ACCU = ""
|
||||
private const val BASE_URL_ACCU = "https://dataservice.accuweather.com/"
|
||||
|
||||
private fun getRetrofit(): Retrofit {
|
||||
return Retrofit.Builder()
|
||||
@ -22,4 +24,20 @@ class AccuweatherRepository {
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getWeatherIcon(icon: Int, isDaytime: Boolean): String = when(icon) {
|
||||
1, 2, 30, 33, 34 -> "01"
|
||||
3, 4, 35, 36 -> "02"
|
||||
5, 37 -> "50"
|
||||
6, 38 -> "03"
|
||||
7, 8 -> "04"
|
||||
11 -> "82"
|
||||
12, 13, 14, 18, 39, 40 -> "10"
|
||||
15 -> "09"
|
||||
16, 17, 41, 42 -> "11"
|
||||
32 -> "80"
|
||||
19, 20, 21, 22, 23, 24, 31, 43, 44 -> "13"
|
||||
25, 26, 29 -> "81"
|
||||
else -> ""
|
||||
} + if (isDaytime) "d" else "n"
|
||||
}
|
||||
|
@ -23,4 +23,157 @@ class HereRepository {
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getWeatherIcon(iconName: String, isDaytime: Boolean): String = when(iconName.substringAfter("night_")) {
|
||||
"sunny" -> "01"
|
||||
"clear" -> "01"
|
||||
"mostly_sunny" -> "01"
|
||||
"mostly_clear" -> "01"
|
||||
"passing_clounds" -> "02"
|
||||
"more_sun_than_clouds" -> "02"
|
||||
"scattered_clouds" -> "02"
|
||||
"partly_cloudy" -> "02"
|
||||
"a_mixture_of_sun_and_clouds" -> "03"
|
||||
"increasing_cloudiness" -> "03"
|
||||
"breaks_of_sun_late" -> "03"
|
||||
"afternoon_clouds" -> "03"
|
||||
"morning_clouds" -> "03"
|
||||
"partly_sunny" -> "03"
|
||||
"high_level_clouds" -> "03"
|
||||
"decreasing_cloudiness" -> "03"
|
||||
"clearing_skies" -> "01"
|
||||
"high_clouds" -> "03"
|
||||
"rain_early" -> "10"
|
||||
"heavy_rain_early" -> "10"
|
||||
"strong_thunderstorms" -> "09"
|
||||
"severe_thunderstorms" -> "09"
|
||||
"thundershowers" -> "11"
|
||||
"thunderstorms" -> "11"
|
||||
"tstorms_early" -> "11"
|
||||
"isolated_tstorms_late" -> "11"
|
||||
"scattered_tstorms_late" -> "11"
|
||||
"tstorms_late" -> "11"
|
||||
"tstorms" -> "11"
|
||||
"ice_fog" -> "82"
|
||||
"more_clouds_than_sun" -> "03"
|
||||
"broken_clouds" -> "03"
|
||||
"scattered_showers" -> "10"
|
||||
"a_few_showers" -> "10"
|
||||
"light_showers" -> "10"
|
||||
"passing_showers" -> "10"
|
||||
"rain_showers" -> "10"
|
||||
"showers" -> "10"
|
||||
"widely_scattered_tstorms" -> "11"
|
||||
"isolated_tstorms" -> "11"
|
||||
"a_few_tstorms" -> "11"
|
||||
"scattered_tstorms" -> "11"
|
||||
"hazy_sunshine" -> "50"
|
||||
"haze" -> "50"
|
||||
"smoke" -> "50"
|
||||
"low_level_haze" -> "50"
|
||||
"early_fog_followed_by_sunny_skies" -> "50"
|
||||
"early_fog" -> "82"
|
||||
"light_fog" -> "82"
|
||||
"fog" -> "82"
|
||||
"dense_fog" -> "82"
|
||||
//"night_haze"
|
||||
//"night_smoke"
|
||||
//"night_low_level_haze"
|
||||
//"night_widely_scattered_tstorms"
|
||||
//"night_isolated_tstorms"
|
||||
//"night_a_few_tstorms"
|
||||
//"night_scattered_tstorms"
|
||||
//"night_tstorms"
|
||||
//"night_clear"
|
||||
"mostly_cloudy" -> "03"
|
||||
"cloudy" -> "04"
|
||||
"overcast" -> "04"
|
||||
"low_clouds" -> "03"
|
||||
"hail" -> "10"
|
||||
"sleet" -> "81"
|
||||
"light_mixture_of_precip" -> "81"
|
||||
"icy_mix" -> "81"
|
||||
"mixture_of_precip" -> "81"
|
||||
"heavy_mixture_of_precip" -> "81"
|
||||
"snow_changing_to_rain" -> "81"
|
||||
"snow_changing_to_an_icy_mix" -> "81"
|
||||
"an_icy_mix_changing_to_snow" -> "81"
|
||||
"an_icy_mix_changing_to_rain" -> "81"
|
||||
"rain_changing_to_snow" -> "81"
|
||||
"rain_changing_to_an_icy_mix" -> "81"
|
||||
"light_icy_mix_early" -> "81"
|
||||
"icy_mix_early" -> "81"
|
||||
"light_icy_mix_late" -> "81"
|
||||
"icy_mix_late" -> "81"
|
||||
"snow_rain_mix" -> "81"
|
||||
"scattered_flurries" -> "13"
|
||||
"snow_flurries" -> "13"
|
||||
"light_snow_showers" -> "13"
|
||||
"snow_showers" -> "13"
|
||||
"light_snow" -> "13"
|
||||
"flurries_early" -> "13"
|
||||
"snow_showers_early" -> "13"
|
||||
"light_snow_early" -> "13"
|
||||
"flurries_late" -> "13"
|
||||
"snow_showers_late" -> "13"
|
||||
"light_snow_late" -> "13"
|
||||
//"night_decreasing_cloudiness"
|
||||
//"night_clearing_skies"
|
||||
//"night_high_level_clouds"
|
||||
//"night_high_clouds"
|
||||
//"night_scattered_showers"
|
||||
//"night_a_few_showers"
|
||||
//"night_light_showers"
|
||||
//"night_passing_showers"
|
||||
//"night_rain_showers"
|
||||
//"night_sprinkles"
|
||||
//"night_showers"
|
||||
//"night_mostly_clear"
|
||||
//"night_passing_clouds"
|
||||
//"night_scattered_clouds"
|
||||
//"night_partly_cloudy"
|
||||
//"increasing_cloudiness"
|
||||
//"night_afternoon_clouds"
|
||||
//"night_morning_clouds"
|
||||
//"night_broken_clouds"
|
||||
//"night_mostly_cloudy"
|
||||
"light_freezing_rain" -> "81"
|
||||
"freezing_rain" -> "81"
|
||||
"heavy_rain" -> "10"
|
||||
"lots_of_rain" -> "10"
|
||||
"tons_of_rain" -> "10"
|
||||
//"heavy_rain_early" -> "10"
|
||||
"heavy_rain_late" -> "10"
|
||||
"flash_floods" -> "10"
|
||||
"flood" -> "10"
|
||||
"drizzle" -> "10"
|
||||
"sprinkles" -> "10"
|
||||
"light_rain" -> "10"
|
||||
"sprinkles_early" -> "10"
|
||||
"light_rain_early" -> "10"
|
||||
"sprinkles_late" -> "10"
|
||||
"light_rain_late" -> "10"
|
||||
"rain" -> "10"
|
||||
"numerous_showers" -> "10"
|
||||
"showery" -> "10"
|
||||
"showers_early" -> "10"
|
||||
//"rain_early" -> "10"
|
||||
"showers_late" -> "10"
|
||||
"rain_late" -> "10"
|
||||
"snow" -> "13"
|
||||
"moderate_snow" -> "13"
|
||||
"snow_early" -> "13"
|
||||
"snow_late" -> "13"
|
||||
"heavy_snow" -> "13"
|
||||
"heavy_snow_early" -> "13"
|
||||
"heavy_snow_late" -> "13"
|
||||
"tornado" -> "80"
|
||||
"tropical_storm" -> "09"
|
||||
"hurricane" -> "80"
|
||||
"sandstorm" -> "50"
|
||||
"duststorm" -> "50"
|
||||
"snowstorm" -> "13"
|
||||
"blizzard" -> "13"
|
||||
else -> ""
|
||||
} + if (isDaytime) "d" else "n"
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import com.google.android.gms.location.*
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import com.tommasoberlose.anotherwidget.utils.setExactIfCanSchedule
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@ -32,11 +33,11 @@ class ActivityDetectionReceiver : BroadcastReceiver() {
|
||||
val result = ActivityTransitionResult.extractResult(intent)!!
|
||||
val lastEvent = result.transitionEvents.last()
|
||||
|
||||
if (lastEvent.activityType == DetectedActivity.WALKING || lastEvent.activityType == DetectedActivity.RUNNING && lastEvent.transitionType == ActivityTransition.ACTIVITY_TRANSITION_EXIT) {
|
||||
if ((lastEvent.activityType == DetectedActivity.WALKING || lastEvent.activityType == DetectedActivity.RUNNING) && lastEvent.transitionType == ActivityTransition.ACTIVITY_TRANSITION_EXIT) {
|
||||
requestDailySteps(context)
|
||||
}
|
||||
} else {
|
||||
if (intent.action == Intent.ACTION_BOOT_COMPLETED || intent.action == Intent.ACTION_MY_PACKAGE_REPLACED && Preferences.showDailySteps && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || context.checkGrantedPermission(Manifest.permission.ACTIVITY_RECOGNITION)) {
|
||||
if ((intent.action == Intent.ACTION_BOOT_COMPLETED || intent.action == Intent.ACTION_MY_PACKAGE_REPLACED) && Preferences.showDailySteps && (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || context.checkGrantedPermission(Manifest.permission.ACTIVITY_RECOGNITION))) {
|
||||
resetDailySteps(context)
|
||||
registerFence(context)
|
||||
} else {
|
||||
@ -45,13 +46,6 @@ class ActivityDetectionReceiver : BroadcastReceiver() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetDailySteps(context: Context) {
|
||||
Kotpref.init(context)
|
||||
Preferences.blockingBulk {
|
||||
remove(Preferences::googleFitSteps)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val FITNESS_OPTIONS: FitnessOptions = FitnessOptions.builder()
|
||||
.addDataType(DataType.TYPE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
|
||||
@ -86,7 +80,7 @@ class ActivityDetectionReceiver : BroadcastReceiver() {
|
||||
context,
|
||||
2,
|
||||
Intent(context, ActivityDetectionReceiver::class.java),
|
||||
0
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
)
|
||||
|
||||
@ -105,7 +99,7 @@ class ActivityDetectionReceiver : BroadcastReceiver() {
|
||||
context,
|
||||
2,
|
||||
Intent(context, ActivityDetectionReceiver::class.java),
|
||||
0
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
)
|
||||
|
||||
@ -115,10 +109,13 @@ class ActivityDetectionReceiver : BroadcastReceiver() {
|
||||
context,
|
||||
2,
|
||||
Intent(context, ActivityDetectionReceiver::class.java),
|
||||
0
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
).cancel()
|
||||
}
|
||||
}
|
||||
|
||||
resetDailySteps(context)
|
||||
clearTimeout(context)
|
||||
}
|
||||
|
||||
fun requestDailySteps(context: Context) {
|
||||
@ -162,20 +159,39 @@ class ActivityDetectionReceiver : BroadcastReceiver() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetDailySteps(context: Context) {
|
||||
Kotpref.init(context)
|
||||
Preferences.blockingBulk {
|
||||
remove(Preferences::googleFitSteps)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setTimeout(context: Context) {
|
||||
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||
cancel(PendingIntent.getBroadcast(context, 5, Intent(context, ActivityDetectionReceiver::class.java), 0))
|
||||
setExact(
|
||||
setExactIfCanSchedule(
|
||||
AlarmManager.RTC,
|
||||
Calendar.getInstance().timeInMillis + 5 * 60 * 1000,
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
5,
|
||||
Intent(context, ActivityDetectionReceiver::class.java),
|
||||
0
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearTimeout(context: Context) {
|
||||
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||
cancel(
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
5,
|
||||
Intent(context, ActivityDetectionReceiver::class.java),
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +0,0 @@
|
||||
package com.tommasoberlose.anotherwidget.receivers
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tommasoberlose.anotherwidget.global.Actions
|
||||
import java.lang.Exception
|
||||
|
||||
class CrashlyticsReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (intent.action == Actions.ACTION_REPORT_CRASH) {
|
||||
val exception: Exception = intent.getSerializableExtra(EXCEPTION) as Exception
|
||||
FirebaseCrashlytics.getInstance().recordException(exception)
|
||||
FirebaseCrashlytics.getInstance().sendUnsentReports()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private const val EXCEPTION = "EXCEPTION"
|
||||
|
||||
fun sendCrash(context: Context, exception: Exception) {
|
||||
context.sendBroadcast(Intent(context, CrashlyticsReceiver::class.java).apply {
|
||||
action = Actions.ACTION_REPORT_CRASH
|
||||
putExtra(EXCEPTION, exception)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -16,6 +16,7 @@ import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import com.tommasoberlose.anotherwidget.utils.setExactIfCanSchedule
|
||||
import java.lang.Exception
|
||||
import java.util.*
|
||||
|
||||
@ -23,19 +24,26 @@ import java.util.*
|
||||
class NotificationListener : NotificationListenerService() {
|
||||
override fun onListenerConnected() {
|
||||
MediaPlayerHelper.updatePlayingMediaInfo(this)
|
||||
MainWidget.updateWidget(this)
|
||||
ActiveNotificationsHelper.clearLastNotification(this)
|
||||
super.onListenerConnected()
|
||||
}
|
||||
|
||||
override fun onListenerDisconnected() {
|
||||
MediaPlayerHelper.updatePlayingMediaInfo(this)
|
||||
ActiveNotificationsHelper.clearLastNotification(this)
|
||||
super.onListenerDisconnected()
|
||||
}
|
||||
|
||||
override fun onNotificationPosted(sbn: StatusBarNotification?) {
|
||||
sbn?.notification?.extras?.let { bundle ->
|
||||
bundle.getParcelable<MediaSession.Token>(Notification.EXTRA_MEDIA_SESSION)?.let {
|
||||
MediaPlayerHelper.updatePlayingMediaInfo(this)
|
||||
if (Preferences.showMusic)
|
||||
MediaPlayerHelper.updatePlayingMediaInfo(this)
|
||||
} ?: run {
|
||||
val isGroupHeader = sbn.notification.flags and Notification.FLAG_GROUP_SUMMARY != 0
|
||||
val isOngoing = sbn.notification.flags and Notification.FLAG_ONGOING_EVENT != 0
|
||||
|
||||
if (bundle.containsKey(Notification.EXTRA_TITLE) && !isGroupHeader && !isOngoing && ActiveNotificationsHelper.isAppAccepted(sbn.packageName) && !sbn.packageName.contains("com.android.systemui")) {
|
||||
if (Preferences.showNotifications && bundle.containsKey(Notification.EXTRA_TITLE) && !isGroupHeader && !isOngoing && ActiveNotificationsHelper.isAppAccepted(sbn.packageName) && !sbn.packageName.contains("com.android.systemui")) {
|
||||
Preferences.lastNotificationId = sbn.id
|
||||
Preferences.lastNotificationTitle = bundle.getString(Notification.EXTRA_TITLE) ?: ""
|
||||
try {
|
||||
@ -59,15 +67,13 @@ class NotificationListener : NotificationListenerService() {
|
||||
}
|
||||
|
||||
override fun onNotificationRemoved(sbn: StatusBarNotification?) {
|
||||
MediaPlayerHelper.updatePlayingMediaInfo(this)
|
||||
|
||||
if (Preferences.showMusic)
|
||||
MediaPlayerHelper.updatePlayingMediaInfo(this)
|
||||
sbn?.let {
|
||||
if (sbn.id == Preferences.lastNotificationId && sbn.packageName == Preferences.lastNotificationPackage) {
|
||||
if (Preferences.showNotifications && sbn.id == Preferences.lastNotificationId && sbn.packageName == Preferences.lastNotificationPackage) {
|
||||
ActiveNotificationsHelper.clearLastNotification(this)
|
||||
}
|
||||
}
|
||||
|
||||
MainWidget.updateWidget(this)
|
||||
super.onNotificationRemoved(sbn)
|
||||
}
|
||||
|
||||
@ -76,10 +82,9 @@ class NotificationListener : NotificationListenerService() {
|
||||
val intent = Intent(context, UpdatesReceiver::class.java).apply {
|
||||
action = Actions.ACTION_CLEAR_NOTIFICATION
|
||||
}
|
||||
cancel(PendingIntent.getBroadcast(context, 28943, intent, 0))
|
||||
val timeoutPref = Constants.GlanceNotificationTimer.fromInt(Preferences.hideNotificationAfter)
|
||||
if (timeoutPref != Constants.GlanceNotificationTimer.WHEN_DISMISSED) {
|
||||
setExact(
|
||||
setExactIfCanSchedule(
|
||||
AlarmManager.RTC,
|
||||
Calendar.getInstance().timeInMillis + when (timeoutPref) {
|
||||
Constants.GlanceNotificationTimer.HALF_MINUTE -> 30 * 1000
|
||||
@ -87,16 +92,27 @@ class NotificationListener : NotificationListenerService() {
|
||||
Constants.GlanceNotificationTimer.FIVE_MINUTES -> 5 * 60 * 1000
|
||||
Constants.GlanceNotificationTimer.TEN_MINUTES -> 10 * 60 * 1000
|
||||
Constants.GlanceNotificationTimer.FIFTEEN_MINUTES -> 15 * 60 * 1000
|
||||
else -> 0
|
||||
else -> 60 * 1000
|
||||
},
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
5,
|
||||
intent,
|
||||
0
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun clearTimeout(context: Context) {
|
||||
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||
val intent = Intent(context, UpdatesReceiver::class.java).apply {
|
||||
action = Actions.ACTION_CLEAR_NOTIFICATION
|
||||
}
|
||||
cancel(PendingIntent.getBroadcast(context, 5, intent, PendingIntent.FLAG_IMMUTABLE))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,11 +12,9 @@ import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.*
|
||||
import com.tommasoberlose.anotherwidget.models.Event
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.joda.time.Period
|
||||
import com.tommasoberlose.anotherwidget.utils.setExactIfCanSchedule
|
||||
import java.util.*
|
||||
import org.joda.time.Period
|
||||
|
||||
|
||||
class UpdatesReceiver : BroadcastReceiver() {
|
||||
@ -27,18 +25,18 @@ class UpdatesReceiver : BroadcastReceiver() {
|
||||
Intent.ACTION_MY_PACKAGE_REPLACED,
|
||||
Intent.ACTION_TIME_CHANGED,
|
||||
Intent.ACTION_TIMEZONE_CHANGED,
|
||||
Intent.ACTION_LOCALE_CHANGED,
|
||||
Intent.ACTION_LOCALE_CHANGED -> {
|
||||
CalendarHelper.updateEventList(context)
|
||||
MediaPlayerHelper.updatePlayingMediaInfo(context)
|
||||
ActiveNotificationsHelper.clearLastNotification(context)
|
||||
GreetingsHelper.toggleGreetings(context)
|
||||
}
|
||||
|
||||
Intent.ACTION_DATE_CHANGED,
|
||||
Actions.ACTION_CALENDAR_UPDATE -> {
|
||||
ActiveNotificationsHelper.clearLastNotification(context)
|
||||
MediaPlayerHelper.updatePlayingMediaInfo(context)
|
||||
CalendarHelper.updateEventList(context)
|
||||
}
|
||||
|
||||
"com.sec.android.widgetapp.APPWIDGET_RESIZE",
|
||||
AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED,
|
||||
Actions.ACTION_ALARM_UPDATE,
|
||||
Actions.ACTION_UPDATE_GREETINGS,
|
||||
Actions.ACTION_TIME_UPDATE -> {
|
||||
MainWidget.updateWidget(context)
|
||||
if (intent.hasExtra(EVENT_ID)) {
|
||||
@ -46,17 +44,21 @@ class UpdatesReceiver : BroadcastReceiver() {
|
||||
}
|
||||
}
|
||||
|
||||
Actions.ACTION_CLEAR_NOTIFICATION -> {
|
||||
ActiveNotificationsHelper.clearLastNotification(context)
|
||||
"com.sec.android.widgetapp.APPWIDGET_RESIZE",
|
||||
AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED,
|
||||
Actions.ACTION_ALARM_UPDATE,
|
||||
Actions.ACTION_UPDATE_GREETINGS -> {
|
||||
MainWidget.updateWidget(context)
|
||||
}
|
||||
|
||||
Actions.ACTION_CLEAR_NOTIFICATION -> {
|
||||
ActiveNotificationsHelper.clearLastNotification(context)
|
||||
}
|
||||
|
||||
Actions.ACTION_REFRESH -> {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
CalendarHelper.updateEventList(context)
|
||||
MediaPlayerHelper.updatePlayingMediaInfo(context)
|
||||
WeatherHelper.updateWeather(context)
|
||||
}
|
||||
CalendarHelper.updateEventList(context)
|
||||
MediaPlayerHelper.updatePlayingMediaInfo(context)
|
||||
WeatherHelper.updateWeather(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -71,7 +73,7 @@ class UpdatesReceiver : BroadcastReceiver() {
|
||||
if (eventId == null) {
|
||||
// schedule ACTION_CALENDAR_UPDATE at midnight (ACTION_DATE_CHANGED no longer works)
|
||||
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||
setExact(
|
||||
setExactIfCanSchedule(
|
||||
AlarmManager.RTC,
|
||||
Calendar.getInstance().apply {
|
||||
set(Calendar.MILLISECOND, 0)
|
||||
@ -86,7 +88,7 @@ class UpdatesReceiver : BroadcastReceiver() {
|
||||
Intent(context, UpdatesReceiver::class.java).apply {
|
||||
action = Actions.ACTION_CALENDAR_UPDATE
|
||||
},
|
||||
0
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -95,7 +97,7 @@ class UpdatesReceiver : BroadcastReceiver() {
|
||||
setEventUpdate(context, event)
|
||||
}
|
||||
} else {
|
||||
val event = eventRepository.getEventByEventId(eventId)
|
||||
val event = eventRepository.getEventById(eventId)
|
||||
if (event != null) {
|
||||
setEventUpdate(context, event)
|
||||
}
|
||||
@ -161,18 +163,18 @@ class UpdatesReceiver : BroadcastReceiver() {
|
||||
add(Calendar.DATE, 1)
|
||||
}.timeInMillis <= fireTime) return
|
||||
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||
setExact(
|
||||
setExactIfCanSchedule(
|
||||
AlarmManager.RTC,
|
||||
fireTime.coerceAtLeast(now.timeInMillis + 1000 * 60),
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
event.eventID.toInt(),
|
||||
event.id.toInt(),
|
||||
Intent(context, UpdatesReceiver::class.java).apply {
|
||||
action = Actions.ACTION_TIME_UPDATE
|
||||
if (event.startDate > now.timeInMillis)
|
||||
putExtra(EVENT_ID, event.eventID)
|
||||
putExtra(EVENT_ID, event.id)
|
||||
},
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -182,12 +184,12 @@ class UpdatesReceiver : BroadcastReceiver() {
|
||||
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||
cancel(PendingIntent.getBroadcast(context, 0, Intent(context, UpdatesReceiver::class.java).apply {
|
||||
action = Actions.ACTION_CALENDAR_UPDATE
|
||||
}, 0))
|
||||
}, PendingIntent.FLAG_IMMUTABLE))
|
||||
val eventRepository = EventRepository(context)
|
||||
eventRepository.getFutureEvents().forEach {
|
||||
cancel(PendingIntent.getBroadcast(context, it.eventID.toInt(), Intent(context, UpdatesReceiver::class.java).apply {
|
||||
cancel(PendingIntent.getBroadcast(context, it.id.toInt(), Intent(context, UpdatesReceiver::class.java).apply {
|
||||
action = Actions.ACTION_TIME_UPDATE
|
||||
}, 0))
|
||||
}, PendingIntent.FLAG_IMMUTABLE))
|
||||
}
|
||||
eventRepository.close()
|
||||
}
|
||||
|
@ -1,18 +1,12 @@
|
||||
package com.tommasoberlose.anotherwidget.receivers
|
||||
|
||||
import android.app.AlarmManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.tommasoberlose.anotherwidget.global.Actions
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.*
|
||||
|
||||
import com.tommasoberlose.anotherwidget.services.WeatherWorker
|
||||
|
||||
class WeatherReceiver : BroadcastReceiver() {
|
||||
|
||||
@ -23,57 +17,21 @@ class WeatherReceiver : BroadcastReceiver() {
|
||||
Intent.ACTION_TIMEZONE_CHANGED,
|
||||
Intent.ACTION_LOCALE_CHANGED,
|
||||
Intent.ACTION_TIME_CHANGED,
|
||||
Actions.ACTION_WEATHER_UPDATE -> setUpdates(context)
|
||||
Actions.ACTION_WEATHER_UPDATE -> {
|
||||
WeatherHelper.updateWeather(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MINUTE = 60 * 1000L
|
||||
fun setUpdates(context: Context) {
|
||||
if (Preferences.showWeather) {
|
||||
val interval = MINUTE * when (Preferences.weatherRefreshPeriod) {
|
||||
0 -> 30
|
||||
1 -> 60
|
||||
2 -> 60L * 3
|
||||
3 -> 60L * 6
|
||||
4 -> 60L * 12
|
||||
5 -> 60L * 24
|
||||
else -> 60
|
||||
}
|
||||
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||
setExact(
|
||||
AlarmManager.RTC,
|
||||
System.currentTimeMillis() + interval,
|
||||
PendingIntent.getBroadcast(context, 0, Intent(context, WeatherReceiver::class.java).apply { action = Actions.ACTION_WEATHER_UPDATE }, 0)
|
||||
)
|
||||
}
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
WeatherHelper.updateWeather(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setOneTimeUpdate(context: Context) {
|
||||
if (Preferences.showWeather) {
|
||||
listOf(10, 20, 30).forEach {
|
||||
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||
setExactAndAllowWhileIdle(
|
||||
AlarmManager.RTC,
|
||||
it * MINUTE,
|
||||
PendingIntent.getBroadcast(context, it, Intent(context, WeatherReceiver::class.java).apply { action = Actions.ACTION_WEATHER_UPDATE }, 0)
|
||||
)
|
||||
}
|
||||
}
|
||||
WeatherWorker.enqueueTrigger(context)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeUpdates(context: Context) {
|
||||
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||
cancel(PendingIntent.getBroadcast(context, 0, Intent(context, WeatherReceiver::class.java).apply { action = Actions.ACTION_WEATHER_UPDATE }, 0))
|
||||
listOf(10, 20, 30).forEach {
|
||||
cancel(PendingIntent.getBroadcast(context, it, Intent(context, WeatherReceiver::class.java).apply { action = Actions.ACTION_WEATHER_UPDATE }, 0))
|
||||
}
|
||||
}
|
||||
WeatherWorker.cancelTrigger(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,61 +0,0 @@
|
||||
package com.tommasoberlose.anotherwidget.services
|
||||
|
||||
import android.app.job.JobInfo
|
||||
import android.app.job.JobParameters
|
||||
import android.app.job.JobScheduler
|
||||
import android.app.job.JobService
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.provider.CalendarContract
|
||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
|
||||
class BatteryListenerJob : JobService() {
|
||||
override fun onStartJob(params: JobParameters): Boolean {
|
||||
MainWidget.updateWidget(this)
|
||||
schedule(
|
||||
this
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun onStopJob(params: JobParameters): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val chargingJobId = 1006
|
||||
private const val notChargingJobId = 1007
|
||||
fun schedule(context: Context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
remove(context)
|
||||
val componentName = ComponentName(
|
||||
context,
|
||||
EventListenerJob::class.java
|
||||
)
|
||||
with(context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler) {
|
||||
schedule(
|
||||
JobInfo.Builder(chargingJobId, componentName)
|
||||
.setRequiresCharging(true)
|
||||
.setPersisted(true)
|
||||
.build()
|
||||
)
|
||||
schedule(
|
||||
JobInfo.Builder(notChargingJobId, componentName)
|
||||
.setRequiresCharging(false)
|
||||
.setPersisted(true)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun remove(context: Context) {
|
||||
val js = context.getSystemService(JobScheduler::class.java)
|
||||
js?.cancel(chargingJobId)
|
||||
js?.cancel(notChargingJobId)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
package com.tommasoberlose.anotherwidget.services
|
||||
|
||||
import android.app.job.JobInfo
|
||||
import android.app.job.JobInfo.TriggerContentUri
|
||||
import android.app.job.JobParameters
|
||||
import android.app.job.JobScheduler
|
||||
import android.app.job.JobService
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.provider.CalendarContract
|
||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
|
||||
|
||||
|
||||
class EventListenerJob : JobService() {
|
||||
override fun onStartJob(params: JobParameters): Boolean {
|
||||
CalendarHelper.updateEventList(this)
|
||||
schedule(
|
||||
this
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun onStopJob(params: JobParameters): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val jobId = 1005
|
||||
fun schedule(context: Context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
val componentName = ComponentName(
|
||||
context,
|
||||
EventListenerJob::class.java
|
||||
)
|
||||
with(context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler) {
|
||||
schedule(
|
||||
JobInfo.Builder(jobId, componentName)
|
||||
.addTriggerContentUri(TriggerContentUri(
|
||||
CalendarContract.CONTENT_URI,
|
||||
TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS
|
||||
))
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun remove(context: Context) {
|
||||
val js = context.getSystemService(JobScheduler::class.java)
|
||||
js?.cancel(jobId)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
package com.tommasoberlose.anotherwidget.services
|
||||
|
||||
import android.Manifest
|
||||
import android.app.*
|
||||
import android.app.job.JobScheduler
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.Address
|
||||
import android.location.Geocoder
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.core.app.*
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.google.android.gms.location.LocationServices
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.lang.Exception
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class LocationService : Service() {
|
||||
|
||||
private var job: Job? = null
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
startForeground(LOCATION_ACCESS_NOTIFICATION_ID, getLocationAccessNotification())
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
startForeground(LOCATION_ACCESS_NOTIFICATION_ID, getLocationAccessNotification())
|
||||
job?.cancel()
|
||||
job = GlobalScope.launch(Dispatchers.IO) {
|
||||
if (checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION) &&
|
||||
(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R ||
|
||||
checkGrantedPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION))
|
||||
) {
|
||||
if (com.google.android.gms.common.GoogleApiAvailability.getInstance()
|
||||
.isGooglePlayServicesAvailable(this@LocationService)
|
||||
== com.google.android.gms.common.ConnectionResult.SUCCESS
|
||||
) {
|
||||
LocationServices.getFusedLocationProviderClient(this@LocationService).lastLocation
|
||||
} else {
|
||||
val lm = getSystemService(LOCATION_SERVICE) as android.location.LocationManager
|
||||
var location: android.location.Location? = null
|
||||
for (provider in arrayOf(
|
||||
"fused", // LocationManager.FUSED_PROVIDER,
|
||||
android.location.LocationManager.GPS_PROVIDER,
|
||||
android.location.LocationManager.NETWORK_PROVIDER,
|
||||
android.location.LocationManager.PASSIVE_PROVIDER
|
||||
)) {
|
||||
if (lm.isProviderEnabled(provider)) {
|
||||
location = lm.getLastKnownLocation(provider)
|
||||
if (location != null) break
|
||||
}
|
||||
}
|
||||
com.google.android.gms.tasks.Tasks.forResult(location)
|
||||
}.addOnCompleteListener { task ->
|
||||
val networkApi = WeatherNetworkApi(this@LocationService)
|
||||
if (task.isSuccessful) {
|
||||
val location = task.result
|
||||
if (location != null) {
|
||||
Preferences.customLocationLat = location.latitude.toString()
|
||||
Preferences.customLocationLon = location.longitude.toString()
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
networkApi.updateWeather()
|
||||
withContext(Dispatchers.Main) {
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
} else {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
networkApi.updateWeather()
|
||||
withContext(Dispatchers.Main) {
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
job?.cancel()
|
||||
job = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LOCATION_ACCESS_NOTIFICATION_ID = 28465
|
||||
|
||||
@JvmStatic
|
||||
fun requestNewLocation(context: Context) {
|
||||
ContextCompat.startForegroundService(context, Intent(context, LocationService::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getLocationAccessNotification(): Notification {
|
||||
with(NotificationManagerCompat.from(this)) {
|
||||
// Create channel
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||
createNotificationChannel(
|
||||
NotificationChannel(
|
||||
getString(R.string.location_access_notification_channel_id),
|
||||
getString(R.string.location_access_notification_channel_name),
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
).apply {
|
||||
description = getString(R.string.location_access_notification_channel_description)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val builder = NotificationCompat.Builder(this@LocationService, getString(R.string.location_access_notification_channel_id))
|
||||
.setSmallIcon(R.drawable.ic_stat_notification)
|
||||
.setContentTitle(getString(R.string.location_access_notification_title))
|
||||
.setOngoing(true)
|
||||
.setColor(ContextCompat.getColor(this@LocationService, R.color.colorAccent))
|
||||
|
||||
// Main intent that open the activity
|
||||
builder.setContentIntent(PendingIntent.getActivity(this@LocationService, 0, Intent(this@LocationService, MainActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,226 +0,0 @@
|
||||
package com.tommasoberlose.anotherwidget.services
|
||||
|
||||
import android.Manifest
|
||||
import android.app.*
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.db.EventRepository
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.applyFilters
|
||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.sortEvents
|
||||
import com.tommasoberlose.anotherwidget.models.Event
|
||||
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import me.everything.providers.android.calendar.CalendarProvider
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class UpdateCalendarService : Service() {
|
||||
|
||||
companion object {
|
||||
const val CALENDAR_SYNC_NOTIFICATION_ID = 28468
|
||||
fun enqueueWork(context: Context) {
|
||||
ContextCompat.startForegroundService(context, Intent(context, UpdateCalendarService::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
startForeground(CALENDAR_SYNC_NOTIFICATION_ID, getCalendarSyncNotification())
|
||||
}
|
||||
|
||||
private var job: Job? = null
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
startForeground(CALENDAR_SYNC_NOTIFICATION_ID, getCalendarSyncNotification())
|
||||
job?.cancel()
|
||||
job = GlobalScope.launch(Dispatchers.IO) {
|
||||
|
||||
UpdatesReceiver.removeUpdates(this@UpdateCalendarService)
|
||||
|
||||
val eventRepository = EventRepository(this@UpdateCalendarService)
|
||||
if (Preferences.showEvents) {
|
||||
val eventList = ArrayList<Event>()
|
||||
|
||||
// fetch all events from now to next ACTION_CALENDAR_UPDATE + limit
|
||||
val now = Calendar.getInstance()
|
||||
val limit = Calendar.getInstance().apply {
|
||||
set(Calendar.MILLISECOND, 0)
|
||||
set(Calendar.SECOND, 0)
|
||||
set(Calendar.MINUTE, 0)
|
||||
set(Calendar.HOUR_OF_DAY, 0)
|
||||
add(Calendar.DATE, 1)
|
||||
when (Preferences.showUntil) {
|
||||
0 -> add(Calendar.HOUR, 3)
|
||||
1 -> add(Calendar.HOUR, 6)
|
||||
2 -> add(Calendar.HOUR, 12)
|
||||
3 -> add(Calendar.DAY_OF_MONTH, 1)
|
||||
4 -> add(Calendar.DAY_OF_MONTH, 3)
|
||||
5 -> add(Calendar.DAY_OF_MONTH, 7)
|
||||
6 -> add(Calendar.MINUTE, 30)
|
||||
7 -> add(Calendar.HOUR, 1)
|
||||
else -> add(Calendar.HOUR, 6)
|
||||
}
|
||||
}
|
||||
|
||||
if (!checkGrantedPermission(
|
||||
Manifest.permission.READ_CALENDAR
|
||||
)
|
||||
) {
|
||||
eventRepository.resetNextEventData()
|
||||
eventRepository.clearEvents()
|
||||
Preferences.showEvents = false
|
||||
} else {
|
||||
try {
|
||||
val provider = CalendarProvider(this@UpdateCalendarService)
|
||||
// apply time zone offset to correctly fetch all-day events
|
||||
val data = provider.getInstances(
|
||||
now.timeInMillis + now.timeZone.getOffset(now.timeInMillis).coerceAtMost(0),
|
||||
limit.timeInMillis + limit.timeZone.getOffset(limit.timeInMillis).coerceAtLeast(0)
|
||||
)
|
||||
if (data != null) {
|
||||
val instances = data.list
|
||||
for (instance in instances) {
|
||||
try {
|
||||
val e = provider.getEvent(instance.eventId)
|
||||
if (e == null || e.deleted || CalendarHelper.getFilteredCalendarIdList().contains(e.calendarId))
|
||||
continue
|
||||
if (e.allDay) {
|
||||
val start = Calendar.getInstance()
|
||||
start.timeInMillis = instance.begin
|
||||
val end = Calendar.getInstance()
|
||||
end.timeInMillis = instance.end
|
||||
instance.begin =
|
||||
start.timeInMillis - start.timeZone.getOffset(start.timeInMillis)
|
||||
instance.end =
|
||||
end.timeInMillis - end.timeZone.getOffset(end.timeInMillis)
|
||||
}
|
||||
if (instance.begin <= limit.timeInMillis && now.timeInMillis < instance.end) {
|
||||
/* Following check may result in "fake" all-day events with
|
||||
* non-UTC start/end time, and therefore cannot be found by
|
||||
* Calendar when tapped to open details.
|
||||
// Check all day events
|
||||
val startDate = Calendar.getInstance()
|
||||
startDate.timeInMillis = instance.begin
|
||||
val endDate = Calendar.getInstance()
|
||||
endDate.timeInMillis = instance.end
|
||||
|
||||
val isAllDay = e.allDay || (
|
||||
startDate.get(Calendar.MILLISECOND) == 0
|
||||
&& startDate.get(Calendar.SECOND) == 0
|
||||
&& startDate.get(Calendar.MINUTE) == 0
|
||||
&& startDate.get(Calendar.HOUR_OF_DAY) == 0
|
||||
&& endDate.get(Calendar.MILLISECOND) == 0
|
||||
&& endDate.get(Calendar.SECOND) == 0
|
||||
&& endDate.get(Calendar.MINUTE) == 0
|
||||
&& endDate.get(Calendar.HOUR_OF_DAY) == 0
|
||||
)
|
||||
*/
|
||||
|
||||
eventList.add(
|
||||
Event(
|
||||
id = instance.id,
|
||||
eventID = e.id,
|
||||
title = e.title ?: "",
|
||||
startDate = instance.begin,
|
||||
endDate = instance.end,
|
||||
calendarID = e.calendarId.toInt(),
|
||||
allDay = e.allDay,
|
||||
address = e.eventLocation ?: "",
|
||||
selfAttendeeStatus = e.selfAttendeeStatus.toInt(),
|
||||
availability = e.availability
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val sortedEvents = eventList.sortEvents()
|
||||
val filteredEventList = sortedEvents
|
||||
.applyFilters()
|
||||
|
||||
if (filteredEventList.isEmpty()) {
|
||||
eventRepository.resetNextEventData()
|
||||
eventRepository.clearEvents()
|
||||
} else {
|
||||
eventRepository.saveEvents(
|
||||
sortedEvents
|
||||
)
|
||||
eventRepository.saveNextEventData(filteredEventList.first())
|
||||
}
|
||||
} catch (ignored: java.lang.Exception) {
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eventRepository.resetNextEventData()
|
||||
eventRepository.clearEvents()
|
||||
}
|
||||
eventRepository.close()
|
||||
|
||||
UpdatesReceiver.setUpdates(this@UpdateCalendarService)
|
||||
MainWidget.updateWidget(this@UpdateCalendarService)
|
||||
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
|
||||
stopSelf()
|
||||
}
|
||||
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
job?.cancel()
|
||||
job = null
|
||||
}
|
||||
|
||||
private fun getCalendarSyncNotification(): Notification {
|
||||
with(NotificationManagerCompat.from(this)) {
|
||||
// Create channel
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||
createNotificationChannel(
|
||||
NotificationChannel(
|
||||
getString(R.string.calendar_sync_notification_channel_id),
|
||||
getString(R.string.calendar_sync_notification_channel_name),
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
).apply {
|
||||
description = getString(R.string.calendar_sync_notification_channel_description)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val builder = NotificationCompat.Builder(this@UpdateCalendarService, getString(R.string.calendar_sync_notification_channel_id))
|
||||
.setSmallIcon(R.drawable.ic_stat_notification)
|
||||
.setContentTitle(getString(R.string.calendar_sync_notification_title))
|
||||
.setOngoing(true)
|
||||
.setColor(ContextCompat.getColor(this@UpdateCalendarService, R.color.colorAccent))
|
||||
|
||||
// Main intent that open the activity
|
||||
builder.setContentIntent(PendingIntent.getActivity(this@UpdateCalendarService, 0, Intent(this@UpdateCalendarService, MainActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,199 @@
|
||||
package com.tommasoberlose.anotherwidget.services
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.provider.CalendarContract
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import com.tommasoberlose.anotherwidget.db.EventRepository
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.applyFilters
|
||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.sortEvents
|
||||
import com.tommasoberlose.anotherwidget.models.Event
|
||||
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
|
||||
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import java.util.*
|
||||
import me.everything.providers.android.calendar.CalendarProvider
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
class UpdateCalendarWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||
|
||||
override fun doWork(): Result {
|
||||
val context = applicationContext
|
||||
UpdatesReceiver.removeUpdates(context)
|
||||
val eventRepository = EventRepository(context)
|
||||
|
||||
if (Preferences.showEvents) {
|
||||
if (!context.checkGrantedPermission(Manifest.permission.READ_CALENDAR)) {
|
||||
eventRepository.resetNextEventData()
|
||||
eventRepository.clearEvents()
|
||||
} else {
|
||||
// fetch all events from now to next ACTION_CALENDAR_UPDATE + limit
|
||||
val now = Calendar.getInstance()
|
||||
val limit = Calendar.getInstance().apply {
|
||||
set(Calendar.MILLISECOND, 0)
|
||||
set(Calendar.SECOND, 0)
|
||||
set(Calendar.MINUTE, 0)
|
||||
set(Calendar.HOUR_OF_DAY, 0)
|
||||
add(Calendar.DATE, 1)
|
||||
when (Preferences.showUntil) {
|
||||
0 -> add(Calendar.HOUR, 3)
|
||||
1 -> add(Calendar.HOUR, 6)
|
||||
2 -> add(Calendar.HOUR, 12)
|
||||
3 -> add(Calendar.DAY_OF_MONTH, 1)
|
||||
4 -> add(Calendar.DAY_OF_MONTH, 3)
|
||||
5 -> add(Calendar.DAY_OF_MONTH, 7)
|
||||
6 -> add(Calendar.MINUTE, 30)
|
||||
7 -> add(Calendar.HOUR, 1)
|
||||
else -> add(Calendar.HOUR, 6)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
val eventList = ArrayList<Event>()
|
||||
val provider = CalendarProvider(context)
|
||||
// apply time zone offset to correctly fetch all-day events
|
||||
val data = provider.getInstances(
|
||||
now.timeInMillis + now.timeZone.getOffset(now.timeInMillis).coerceAtMost(0),
|
||||
limit.timeInMillis + limit.timeZone.getOffset(limit.timeInMillis).coerceAtLeast(0)
|
||||
)
|
||||
if (data != null) {
|
||||
val filteredCalendarIdList = CalendarHelper.getFilteredCalendarIdList()
|
||||
for (instance in data.list) {
|
||||
try {
|
||||
val e = provider.getEvent(instance.eventId)
|
||||
if (e == null || e.deleted || filteredCalendarIdList.contains(e.calendarId))
|
||||
continue
|
||||
if (e.allDay) {
|
||||
val start = Calendar.getInstance()
|
||||
start.timeInMillis = instance.begin
|
||||
val end = Calendar.getInstance()
|
||||
end.timeInMillis = instance.end
|
||||
instance.begin =
|
||||
start.timeInMillis - start.timeZone.getOffset(start.timeInMillis)
|
||||
instance.end =
|
||||
end.timeInMillis - end.timeZone.getOffset(end.timeInMillis)
|
||||
}
|
||||
if (instance.begin <= limit.timeInMillis && now.timeInMillis < instance.end) {
|
||||
/* Following check may result in "fake" all-day events with
|
||||
* non-UTC start/end time, and therefore cannot be found by
|
||||
* Calendar when tapped to open details.
|
||||
// Check all day events
|
||||
val startDate = Calendar.getInstance()
|
||||
startDate.timeInMillis = instance.begin
|
||||
val endDate = Calendar.getInstance()
|
||||
endDate.timeInMillis = instance.end
|
||||
|
||||
val isAllDay = e.allDay || (
|
||||
startDate.get(Calendar.MILLISECOND) == 0
|
||||
&& startDate.get(Calendar.SECOND) == 0
|
||||
&& startDate.get(Calendar.MINUTE) == 0
|
||||
&& startDate.get(Calendar.HOUR_OF_DAY) == 0
|
||||
&& endDate.get(Calendar.MILLISECOND) == 0
|
||||
&& endDate.get(Calendar.SECOND) == 0
|
||||
&& endDate.get(Calendar.MINUTE) == 0
|
||||
&& endDate.get(Calendar.HOUR_OF_DAY) == 0
|
||||
)
|
||||
*/
|
||||
|
||||
eventList.add(
|
||||
Event(
|
||||
id = instance.id,
|
||||
eventID = e.id,
|
||||
title = e.title ?: "",
|
||||
startDate = instance.begin,
|
||||
endDate = instance.end,
|
||||
calendarID = e.calendarId,
|
||||
allDay = e.allDay,
|
||||
address = e.eventLocation ?: "",
|
||||
selfAttendeeStatus = e.selfAttendeeStatus.toInt(),
|
||||
availability = e.availability
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val sortedEvents = eventList.sortEvents()
|
||||
val filteredEventList = sortedEvents.applyFilters()
|
||||
|
||||
if (filteredEventList.isEmpty()) {
|
||||
eventRepository.resetNextEventData()
|
||||
eventRepository.clearEvents()
|
||||
} else {
|
||||
val first = filteredEventList.first()
|
||||
if (Preferences.nextEventId != first.id && (
|
||||
//Preferences.showWeatherAsGlanceProvider || !Preferences.showNextEvent ||
|
||||
eventRepository.getEventById(first.id)?.startDate != first.startDate))
|
||||
eventRepository.saveNextEventData(first)
|
||||
eventRepository.saveEvents(filteredEventList)
|
||||
}
|
||||
} catch (ignored: java.lang.Exception) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eventRepository.resetNextEventData()
|
||||
eventRepository.clearEvents()
|
||||
}
|
||||
eventRepository.close()
|
||||
UpdatesReceiver.setUpdates(context)
|
||||
|
||||
MainWidget.updateWidget(context)
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
|
||||
if (Preferences.showEvents)
|
||||
enqueueTrigger(context)
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun enqueue(context: Context) {
|
||||
WorkManager.getInstance(context).enqueueUniqueWork(
|
||||
"updateEventList",
|
||||
ExistingWorkPolicy.KEEP,
|
||||
OneTimeWorkRequestBuilder<UpdateCalendarWorker>().build()
|
||||
)
|
||||
}
|
||||
|
||||
fun enqueueTrigger(context: Context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
WorkManager.getInstance(context).enqueueUniqueWork(
|
||||
"updateEventListTrigger",
|
||||
ExistingWorkPolicy.KEEP,
|
||||
OneTimeWorkRequestBuilder<Trigger>().setConstraints(
|
||||
Constraints.Builder().addContentUriTrigger(
|
||||
CalendarContract.CONTENT_URI,
|
||||
true
|
||||
).build()
|
||||
).build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelTrigger(context: Context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
WorkManager.getInstance(context).cancelUniqueWork(
|
||||
"updateEventListTrigger"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Trigger(context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||
override fun doWork(): Result {
|
||||
if (Preferences.showEvents && !isStopped)
|
||||
enqueue(applicationContext)
|
||||
return Result.success()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
package com.tommasoberlose.anotherwidget.services
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import com.google.android.gms.common.ConnectionResult
|
||||
import com.google.android.gms.common.GoogleApiAvailability
|
||||
import com.google.android.gms.location.LocationServices
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.coroutines.resume
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class WeatherWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
val context = applicationContext
|
||||
if (Preferences.customLocationAdd == "" &&
|
||||
context.checkGrantedPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||
) {
|
||||
if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context)
|
||||
== ConnectionResult.SUCCESS
|
||||
) {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
LocationServices.getFusedLocationProviderClient(context).lastLocation.addOnCompleteListener {
|
||||
continuation.resume(if (it.isSuccessful) it.result else null)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val lm = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
var location: Location? = null
|
||||
for (provider in lm.getProviders(true)) {
|
||||
lm.getLastKnownLocation(provider)?.let {
|
||||
if (location == null ||
|
||||
it.time - location!!.time > 2 * 60 * 1000 ||
|
||||
(it.time - location!!.time > -2 * 60 * 1000 && it.accuracy < location!!.accuracy))
|
||||
location = it
|
||||
}
|
||||
}
|
||||
location
|
||||
}?.let { location ->
|
||||
Preferences.customLocationLat = location.latitude.toString()
|
||||
Preferences.customLocationLon = location.longitude.toString()
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.IO) {
|
||||
WeatherNetworkApi(context).updateWeather()
|
||||
}
|
||||
|
||||
if (Preferences.showWeather)
|
||||
enqueueTrigger(context)
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun enqueue(context: Context, replace: Boolean = false) {
|
||||
WorkManager.getInstance(context).enqueueUniqueWork(
|
||||
"updateWeather",
|
||||
if (replace) ExistingWorkPolicy.REPLACE else ExistingWorkPolicy.KEEP,
|
||||
OneTimeWorkRequestBuilder<WeatherWorker>().build()
|
||||
)
|
||||
}
|
||||
|
||||
fun enqueueTrigger(context: Context) {
|
||||
val interval = when (Preferences.weatherRefreshPeriod) {
|
||||
0 -> 30
|
||||
1 -> 60
|
||||
2 -> 60L * 3
|
||||
3 -> 60L * 6
|
||||
4 -> 60L * 12
|
||||
5 -> 60L * 24
|
||||
else -> 60
|
||||
}
|
||||
WorkManager.getInstance(context).enqueueUniqueWork(
|
||||
"updateWeatherTrigger",
|
||||
ExistingWorkPolicy.REPLACE,
|
||||
OneTimeWorkRequestBuilder<Trigger>().setInitialDelay(
|
||||
interval, TimeUnit.MINUTES
|
||||
).build()
|
||||
)
|
||||
}
|
||||
|
||||
fun cancelTrigger(context: Context) {
|
||||
WorkManager.getInstance(context).cancelUniqueWork(
|
||||
"updateWeatherTrigger"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Trigger(context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||
override fun doWork(): Result {
|
||||
if (Preferences.showWeather && !isStopped)
|
||||
enqueue(applicationContext)
|
||||
return Result.success()
|
||||
}
|
||||
}
|
||||
}
|
@ -46,6 +46,7 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
overridePendingTransition(R.anim.nav_default_enter_anim, R.anim.nav_default_exit_anim)
|
||||
|
||||
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
@ -140,7 +141,6 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh
|
||||
|
||||
if (Preferences.showEvents && !checkGrantedPermission(Manifest.permission.READ_CALENDAR)) {
|
||||
Preferences.showEvents = false
|
||||
com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver.removeUpdates(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,28 @@
|
||||
package com.tommasoberlose.anotherwidget.ui.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.databinding.ActivityMainBinding
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
class SplashActivity: AppCompatActivity() {
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
lifecycleScope.launchWhenResumed {
|
||||
delay(1000)
|
||||
|
||||
if (!this@SplashActivity.isDestroyed) {
|
||||
startActivity(Intent(this@SplashActivity, MainActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -169,6 +169,7 @@ class CustomDateActivity : AppCompatActivity() {
|
||||
isDateCapitalize = viewModel.isDateCapitalize.value ?: true
|
||||
isDateUppercase = viewModel.isDateUppercase.value ?: false
|
||||
}
|
||||
com.tommasoberlose.anotherwidget.ui.widgets.MainWidget.updateWidget(this)
|
||||
super.onBackPressed()
|
||||
}
|
||||
|
||||
|
@ -57,9 +57,7 @@ class WeatherProviderActivity : AppCompatActivity() {
|
||||
updateListItem()
|
||||
binding.loader.isVisible = true
|
||||
|
||||
lifecycleScope.launch {
|
||||
WeatherHelper.updateWeather(this@WeatherProviderActivity)
|
||||
}
|
||||
WeatherHelper.updateWeather(this@WeatherProviderActivity, true)
|
||||
}
|
||||
.clicked(R.id.radioButton) {
|
||||
if (Preferences.weatherProvider != provider.rawValue) {
|
||||
@ -72,9 +70,7 @@ class WeatherProviderActivity : AppCompatActivity() {
|
||||
updateListItem()
|
||||
binding.loader.isVisible = true
|
||||
|
||||
lifecycleScope.launch {
|
||||
WeatherHelper.updateWeather(this@WeatherProviderActivity)
|
||||
}
|
||||
WeatherHelper.updateWeather(this@WeatherProviderActivity, true)
|
||||
}
|
||||
.checked(R.id.radioButton, provider.rawValue == Preferences.weatherProvider)
|
||||
.with<TextView>(R.id.text2) {
|
||||
@ -92,10 +88,8 @@ class WeatherProviderActivity : AppCompatActivity() {
|
||||
}
|
||||
.clicked(R.id.action_configure) {
|
||||
BottomSheetWeatherProviderSettings(this) {
|
||||
lifecycleScope.launch {
|
||||
binding.loader.isVisible = true
|
||||
WeatherHelper.updateWeather(this@WeatherProviderActivity)
|
||||
}
|
||||
binding.loader.isVisible = true
|
||||
WeatherHelper.updateWeather(this@WeatherProviderActivity, true)
|
||||
}.show()
|
||||
}
|
||||
.visibility(R.id.action_configure, if (/*WeatherHelper.isKeyRequired(provider) && */provider.rawValue == Preferences.weatherProvider) View.VISIBLE else View.GONE)
|
||||
@ -115,8 +109,6 @@ class WeatherProviderActivity : AppCompatActivity() {
|
||||
|
||||
adapter.updateData(
|
||||
Constants.WeatherProvider.values().asList()
|
||||
.filter { it != Constants.WeatherProvider.HERE }
|
||||
.filter { it != Constants.WeatherProvider.ACCUWEATHER }
|
||||
)
|
||||
|
||||
setupListener()
|
||||
@ -165,7 +157,7 @@ class WeatherProviderActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onMessageEvent(ignore: MainFragment.UpdateUiMessageEvent?) {
|
||||
fun onMessageEvent(@Suppress("UNUSED_PARAMETER") ignore: MainFragment.UpdateUiMessageEvent?) {
|
||||
binding.loader.isVisible = Preferences.weatherProviderError == "-"
|
||||
if (Preferences.weatherProviderError == "" && Preferences.weatherProviderLocationError == "") {
|
||||
Snackbar.make(binding.listView, getString(R.string.settings_weather_provider_api_key_subtitle_all_set), Snackbar.LENGTH_LONG).show()
|
||||
|
@ -12,6 +12,7 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.RemoteViews
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
@ -86,6 +87,7 @@ class MainFragment : Fragment() {
|
||||
binding.actionSettings.isClickable = !show
|
||||
binding.actionSettings.isFocusable = !show
|
||||
binding.fragmentTitle.text = if (show) destination.label.toString() else getString(R.string.app_name)
|
||||
binding.toolbar.cardElevation = 0f
|
||||
}
|
||||
|
||||
binding.actionSettings.setOnSingleClickListener {
|
||||
@ -138,7 +140,11 @@ class MainFragment : Fragment() {
|
||||
}
|
||||
|
||||
viewModel.fragmentScrollY.observe(viewLifecycleOwner) {
|
||||
binding.toolbar.cardElevation = if (it > 0) 24f else 0f
|
||||
binding.toolbar.cardElevation = if (it > 0) 32f else 0f
|
||||
}
|
||||
|
||||
viewModel.showPreview.observe(viewLifecycleOwner) {
|
||||
binding.preview.isVisible = it
|
||||
}
|
||||
|
||||
viewModel.widgetPreferencesUpdate.observe(viewLifecycleOwner) {
|
||||
@ -164,16 +170,13 @@ class MainFragment : Fragment() {
|
||||
val view: View = generatedView.apply(requireActivity().applicationContext, binding.widget)
|
||||
view.measure(0, 0)
|
||||
|
||||
binding.widgetLoader.animate().scaleX(1f).scaleY(1f).alpha(1f)
|
||||
.setDuration(200L).start()
|
||||
binding.widgetLoader.animate().alpha(0f).setDuration(200L).start()
|
||||
binding.widget.animate().alpha(0f).setDuration(200L).withEndAction {
|
||||
updatePreviewVisibility(view.measuredHeight)
|
||||
binding.widget.removeAllViews()
|
||||
binding.widget.addView(view)
|
||||
|
||||
updatePreviewVisibility(view.measuredHeight)
|
||||
binding.widgetLoader.animate().scaleX(0f).scaleY(0f).alpha(0f)
|
||||
.setDuration(200L).start()
|
||||
binding.widget.animate().alpha(1f).start()
|
||||
binding.widget.animate().setStartDelay(300L).alpha(1f).start()
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
@ -183,28 +186,30 @@ class MainFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun updatePreviewVisibility(widgetHeight: Int) {
|
||||
val newHeight = widgetHeight + 32f.convertDpToPixel(requireContext()).toInt()
|
||||
if (binding.preview.layoutParams.height != newHeight) {
|
||||
binding.preview.clearAnimation()
|
||||
ValueAnimator.ofInt(
|
||||
binding.preview.height,
|
||||
newHeight
|
||||
).apply {
|
||||
duration = 500L
|
||||
addUpdateListener {
|
||||
val animatedValue = animatedValue as Int
|
||||
val layoutParams = binding.preview.layoutParams
|
||||
layoutParams.height = animatedValue
|
||||
binding.preview.layoutParams = layoutParams
|
||||
}
|
||||
}.start()
|
||||
if (isAdded) {
|
||||
val newHeight = widgetHeight + 32f.convertDpToPixel(requireContext()).toInt()
|
||||
if (binding.preview.layoutParams.height != newHeight) {
|
||||
binding.preview.clearAnimation()
|
||||
ValueAnimator.ofInt(
|
||||
binding.preview.height,
|
||||
newHeight
|
||||
).apply {
|
||||
duration = 300L
|
||||
addUpdateListener {
|
||||
val animatedValue = animatedValue as Int
|
||||
val layoutParams = binding.preview.layoutParams
|
||||
layoutParams.height = animatedValue
|
||||
binding.preview.layoutParams = layoutParams
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
EventBus.getDefault().register(this)
|
||||
updateUI()
|
||||
// updateUI()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
@ -218,7 +223,7 @@ class MainFragment : Fragment() {
|
||||
class ChangeTabEvent(val page: Int)
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onUpdateUiEvent(ignore: UpdateUiMessageEvent?) {
|
||||
fun onUpdateUiEvent(@Suppress("UNUSED_PARAMETER") ignore: UpdateUiMessageEvent?) {
|
||||
delayJob?.cancel()
|
||||
delayJob = lifecycleScope.launch(Dispatchers.IO) {
|
||||
delay(300)
|
||||
@ -229,7 +234,7 @@ class MainFragment : Fragment() {
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onChangeTabEvent(ignore: ChangeTabEvent) {
|
||||
fun onChangeTabEvent(@Suppress("UNUSED_PARAMETER") ignore: ChangeTabEvent) {
|
||||
val navHost = childFragmentManager.findFragmentById(R.id.settings_fragment) as? NavHostFragment?
|
||||
navHost?.navController?.navigateUp()
|
||||
}
|
||||
|
@ -206,15 +206,13 @@ class SettingsFragment : Fragment() {
|
||||
.animate()
|
||||
.rotation((binding.actionRefreshIcon.rotation - binding.actionRefreshIcon.rotation % 360f) + 360f)
|
||||
.withEndAction {
|
||||
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
WeatherHelper.updateWeather(requireContext())
|
||||
CalendarHelper.updateEventList(requireContext())
|
||||
MediaPlayerHelper.updatePlayingMediaInfo(requireContext())
|
||||
ActiveNotificationsHelper.clearLastNotification(requireContext())
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
}
|
||||
try {
|
||||
WeatherHelper.updateWeather(requireContext())
|
||||
CalendarHelper.updateEventList(requireContext())
|
||||
MediaPlayerHelper.updatePlayingMediaInfo(requireContext())
|
||||
ActiveNotificationsHelper.clearLastNotification(requireContext())
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
}
|
||||
}
|
||||
.start()
|
||||
|
@ -144,6 +144,12 @@ class GesturesFragment : Fragment() {
|
||||
|
||||
binding.showMultipleEventsToggle.setOnCheckedChangeListener { _, isChecked ->
|
||||
Preferences.showNextEvent = isChecked
|
||||
if (!isChecked) {
|
||||
com.tommasoberlose.anotherwidget.db.EventRepository(requireContext()).run {
|
||||
resetNextEventData()
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.actionOpenEventDetails.setOnClickListener {
|
||||
|
@ -23,6 +23,7 @@ import com.tommasoberlose.anotherwidget.components.MaterialBottomSheetDialog
|
||||
import com.tommasoberlose.anotherwidget.databinding.FragmentPreferencesBinding
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
||||
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
|
||||
import com.tommasoberlose.anotherwidget.receivers.WeatherReceiver
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
|
||||
@ -88,6 +89,7 @@ class PreferencesFragment : Fragment() {
|
||||
CalendarHelper.setEventUpdatesAndroidN(requireContext())
|
||||
} else {
|
||||
CalendarHelper.removeEventUpdatesAndroidN(requireContext())
|
||||
UpdatesReceiver.removeUpdates(requireContext())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -124,7 +126,6 @@ class PreferencesFragment : Fragment() {
|
||||
requireCalendarPermission()
|
||||
} else {
|
||||
Preferences.showEvents = enabled
|
||||
UpdatesReceiver.removeUpdates(requireContext())
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,7 +138,7 @@ class PreferencesFragment : Fragment() {
|
||||
if (enabled) {
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
WeatherReceiver.setUpdates(requireContext())
|
||||
WeatherHelper.updateWeather(requireContext())
|
||||
} else {
|
||||
WeatherReceiver.removeUpdates(requireContext())
|
||||
}
|
||||
@ -165,6 +166,7 @@ class PreferencesFragment : Fragment() {
|
||||
.withPermissions(
|
||||
Manifest.permission.READ_CALENDAR
|
||||
).withListener(object: MultiplePermissionsListener {
|
||||
private var shouldShowRationale = false
|
||||
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
|
||||
report?.let {
|
||||
val granted = report.areAllPermissionsGranted()
|
||||
@ -172,12 +174,33 @@ class PreferencesFragment : Fragment() {
|
||||
if (granted) {
|
||||
CalendarHelper.updateEventList(requireContext())
|
||||
}
|
||||
else if (!shouldShowRationale && report.isAnyPermissionPermanentlyDenied) {
|
||||
MaterialBottomSheetDialog(
|
||||
requireContext(),
|
||||
getString(R.string.title_permission_calendar),
|
||||
getString(R.string.description_permission_calendar)
|
||||
).setNegativeButton(getString(R.string.action_ignore))
|
||||
.setPositiveButton(getString(R.string.action_grant_permission)) {
|
||||
startActivity(
|
||||
android.content.Intent(
|
||||
android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||
).apply {
|
||||
data = android.net.Uri.fromParts(
|
||||
"package",
|
||||
requireContext().packageName,
|
||||
null
|
||||
)
|
||||
}
|
||||
)
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun onPermissionRationaleShouldBeShown(
|
||||
permissions: MutableList<PermissionRequest>?,
|
||||
token: PermissionToken?
|
||||
) {
|
||||
shouldShowRationale = true
|
||||
// Remember to invoke this method when the custom rationale is closed
|
||||
// or just by default if you don't want to use any custom rationale.
|
||||
token?.continuePermissionRequest()
|
||||
|
@ -134,23 +134,13 @@ class WeatherFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun checkLocationPermission() {
|
||||
if (requireActivity().checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION) &&
|
||||
(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R ||
|
||||
requireActivity().checkGrantedPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION))
|
||||
) {
|
||||
if (requireActivity().checkGrantedPermission(Manifest.permission.ACCESS_COARSE_LOCATION)) {
|
||||
binding.locationPermissionAlert.isVisible = false
|
||||
} else if (Preferences.customLocationAdd == "") {
|
||||
binding.locationPermissionAlert.isVisible = true
|
||||
binding.locationPermissionAlert.setOnClickListener {
|
||||
requirePermission()
|
||||
}
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R &&
|
||||
requireActivity().checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
) {
|
||||
val text = getString(R.string.action_grant_permission) + " - " +
|
||||
requireContext().packageManager.backgroundPermissionOptionLabel
|
||||
binding.locationPermissionAlert.text = text
|
||||
}
|
||||
binding.weatherProviderLocationError.isVisible = false
|
||||
} else {
|
||||
binding.locationPermissionAlert.isVisible = false
|
||||
@ -187,9 +177,7 @@ class WeatherFragment : Fragment() {
|
||||
.addOnSelectItemListener { value ->
|
||||
if (value != Preferences.weatherTempUnit) {
|
||||
Preferences.weatherTempUnit = value
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
WeatherHelper.updateWeather(requireContext())
|
||||
}
|
||||
WeatherHelper.updateWeather(requireContext())
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
@ -218,9 +206,7 @@ class WeatherFragment : Fragment() {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
when (requestCode) {
|
||||
Constants.RESULT_CODE_CUSTOM_LOCATION -> {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
WeatherHelper.updateWeather(requireContext())
|
||||
}
|
||||
WeatherHelper.updateWeather(requireContext())
|
||||
}
|
||||
//RequestCode.WEATHER_PROVIDER_REQUEST_CODE.code -> {
|
||||
//}
|
||||
@ -232,19 +218,35 @@ class WeatherFragment : Fragment() {
|
||||
private fun requirePermission() {
|
||||
Dexter.withContext(requireContext())
|
||||
.withPermissions(
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R &&
|
||||
requireActivity().checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION))
|
||||
Manifest.permission.ACCESS_BACKGROUND_LOCATION
|
||||
else
|
||||
Manifest.permission.ACCESS_FINE_LOCATION
|
||||
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
).withListener(object: MultiplePermissionsListener {
|
||||
private var shouldShowRationale = false
|
||||
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
|
||||
report?.let {
|
||||
if (report.areAllPermissionsGranted()) {
|
||||
if (report.grantedPermissionResponses.isNotEmpty()) {
|
||||
checkLocationPermission()
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
WeatherHelper.updateWeather(requireContext())
|
||||
}
|
||||
WeatherHelper.updateWeather(requireContext())
|
||||
}
|
||||
else if (!shouldShowRationale && report.isAnyPermissionPermanentlyDenied) {
|
||||
MaterialBottomSheetDialog(
|
||||
requireContext(),
|
||||
getString(R.string.title_permission_location),
|
||||
getString(R.string.description_permission_location)
|
||||
).setNegativeButton(getString(R.string.action_ignore))
|
||||
.setPositiveButton(getString(R.string.action_grant_permission)) {
|
||||
startActivity(
|
||||
Intent(
|
||||
android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||
).apply {
|
||||
data = android.net.Uri.fromParts(
|
||||
"package",
|
||||
requireContext().packageName,
|
||||
null
|
||||
)
|
||||
}
|
||||
)
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -252,6 +254,7 @@ class WeatherFragment : Fragment() {
|
||||
permissions: MutableList<PermissionRequest>?,
|
||||
token: PermissionToken?
|
||||
) {
|
||||
shouldShowRationale = true
|
||||
// Remember to invoke this method when the custom rationale is closed
|
||||
// or just by default if you don't want to use any custom rationale.
|
||||
token?.continuePermissionRequest()
|
||||
@ -260,6 +263,12 @@ class WeatherFragment : Fragment() {
|
||||
.check()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
checkWeatherProviderConfig()
|
||||
checkLocationPermission()
|
||||
}
|
||||
|
||||
private fun maintainScrollPosition(callback: () -> Unit) {
|
||||
binding.scrollView.isScrollable = false
|
||||
callback.invoke()
|
||||
|
@ -135,7 +135,6 @@ class MainViewModel(context: Application) : AndroidViewModel(context) {
|
||||
addSource(Preferences.asLiveData(Preferences::customFontFile)) { value = true }
|
||||
addSource(Preferences.asLiveData(Preferences::customFontName)) { value = true }
|
||||
addSource(Preferences.asLiveData(Preferences::customFontVariant)) { value = true }
|
||||
addSource(Preferences.asLiveData(Preferences::secondRowInformation)) { value = true }
|
||||
addSource(Preferences.asLiveData(Preferences::widgetAlign)) { value = true }
|
||||
addSource(Preferences.asLiveData(Preferences::widgetMargin)) { value = true }
|
||||
addSource(Preferences.asLiveData(Preferences::widgetPadding)) { value = true }
|
||||
@ -156,7 +155,7 @@ class MainViewModel(context: Application) : AndroidViewModel(context) {
|
||||
addSource(Preferences.asLiveData(Preferences::secondRowInformation)) { value = true }
|
||||
|
||||
addSource(Preferences.asLiveData(Preferences::showWeather)) { value = true }
|
||||
addSource(Preferences.asLiveData(Preferences::weatherTempUnit)) { value = true }
|
||||
addSource(Preferences.asLiveData(Preferences::weatherProvider)) { value = true }
|
||||
addSource(Preferences.asLiveData(Preferences::weatherIconPack)) { value = true }
|
||||
addSource(Preferences.asLiveData(Preferences::customLocationLat)) { value = true }
|
||||
addSource(Preferences.asLiveData(Preferences::customLocationLon)) { value = true }
|
||||
|
@ -7,7 +7,6 @@ import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.text.format.DateUtils
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
@ -16,7 +15,6 @@ import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateMargins
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.databinding.LeftAlignedWidgetBinding
|
||||
import com.tommasoberlose.anotherwidget.db.EventRepository
|
||||
@ -26,12 +24,12 @@ import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.*
|
||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toIntValue
|
||||
import com.tommasoberlose.anotherwidget.helpers.ImageHelper.applyShadow
|
||||
import com.tommasoberlose.anotherwidget.receivers.CrashlyticsReceiver
|
||||
import com.tommasoberlose.anotherwidget.receivers.NewCalendarEventReceiver
|
||||
import com.tommasoberlose.anotherwidget.receivers.WidgetClickListenerReceiver
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import com.tommasoberlose.anotherwidget.utils.convertDpToPixel
|
||||
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
|
||||
import com.tommasoberlose.anotherwidget.utils.toPixel
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
@ -60,7 +58,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
context,
|
||||
appWidgetId,
|
||||
IntentHelper.getWidgetUpdateIntent(context),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.widget_shape_background, refreshIntent)
|
||||
|
||||
@ -69,7 +67,6 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
views.setViewPadding(R.id.main_layout, padding, padding, padding, padding)
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
CrashlyticsReceiver.sendCrash(context, ex)
|
||||
}
|
||||
|
||||
// Clock
|
||||
@ -87,7 +84,6 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
views = updateGridView(generatedBinding, views, appWidgetId)
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
CrashlyticsReceiver.sendCrash(context, ex)
|
||||
}
|
||||
|
||||
return views
|
||||
@ -107,7 +103,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
|
||||
val i = Intent(context, WidgetClickListenerReceiver::class.java)
|
||||
i.action = Actions.ACTION_OPEN_WEATHER_INTENT
|
||||
val weatherPIntent = PendingIntent.getBroadcast(context, widgetID, i, 0)
|
||||
val weatherPIntent = PendingIntent.getBroadcast(context, widgetID, i, PendingIntent.FLAG_IMMUTABLE)
|
||||
|
||||
views.setOnClickPendingIntent(R.id.weather_rect, weatherPIntent)
|
||||
views.setOnClickPendingIntent(R.id.weather_sub_line_rect, weatherPIntent)
|
||||
@ -137,7 +133,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getCalendarIntent(context),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.date_rect, calPIntent)
|
||||
views.setViewVisibility(R.id.first_line_rect, View.VISIBLE)
|
||||
@ -174,7 +170,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
context,
|
||||
NewCalendarEventReceiver::class.java
|
||||
).apply { action = Actions.ACTION_GO_TO_NEXT_EVENT },
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
)
|
||||
|
||||
@ -188,7 +184,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getEventIntent(context, nextEvent),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.next_event_rect, eventIntent)
|
||||
views.setImageViewBitmap(
|
||||
@ -225,7 +221,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getGoogleMapsIntentFromAddress(context, nextEvent.address),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.sub_line_rect, mapIntent)
|
||||
} else {
|
||||
@ -233,7 +229,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getCalendarIntent(context, nextEvent.startDate),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.sub_line_rect, pIntentDetail)
|
||||
}
|
||||
@ -253,7 +249,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getMusicIntent(context),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.sub_line_rect, musicIntent)
|
||||
showSomething = true
|
||||
@ -268,7 +264,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getClockIntent(context),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.sub_line_rect, alarmIntent)
|
||||
showSomething = true
|
||||
@ -284,7 +280,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getBatteryIntent(),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.sub_line_rect, batteryIntent)
|
||||
showSomething = true
|
||||
@ -304,7 +300,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getFitIntent(context),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.sub_line_rect, fitIntent)
|
||||
showSomething = true
|
||||
@ -325,7 +321,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getNotificationIntent(context),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(
|
||||
R.id.sub_line_rect,
|
||||
@ -343,7 +339,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
}
|
||||
}
|
||||
Constants.GlanceProviderId.EVENTS -> {
|
||||
if (Preferences.showEventsAsGlanceProvider&& Preferences.showEvents && context.checkGrantedPermission(
|
||||
if (Preferences.showEventsAsGlanceProvider && Preferences.showEvents && context.checkGrantedPermission(
|
||||
Manifest.permission.READ_CALENDAR) && nextEvent != null) {
|
||||
val pIntentDetail = IntentHelper.getPendingIntent(
|
||||
context,
|
||||
@ -353,7 +349,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
nextEvent,
|
||||
forceEventDetails = true
|
||||
),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(
|
||||
R.id.sub_line_rect,
|
||||
@ -367,7 +363,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
if (Preferences.showWeatherAsGlanceProvider && Preferences.showWeather && Preferences.weatherIcon != "") {
|
||||
val i = Intent(context, WidgetClickListenerReceiver::class.java)
|
||||
i.action = Actions.ACTION_OPEN_WEATHER_INTENT
|
||||
val weatherPIntent = PendingIntent.getBroadcast(context, widgetID, i, 0)
|
||||
val weatherPIntent = PendingIntent.getBroadcast(context, widgetID, i, PendingIntent.FLAG_IMMUTABLE)
|
||||
|
||||
views.setOnClickPendingIntent(
|
||||
R.id.sub_line_rect,
|
||||
@ -416,7 +412,6 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
)
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
CrashlyticsReceiver.sendCrash(context, ex)
|
||||
}
|
||||
|
||||
return views
|
||||
@ -501,8 +496,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
context,
|
||||
now.timeInMillis,
|
||||
nextEvent.startDate
|
||||
)
|
||||
.toLowerCase(Locale.getDefault())
|
||||
).toLowerCase(Locale.getDefault())
|
||||
} else {
|
||||
SettingsStringHelper.getAllDayEventDifferenceText(
|
||||
context,
|
||||
@ -582,11 +576,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
} else {
|
||||
val start = Calendar.getInstance().apply { timeInMillis = nextEvent.startDate }
|
||||
|
||||
bindingView.subLineText.text = if (now.get(Calendar.DAY_OF_YEAR) == start.get(
|
||||
Calendar.DAY_OF_YEAR)) {
|
||||
DateHelper.getDateText(context, start)
|
||||
} else if (now.get(Calendar.DAY_OF_YEAR) > start.get(Calendar.DAY_OF_YEAR) || now.get(
|
||||
Calendar.YEAR) > start.get(Calendar.YEAR)) {
|
||||
bindingView.subLineText.text = if (now.after(start)) {
|
||||
DateHelper.getDateText(context, now)
|
||||
} else {
|
||||
DateHelper.getDateText(context, start)
|
||||
@ -721,8 +711,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
context,
|
||||
now.timeInMillis,
|
||||
nextEvent.startDate
|
||||
)
|
||||
.toLowerCase(Locale.getDefault())
|
||||
).toLowerCase(Locale.getDefault())
|
||||
} else {
|
||||
SettingsStringHelper.getAllDayEventDifferenceText(
|
||||
context,
|
||||
@ -875,24 +864,24 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
val shadowRadius =
|
||||
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
|
||||
0 -> 0f
|
||||
1 -> 5f
|
||||
2 -> 5f
|
||||
else -> 5f
|
||||
}
|
||||
1 -> 2f
|
||||
2 -> 3f
|
||||
else -> 2f
|
||||
}.toPixel(context)
|
||||
val shadowColor =
|
||||
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
|
||||
0 -> Color.TRANSPARENT
|
||||
1 -> R.color.black_50
|
||||
1 -> Color.DKGRAY
|
||||
2 -> Color.BLACK
|
||||
else -> R.color.black_50
|
||||
else -> Color.DKGRAY
|
||||
}
|
||||
val shadowDy =
|
||||
val shadowOffset =
|
||||
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
|
||||
0 -> 0f
|
||||
1 -> 0f
|
||||
2 -> 1f
|
||||
2 -> 0.5f
|
||||
else -> 0f
|
||||
}
|
||||
}.toPixel(context)
|
||||
|
||||
listOf<TextView>(
|
||||
bindingView.date,
|
||||
@ -903,7 +892,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
bindingView.weatherSubLineDivider,
|
||||
bindingView.weatherSubLineTemperature,
|
||||
).forEach {
|
||||
it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor)
|
||||
it.setShadowLayer(shadowRadius, shadowOffset, shadowOffset, shadowColor)
|
||||
}
|
||||
|
||||
// Icons shadow
|
||||
@ -917,7 +906,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
it.second.isVisible = it.first.isVisible
|
||||
it.second.scaleX = it.first.scaleX
|
||||
it.second.scaleY = it.first.scaleY
|
||||
it.second.applyShadow(it.first, 0.7f)
|
||||
it.second.applyShadow(it.first, 0.8f)
|
||||
}
|
||||
}
|
||||
|
||||
@ -995,7 +984,6 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
return bindingView
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
CrashlyticsReceiver.sendCrash(context, ex)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package com.tommasoberlose.anotherwidget.ui.widgets
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.RemoteViews
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
@ -11,7 +10,6 @@ import com.tommasoberlose.anotherwidget.global.Constants
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.IntentHelper
|
||||
import com.tommasoberlose.anotherwidget.receivers.CrashlyticsReceiver
|
||||
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
|
||||
import com.tommasoberlose.anotherwidget.utils.toPixel
|
||||
|
||||
@ -43,7 +41,7 @@ class ClockWidget(val context: Context) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getClockIntent(context),
|
||||
0
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.time, clockPIntent)
|
||||
views.setOnClickPendingIntent(R.id.time_am_pm, clockPIntent)
|
||||
@ -111,7 +109,6 @@ class ClockWidget(val context: Context) {
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
CrashlyticsReceiver.sendCrash(context, ex)
|
||||
}
|
||||
|
||||
return views
|
||||
|
@ -32,27 +32,28 @@ class MainWidget : AppWidgetProvider() {
|
||||
|
||||
override fun onEnabled(context: Context) {
|
||||
CalendarHelper.updateEventList(context)
|
||||
WeatherReceiver.setUpdates(context)
|
||||
WeatherHelper.updateWeather(context)
|
||||
MediaPlayerHelper.updatePlayingMediaInfo(context)
|
||||
|
||||
if (Preferences.showEvents) {
|
||||
CalendarHelper.setEventUpdatesAndroidN(context)
|
||||
} else {
|
||||
CalendarHelper.removeEventUpdatesAndroidN(context)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDisabled(context: Context) {
|
||||
if (getWidgetCount(context) == 0) {
|
||||
CalendarHelper.removeEventUpdatesAndroidN(context)
|
||||
UpdatesReceiver.removeUpdates(context)
|
||||
WeatherReceiver.removeUpdates(context)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val handler by lazy { android.os.Handler(android.os.Looper.getMainLooper()) }
|
||||
|
||||
fun updateWidget(context: Context) {
|
||||
context.sendBroadcast(IntentHelper.getWidgetUpdateIntent(context))
|
||||
handler.run {
|
||||
removeCallbacksAndMessages(null)
|
||||
postDelayed ({
|
||||
context.sendBroadcast(IntentHelper.getWidgetUpdateIntent(context))
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
|
||||
fun getWidgetCount(context: Context): Int {
|
||||
|
@ -2,13 +2,11 @@ package com.tommasoberlose.anotherwidget.ui.widgets
|
||||
|
||||
import android.Manifest
|
||||
import android.app.PendingIntent
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.text.format.DateUtils
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
@ -28,7 +26,6 @@ import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.*
|
||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toIntValue
|
||||
import com.tommasoberlose.anotherwidget.helpers.ImageHelper.applyShadow
|
||||
import com.tommasoberlose.anotherwidget.receivers.CrashlyticsReceiver
|
||||
import com.tommasoberlose.anotherwidget.receivers.NewCalendarEventReceiver
|
||||
import com.tommasoberlose.anotherwidget.receivers.WidgetClickListenerReceiver
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
@ -63,7 +60,7 @@ class StandardWidget(val context: Context) {
|
||||
context,
|
||||
appWidgetId,
|
||||
IntentHelper.getWidgetUpdateIntent(context),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.widget_shape_background, refreshIntent)
|
||||
|
||||
@ -72,7 +69,6 @@ class StandardWidget(val context: Context) {
|
||||
views.setViewPadding(R.id.main_layout, padding, padding, padding, padding)
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
CrashlyticsReceiver.sendCrash(context, ex)
|
||||
}
|
||||
|
||||
// Clock
|
||||
@ -90,7 +86,6 @@ class StandardWidget(val context: Context) {
|
||||
views = updateGridView(generatedBinding, views, appWidgetId)
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
CrashlyticsReceiver.sendCrash(context, ex)
|
||||
}
|
||||
|
||||
return views
|
||||
@ -110,7 +105,7 @@ class StandardWidget(val context: Context) {
|
||||
|
||||
val i = Intent(context, WidgetClickListenerReceiver::class.java)
|
||||
i.action = Actions.ACTION_OPEN_WEATHER_INTENT
|
||||
val weatherPIntent = PendingIntent.getBroadcast(context, widgetID, i, 0)
|
||||
val weatherPIntent = PendingIntent.getBroadcast(context, widgetID, i, PendingIntent.FLAG_IMMUTABLE)
|
||||
|
||||
views.setOnClickPendingIntent(R.id.weather_rect, weatherPIntent)
|
||||
views.setOnClickPendingIntent(R.id.weather_sub_line_rect, weatherPIntent)
|
||||
@ -140,7 +135,7 @@ class StandardWidget(val context: Context) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getCalendarIntent(context),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.date_rect, calPIntent)
|
||||
views.setViewVisibility(R.id.first_line_rect, View.VISIBLE)
|
||||
@ -183,7 +178,7 @@ class StandardWidget(val context: Context) {
|
||||
context,
|
||||
NewCalendarEventReceiver::class.java
|
||||
).apply { action = Actions.ACTION_GO_TO_NEXT_EVENT },
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
)
|
||||
|
||||
@ -202,7 +197,7 @@ class StandardWidget(val context: Context) {
|
||||
context,
|
||||
NewCalendarEventReceiver::class.java
|
||||
).apply { action = Actions.ACTION_GO_TO_PREVIOUS_EVENT },
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
)
|
||||
|
||||
@ -218,7 +213,7 @@ class StandardWidget(val context: Context) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getEventIntent(context, nextEvent),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.next_event_rect, eventIntent)
|
||||
views.setViewVisibility(R.id.next_event_rect, View.VISIBLE)
|
||||
@ -251,7 +246,7 @@ class StandardWidget(val context: Context) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getGoogleMapsIntentFromAddress(context, nextEvent.address),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.sub_line_rect, mapIntent)
|
||||
} else {
|
||||
@ -259,7 +254,7 @@ class StandardWidget(val context: Context) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getCalendarIntent(context, nextEvent.startDate),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.sub_line_rect, pIntentDetail)
|
||||
}
|
||||
@ -285,7 +280,7 @@ class StandardWidget(val context: Context) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getMusicIntent(context),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.sub_line_rect, musicIntent)
|
||||
showSomething = true
|
||||
@ -300,7 +295,7 @@ class StandardWidget(val context: Context) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getClockIntent(context),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.sub_line_rect, alarmIntent)
|
||||
showSomething = true
|
||||
@ -316,7 +311,7 @@ class StandardWidget(val context: Context) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getBatteryIntent(),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.sub_line_rect, batteryIntent)
|
||||
showSomething = true
|
||||
@ -336,7 +331,7 @@ class StandardWidget(val context: Context) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getFitIntent(context),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.sub_line_rect, fitIntent)
|
||||
showSomething = true
|
||||
@ -357,7 +352,7 @@ class StandardWidget(val context: Context) {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getNotificationIntent(context),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(
|
||||
R.id.sub_line_rect,
|
||||
@ -375,7 +370,7 @@ class StandardWidget(val context: Context) {
|
||||
}
|
||||
}
|
||||
Constants.GlanceProviderId.EVENTS -> {
|
||||
if (Preferences.showEventsAsGlanceProvider&& Preferences.showEvents && context.checkGrantedPermission(
|
||||
if (Preferences.showEventsAsGlanceProvider && Preferences.showEvents && context.checkGrantedPermission(
|
||||
Manifest.permission.READ_CALENDAR) && nextEvent != null) {
|
||||
val pIntentDetail = IntentHelper.getPendingIntent(
|
||||
context,
|
||||
@ -385,7 +380,7 @@ class StandardWidget(val context: Context) {
|
||||
nextEvent,
|
||||
forceEventDetails = true
|
||||
),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
views.setOnClickPendingIntent(
|
||||
R.id.sub_line_rect,
|
||||
@ -399,7 +394,7 @@ class StandardWidget(val context: Context) {
|
||||
if (Preferences.showWeatherAsGlanceProvider && Preferences.showWeather && Preferences.weatherIcon != "") {
|
||||
val i = Intent(context, WidgetClickListenerReceiver::class.java)
|
||||
i.action = Actions.ACTION_OPEN_WEATHER_INTENT
|
||||
val weatherPIntent = PendingIntent.getBroadcast(context, widgetID, i, 0)
|
||||
val weatherPIntent = PendingIntent.getBroadcast(context, widgetID, i, PendingIntent.FLAG_IMMUTABLE)
|
||||
|
||||
views.setOnClickPendingIntent(
|
||||
R.id.sub_line_rect,
|
||||
@ -446,7 +441,6 @@ class StandardWidget(val context: Context) {
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
CrashlyticsReceiver.sendCrash(context, ex)
|
||||
}
|
||||
|
||||
return views
|
||||
@ -534,8 +528,7 @@ class StandardWidget(val context: Context) {
|
||||
context,
|
||||
now.timeInMillis,
|
||||
nextEvent.startDate
|
||||
)
|
||||
.toLowerCase(Locale.getDefault())
|
||||
).toLowerCase(Locale.getDefault())
|
||||
} else {
|
||||
SettingsStringHelper.getAllDayEventDifferenceText(
|
||||
context,
|
||||
@ -615,11 +608,7 @@ class StandardWidget(val context: Context) {
|
||||
} else {
|
||||
val start = Calendar.getInstance().apply { timeInMillis = nextEvent.startDate }
|
||||
|
||||
bindingView.subLineText.text = if (now.get(Calendar.DAY_OF_YEAR) == start.get(
|
||||
Calendar.DAY_OF_YEAR)) {
|
||||
DateHelper.getDateText(context, start)
|
||||
} else if (now.get(Calendar.DAY_OF_YEAR) > start.get(Calendar.DAY_OF_YEAR) || now.get(
|
||||
Calendar.YEAR) > start.get(Calendar.YEAR)) {
|
||||
bindingView.subLineText.text = if (now.after(start)) {
|
||||
DateHelper.getDateText(context, now)
|
||||
} else {
|
||||
DateHelper.getDateText(context, start)
|
||||
@ -754,8 +743,7 @@ class StandardWidget(val context: Context) {
|
||||
context,
|
||||
now.timeInMillis,
|
||||
nextEvent.startDate
|
||||
)
|
||||
.toLowerCase(Locale.getDefault())
|
||||
).toLowerCase(Locale.getDefault())
|
||||
} else {
|
||||
SettingsStringHelper.getAllDayEventDifferenceText(
|
||||
context,
|
||||
@ -912,24 +900,24 @@ class StandardWidget(val context: Context) {
|
||||
val shadowRadius =
|
||||
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
|
||||
0 -> 0f
|
||||
1 -> 5f
|
||||
2 -> 5f
|
||||
else -> 5f
|
||||
}
|
||||
1 -> 2f
|
||||
2 -> 3f
|
||||
else -> 2f
|
||||
}.toPixel(context)
|
||||
val shadowColor =
|
||||
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
|
||||
0 -> Color.TRANSPARENT
|
||||
1 -> R.color.black_50
|
||||
1 -> Color.DKGRAY
|
||||
2 -> Color.BLACK
|
||||
else -> R.color.black_50
|
||||
else -> Color.DKGRAY
|
||||
}
|
||||
val shadowDy =
|
||||
val shadowOffset =
|
||||
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
|
||||
0 -> 0f
|
||||
1 -> 0f
|
||||
2 -> 1f
|
||||
2 -> 0.5f
|
||||
else -> 0f
|
||||
}
|
||||
}.toPixel(context)
|
||||
|
||||
listOf<TextView>(
|
||||
bindingView.date,
|
||||
@ -941,7 +929,7 @@ class StandardWidget(val context: Context) {
|
||||
bindingView.weatherSubLineDivider,
|
||||
bindingView.weatherSubLineTemperature,
|
||||
).forEach {
|
||||
it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor)
|
||||
it.setShadowLayer(shadowRadius, shadowOffset, shadowOffset, shadowColor)
|
||||
}
|
||||
|
||||
// Icons shadow
|
||||
@ -955,7 +943,7 @@ class StandardWidget(val context: Context) {
|
||||
it.second.isVisible = it.first.isVisible
|
||||
it.second.scaleX = it.first.scaleX
|
||||
it.second.scaleY = it.first.scaleY
|
||||
it.second.applyShadow(it.first, 0.7f)
|
||||
it.second.applyShadow(it.first, 0.8f)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1027,7 +1015,6 @@ class StandardWidget(val context: Context) {
|
||||
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
CrashlyticsReceiver.sendCrash(context, ex)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
@ -214,6 +214,14 @@ fun Context.checkGrantedPermission(permission: String): Boolean {
|
||||
return ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
fun android.app.AlarmManager.setExactIfCanSchedule(type: Int, triggerAtMillis: Long, operation: android.app.PendingIntent) {
|
||||
// uncomment the following check after bumping compileSdkVersion/targetSdkVersion to 31
|
||||
//if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.S || canScheduleExactAlarms())
|
||||
setExact(type, triggerAtMillis, operation)
|
||||
//else
|
||||
// set(type, triggerAtMillis, operation)
|
||||
}
|
||||
|
||||
fun Context.getCurrentWallpaper(): Drawable? = try {
|
||||
WallpaperManager.getInstance(this).drawable
|
||||
} catch (e: Exception) {
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 32 KiB |
BIN
app/src/main/res/drawable-hdpi/round_upcoming_white_18.png
Normal file
After Width: | Height: | Size: 284 B |
BIN
app/src/main/res/drawable-hdpi/round_upcoming_white_20.png
Normal file
After Width: | Height: | Size: 302 B |
BIN
app/src/main/res/drawable-hdpi/round_upcoming_white_24.png
Normal file
After Width: | Height: | Size: 348 B |
BIN
app/src/main/res/drawable-hdpi/round_upcoming_white_36.png
Normal file
After Width: | Height: | Size: 483 B |
BIN
app/src/main/res/drawable-hdpi/round_upcoming_white_48.png
Normal file
After Width: | Height: | Size: 590 B |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 16 KiB |
BIN
app/src/main/res/drawable-mdpi/round_upcoming_white_18.png
Normal file
After Width: | Height: | Size: 194 B |
BIN
app/src/main/res/drawable-mdpi/round_upcoming_white_20.png
Normal file
After Width: | Height: | Size: 179 B |
BIN
app/src/main/res/drawable-mdpi/round_upcoming_white_24.png
Normal file
After Width: | Height: | Size: 220 B |
BIN
app/src/main/res/drawable-mdpi/round_upcoming_white_36.png
Normal file
After Width: | Height: | Size: 348 B |
BIN
app/src/main/res/drawable-mdpi/round_upcoming_white_48.png
Normal file
After Width: | Height: | Size: 389 B |
BIN
app/src/main/res/drawable-night-hdpi/round_upcoming_24.png
Normal file
After Width: | Height: | Size: 337 B |
BIN
app/src/main/res/drawable-night-mdpi/round_upcoming_24.png
Normal file
After Width: | Height: | Size: 221 B |
BIN
app/src/main/res/drawable-night-xhdpi/round_upcoming_24.png
Normal file
After Width: | Height: | Size: 377 B |
BIN
app/src/main/res/drawable-night-xxhdpi/round_upcoming_24.png
Normal file
After Width: | Height: | Size: 575 B |
BIN
app/src/main/res/drawable-night-xxxhdpi/round_upcoming_24.png
Normal file
After Width: | Height: | Size: 746 B |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 52 KiB |
BIN
app/src/main/res/drawable-xhdpi/round_upcoming_white_18.png
Normal file
After Width: | Height: | Size: 348 B |
BIN
app/src/main/res/drawable-xhdpi/round_upcoming_white_20.png
Normal file
After Width: | Height: | Size: 289 B |
BIN
app/src/main/res/drawable-xhdpi/round_upcoming_white_24.png
Normal file
After Width: | Height: | Size: 389 B |
BIN
app/src/main/res/drawable-xhdpi/round_upcoming_white_36.png
Normal file
After Width: | Height: | Size: 590 B |
BIN
app/src/main/res/drawable-xhdpi/round_upcoming_white_48.png
Normal file
After Width: | Height: | Size: 780 B |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 107 KiB |
BIN
app/src/main/res/drawable-xxhdpi/round_upcoming_white_18.png
Normal file
After Width: | Height: | Size: 483 B |
BIN
app/src/main/res/drawable-xxhdpi/round_upcoming_white_20.png
Normal file
After Width: | Height: | Size: 383 B |
BIN
app/src/main/res/drawable-xxhdpi/round_upcoming_white_24.png
Normal file
After Width: | Height: | Size: 590 B |
BIN
app/src/main/res/drawable-xxhdpi/round_upcoming_white_36.png
Normal file
After Width: | Height: | Size: 893 B |
BIN
app/src/main/res/drawable-xxhdpi/round_upcoming_white_48.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 178 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/round_upcoming_white_18.png
Normal file
After Width: | Height: | Size: 590 B |
BIN
app/src/main/res/drawable-xxxhdpi/round_upcoming_white_20.png
Normal file
After Width: | Height: | Size: 520 B |
BIN
app/src/main/res/drawable-xxxhdpi/round_upcoming_white_24.png
Normal file
After Width: | Height: | Size: 780 B |
BIN
app/src/main/res/drawable-xxxhdpi/round_upcoming_white_36.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/round_upcoming_white_48.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
@ -3,9 +3,9 @@
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners
|
||||
android:radius="9dp" />
|
||||
android:radius="20dp" />
|
||||
<solid
|
||||
android:color="@android:color/white"/>
|
||||
android:color="@color/colorPrimary"/>
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="oval">
|
||||
<stroke android:color="@android:color/white"
|
||||
android:width="2dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
19
app/src/main/res/drawable/round_upcoming_20.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M16.5,10h-3.04c-0.52,0 -1.02,0.27 -1.28,0.73C11.75,11.49 10.94,12 10,12s-1.75,-0.51 -2.18,-1.27C7.56,10.27 7.07,10 6.54,10H3.5C2.67,10 2,10.67 2,11.5v4C2,16.33 2.67,17 3.5,17h13c0.83,0 1.5,-0.67 1.5,-1.5v-4C18,10.67 17.33,10 16.5,10z"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M10,3L10,3C9.59,3 9.25,3.34 9.25,3.75v2.5C9.25,6.66 9.59,7 10,7h0c0.41,0 0.75,-0.34 0.75,-0.75v-2.5C10.75,3.34 10.41,3 10,3z"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M16.36,5.64L16.36,5.64c-0.29,-0.29 -0.77,-0.29 -1.06,0L13.54,7.4c-0.29,0.29 -0.29,0.77 0,1.06l0,0c0.29,0.29 0.77,0.29 1.06,0l1.77,-1.77C16.66,6.4 16.66,5.93 16.36,5.64z"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M6.46,7.4L4.7,5.64c-0.29,-0.29 -0.77,-0.29 -1.06,0l0,0c-0.29,0.29 -0.29,0.77 0,1.06L5.4,8.46c0.29,0.29 0.77,0.29 1.06,0l0,0C6.76,8.17 6.76,7.7 6.46,7.4z"/>
|
||||
</vector>
|
19
app/src/main/res/drawable/round_upcoming_24.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20.45,6.55L20.45,6.55c-0.38,-0.38 -1.01,-0.38 -1.39,0L16.89,8.7c-0.39,0.38 -0.39,1.01 0,1.39l0.01,0.01c0.39,0.39 1.01,0.39 1.4,0c0.62,-0.63 1.52,-1.54 2.15,-2.17C20.83,7.55 20.83,6.93 20.45,6.55z"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12.02,3h-0.03C11.44,3 11,3.44 11,3.98v3.03C11,7.56 11.44,8 11.98,8h0.03C12.56,8 13,7.56 13,7.02V3.98C13,3.44 12.56,3 12.02,3z"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M7.1,10.11l0.01,-0.01c0.38,-0.38 0.38,-1.01 0,-1.39L4.96,6.54c-0.38,-0.39 -1.01,-0.39 -1.39,0L3.55,6.55c-0.39,0.39 -0.39,1.01 0,1.39c0.63,0.62 1.53,1.54 2.15,2.17C6.09,10.49 6.72,10.49 7.1,10.11z"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,15c-1.24,0 -2.31,-0.75 -2.76,-1.83C8.92,12.43 8.14,12 7.34,12L4,12c-1.1,0 -2,0.9 -2,2l0,5c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2v-5c0,-1.1 -0.9,-2 -2,-2l-3.34,0c-0.8,0 -1.58,0.43 -1.9,1.17C14.31,14.25 13.24,15 12,15"/>
|
||||
</vector>
|
@ -511,7 +511,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="24dp"
|
||||
android:paddingRight="24dp"
|
||||
android:paddingBottom="32dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:duplicateParentState="true"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/colorSecondaryText"
|
||||
|
@ -16,7 +16,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="32dp"
|
||||
android:paddingBottom="48dp"
|
||||
android:orientation="vertical">
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="match_parent"
|
||||
|
@ -76,7 +76,6 @@
|
||||
android:id="@+id/footer"
|
||||
android:padding="8dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:orientation="horizontal">
|
||||
<ImageView
|
||||
android:layout_width="48dp"
|
||||
|
@ -1,4 +1,6 @@
|
||||
<RelativeLayout 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:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipChildren="false"
|
||||
@ -138,6 +140,8 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:clipChildren="false"
|
||||
android:gravity="center_vertical"
|
||||
android:id="@+id/sub_line"
|
||||
android:visibility="gone"
|
||||
@ -145,12 +149,12 @@
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:cropToPadding="false"
|
||||
android:clipToPadding="false"
|
||||
android:clipChildren="false">
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:cropToPadding="false"
|
||||
android:clipToPadding="false"
|
||||
android:clipChildren="false"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:id="@+id/sub_line_icon_shadow"
|
||||
@ -158,7 +162,7 @@
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:cropToPadding="false"
|
||||
android:clipToPadding="false"
|
||||
android:clipChildren="false"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:id="@+id/sub_line_icon"
|
||||
@ -179,6 +183,8 @@
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:clipChildren="false"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:layout_marginStart="2dp"
|
||||
|
@ -157,12 +157,16 @@
|
||||
android:layout_gravity="center"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:layoutDirection="locale"
|
||||
android:gravity="center">
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:clipChildren="false"
|
||||
android:gravity="center_vertical"
|
||||
android:id="@+id/sub_line"
|
||||
android:visibility="gone"
|
||||
@ -170,12 +174,12 @@
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:cropToPadding="false"
|
||||
android:clipToPadding="false"
|
||||
android:clipChildren="false">
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:cropToPadding="false"
|
||||
android:clipToPadding="false"
|
||||
android:clipChildren="false"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:id="@+id/sub_line_icon_shadow"
|
||||
@ -183,7 +187,7 @@
|
||||
<ImageView
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:cropToPadding="false"
|
||||
android:clipToPadding="false"
|
||||
android:clipChildren="false"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:id="@+id/sub_line_icon"
|
||||
@ -203,6 +207,8 @@
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:clipChildren="false"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:layout_marginStart="2dp"
|
||||
|
@ -66,12 +66,12 @@
|
||||
<!-- Calendar -->
|
||||
<string name="settings_calendar_title">日历</string>
|
||||
<string name="title_permission_calendar">显示你的活动</string>
|
||||
<string name="description_permission_calendar">授予应用查看日历的权限以在微件中查看你的活动。</string>
|
||||
<string name="description_permission_calendar">授予访问日历的权限\n以在您的微件上查看活动。</string>
|
||||
<string name="settings_filter_calendar_title">过滤活动</string>
|
||||
<string name="settings_filter_calendar_subtitle">改变日历可见性</string>
|
||||
<string name="settings_all_day_title">全天活动</string>
|
||||
<string name="main_calendar">日历账户</string>
|
||||
<string name="calendar_settings_list_error">加载日历列表出现错误。</string>
|
||||
<string name="main_calendar">日历</string>
|
||||
<string name="calendar_settings_list_error">加载日历列表出错。</string>
|
||||
<string name="all_day">全天活动</string>
|
||||
<string name="show_events_visible">活动可见</string>
|
||||
<string name="show_events_not_visible">活动不可见</string>
|
||||
@ -100,7 +100,7 @@
|
||||
<string name="settings_show_multiple_events_title">多活动切换器</string>
|
||||
<string name="soon">即将</string>
|
||||
<string name="now">即刻</string>
|
||||
<string name="settings_widget_update_frequency_title">更新剩余时间的频率</string>
|
||||
<string name="settings_widget_update_frequency_title">剩余时间更新频率</string>
|
||||
<string name="settings_widget_update_frequency_subtitle">更新频率越高耗电量越大。</string>
|
||||
<string name="settings_widget_update_frequency_low">低</string>
|
||||
<string name="settings_widget_update_frequency_default">默认</string>
|
||||
@ -123,8 +123,8 @@
|
||||
|
||||
<!-- Weather -->
|
||||
<string name="settings_weather_title">天气</string>
|
||||
<string name="title_permission_location">显示天气信息</string>
|
||||
<string name="description_permission_location">授予位置权限\n以在您的微件上查看天气。</string>
|
||||
<string name="title_permission_location">显示天气</string>
|
||||
<string name="description_permission_location">授予定位权限\n以在您的微件上查看天气。</string>
|
||||
<string name="settings_unit_title">单位</string>
|
||||
<string name="fahrenheit" translatable="false">°F - 华氏度</string>
|
||||
<string name="celsius" translatable="false">°C - 摄氏度</string>
|
||||
@ -141,15 +141,15 @@
|
||||
<string name="show_weather_not_visible">天气信息不可见</string>
|
||||
<string name="settings_weather_app_title">天气</string>
|
||||
<string name="settings_weather_provider_api_key_title">天气API密钥</string>
|
||||
<string name="settings_weather_provider_api_key_subtitle_all_set">已正确配置天气信息来源。</string>
|
||||
<string name="settings_weather_provider_api_key_subtitle_not_set">必须配置天气信息来源。</string>
|
||||
<string name="settings_weather_provider_api_key_subtitle_all_set">已正确配置天气数据源。</string>
|
||||
<string name="settings_weather_provider_api_key_subtitle_not_set">必须配置天气数据源。</string>
|
||||
<string name="api_key_hint">OpenWeatherMap API密钥</string>
|
||||
<string name="default_weather_app">Google Weather</string>
|
||||
<string name="weather_warning">Google Awareness API已被弃用。要在微件上显示天气,您需要从第三方来源获取API密钥。</string>
|
||||
<string name="settings_weather_icon_pack_title">天气图标</string>
|
||||
<string name="default_weather_app">Google天气</string>
|
||||
<string name="weather_warning">Google Awareness API已被弃用。要在微件上显示天气,您需要从第三方数据源获取API密钥。</string>
|
||||
<string name="settings_weather_icon_pack_title">图标包</string>
|
||||
<string name="settings_weather_icon_pack_default">图标包%d</string>
|
||||
<string name="background_location_warning">选择地理定位后,即便应用未启动或在后台,我们也会收集您的定位数据来更新天气信息。\n这些数据不会用在其他地方。</string>
|
||||
<string name="settings_weather_provider_api">天气信息来源</string>
|
||||
<string name="settings_weather_provider_api">天气数据源</string>
|
||||
|
||||
<string name="settings_weather_provider_open_weather" translatable="false">OpenWeatherMap.org</string>
|
||||
<string name="settings_weather_provider_weatherbit" translatable="false">Weatherbit.io</string>
|
||||
@ -159,35 +159,35 @@
|
||||
<string name="settings_weather_provider_weather_gov" translatable="false">Weather.gov (USA)\nby National Weather Services</string>
|
||||
<string name="settings_weather_provider_yr" translatable="false">YR.no/Met.no\nby Meteorologisk Institutt</string>
|
||||
|
||||
<string name="weather_provider_info_open_weather_title">该天气信息来源需要API密钥才能正常工作。</string>
|
||||
<string name="weather_provider_info_weatherbit_title">该天气信息来源需要API密钥才能正常工作。</string>
|
||||
<string name="weather_provider_info_weatherapi_title">该天气信息来源需要API密钥才能正常工作。</string>
|
||||
<string name="weather_provider_info_here_title">该天气信息来源需要API密钥才能正常工作。</string>
|
||||
<string name="weather_provider_info_accuweather_title">该天气信息来源需要API密钥才能正常工作。</string>
|
||||
<string name="weather_provider_info_weather_gov_title">该天气信息来源仅在美国境内有效。</string>
|
||||
<string name="weather_provider_info_yr_title">该天气信息来源仅支持摄氏度单位。</string>
|
||||
<string name="weather_provider_info_open_weather_title">该天气数据源需要API密钥才能正常工作。</string>
|
||||
<string name="weather_provider_info_weatherbit_title">该天气数据源需要API密钥才能正常工作。</string>
|
||||
<string name="weather_provider_info_weatherapi_title">该天气数据源需要API密钥才能正常工作。</string>
|
||||
<string name="weather_provider_info_here_title">该天气数据源需要API密钥才能正常工作。</string>
|
||||
<string name="weather_provider_info_accuweather_title">该天气数据源需要API密钥才能正常工作。</string>
|
||||
<string name="weather_provider_info_weather_gov_title">该天气数据源仅对美国境内有效。</string>
|
||||
<string name="weather_provider_info_yr_title">该天气数据源仅支持摄氏度单位。</string>
|
||||
|
||||
<string name="weather_provider_info_open_weather_subtitle">前往天气信息来源网站、创建个人账户并将默认密钥复制粘贴到此处。</string>
|
||||
<string name="weather_provider_info_weatherbit_subtitle">前往天气信息来源网站、创建个人账户并将默认密钥复制粘贴到此处。</string>
|
||||
<string name="weather_provider_info_weatherapi_subtitle">前往天气信息来源网站、创建个人账户并将默认密钥复制粘贴到此处。</string>
|
||||
<string name="weather_provider_info_here_subtitle">前往天气信息来源网站、创建个人账户并将默认密钥复制粘贴到此处。</string>
|
||||
<string name="weather_provider_info_accuweather_subtitle">前往天气信息来源网站、创建个人账户并将默认密钥复制粘贴到此处。</string>
|
||||
<string name="weather_provider_info_open_weather_subtitle">前往天气数据源网站、创建个人账户并将默认密钥复制粘贴到此处。</string>
|
||||
<string name="weather_provider_info_weatherbit_subtitle">前往天气数据源网站、创建个人账户并将默认密钥复制粘贴到此处。</string>
|
||||
<string name="weather_provider_info_weatherapi_subtitle">前往天气数据源网站、创建个人账户并将默认密钥复制粘贴到此处。</string>
|
||||
<string name="weather_provider_info_here_subtitle">前往天气数据源网站、创建个人账户并将默认密钥复制粘贴到此处。</string>
|
||||
<string name="weather_provider_info_accuweather_subtitle">前往天气数据源网站、创建个人账户并将默认密钥复制粘贴到此处。</string>
|
||||
<string name="weather_provider_info_weather_gov_subtitle">\n</string>
|
||||
<string name="weather_provider_info_yr_subtitle">\n</string>
|
||||
|
||||
<string name="weather_provider_error_missing_key">您选择的天气信息来源需要API密钥。</string>
|
||||
<string name="weather_provider_error_missing_key">您选择的天气数据源需要API密钥。</string>
|
||||
<string name="weather_provider_error_wrong_location">不支持当前位置。</string>
|
||||
<string name="weather_provider_error_missing_location">请选择一个有效的地址。</string>
|
||||
<string name="weather_provider_error_missing_location">请选择一个有效的位置。</string>
|
||||
<string name="weather_provider_error_expired_key">您的API密钥似乎已经过期了。</string>
|
||||
<string name="weather_provider_error_invalid_key">API密钥无效或尚未激活。</string>
|
||||
<string name="weather_provider_error_misconfigured">天气信息来源配置错误。</string>
|
||||
<string name="weather_provider_error_misconfigured">天气数据源配置错误。</string>
|
||||
<string name="weather_provider_error_connection">网络连接错误。</string>
|
||||
<string name="weather_provider_error_generic">出错了,请检查天气信息来源设置。</string>
|
||||
<string name="weather_provider_error_generic">出错了,请检查天气数据源设置。</string>
|
||||
<string name="api_key_required_message">需要账户</string>
|
||||
<string name="us_only_message">仅限美国</string>
|
||||
<string name="celsius_only_message">仅公制单位</string>
|
||||
<string name="weather_select_a_provider">选择信息来源</string>
|
||||
<string name="weather_provider_activity_subtitle">从列表中选择天气信息来源。\n部分来源需要注册免费个人账户,\n但它们通常也更精准。</string>
|
||||
<string name="weather_select_a_provider">选择数据源</string>
|
||||
<string name="weather_provider_activity_subtitle">从列表中选择天气数据源。\n部分数据源需要注册免费个人账户,\n但它们通常也更精准。</string>
|
||||
|
||||
<string name="location_access_notification_channel_id" translatable="false">location-access</string>
|
||||
<string name="location_access_notification_channel_name">天气更新服务</string>
|
||||
@ -241,8 +241,8 @@
|
||||
<string name="title_show_glance">显示速览信息</string>
|
||||
<string name="description_show_glance_visible">服务已启用</string>
|
||||
<string name="description_show_glance_not_visible">服务已禁用</string>
|
||||
<string name="settings_sort_glance_providers_title">来源优先级</string>
|
||||
<string name="settings_sort_glance_providers_subtitle">您可以通过拖放图标来调整下方列表的项目顺序,以此更改数据来源优先级。</string>
|
||||
<string name="settings_sort_glance_providers_title">数据源优先级</string>
|
||||
<string name="settings_sort_glance_providers_subtitle">您可以通过拖放图标来调整下方列表的项目顺序,以此更改数据源优先级。</string>
|
||||
<string name="settings_custom_notes_title">自定义提示</string>
|
||||
<string name="settings_daily_steps_title">运动健康</string>
|
||||
<string name="daily_steps_counter">目前走了%d步</string>
|
||||
@ -250,7 +250,7 @@
|
||||
<string name="battery_low_warning">电量低</string>
|
||||
<string name="charging">正在充电</string>
|
||||
<string name="charged">完全充满</string>
|
||||
<string name="providers">来源</string>
|
||||
<string name="providers">数据源</string>
|
||||
<string name="glance_info">仅在当前没有活动且满足特定条件时才会显示速览。</string>
|
||||
<string name="settings_music_players_filter_title">音乐播放器</string>
|
||||
<string name="settings_music_players_filter_subtitle">选择您常用的音乐播放器</string>
|
||||
@ -304,7 +304,7 @@
|
||||
</string-array>
|
||||
<string name="settings_show_events_as_glance_provider_title">活动</string>
|
||||
<string name="settings_show_events_as_glance_provider_subtitle">查看您的日历活动的速览,并始终显示当前日期。</string>
|
||||
<string name="settings_show_events_as_glance_provider_error">请在日历标签中启用活动显示,并授予所需的权限。</string>
|
||||
<string name="settings_show_events_as_glance_provider_error">请启用日历显示,并授予所需的权限。</string>
|
||||
<string name="events_glance_provider_format">%1$s %2$s</string>
|
||||
<string name="settings_show_weather_as_glance_provider_title">天气</string>
|
||||
<string name="settings_show_weather_as_glance_provider_subtitle">查看有关天气的更多信息,并始终显示当前日期。</string>
|
||||
@ -318,9 +318,9 @@
|
||||
<string name="action_about">关于</string>
|
||||
<string name="action_refresh_widget">刷新微件</string>
|
||||
<string name="toolbar_transition_name" translatable="false">toolbar</string>
|
||||
<string name="error_opening_uri">打开URL时遭遇出错:链接已复制到剪贴板。</string>
|
||||
<string name="error_opening_uri">打开URL出错:链接已复制到剪贴板。</string>
|
||||
<string name="loading_text">正在加载数据……</string>
|
||||
<string name="error_opening_app">打开应用时出错。</string>
|
||||
<string name="error_opening_app">打开应用出错。</string>
|
||||
<string name="default_name">默认应用</string>
|
||||
<string name="action_save">保存</string>
|
||||
<string name="settings_visible">可见</string>
|
||||
@ -344,17 +344,17 @@
|
||||
<string name="settings_feedback_subtitle">欢迎为该项目贡献您的力量!</string>
|
||||
<string name="settings_feedback_title">反馈和功能请求</string>
|
||||
<string name="xiaomi_manufacturer" translatable="false">xiaomi</string>
|
||||
<string name="xiaomi_warning_title">检测到小米设备</string>
|
||||
<string name="xiaomi_warning_message">请在应用设置的「其他权限」中授予允许程序在后台运行时显示弹出窗口的权限,否则您将无法通过点击微件打开任何应用。</string>
|
||||
<string name="xiaomi_warning_title">小米设备</string>
|
||||
<string name="xiaomi_warning_message">请在应用设置的其他权限部分授予后台弹出界面的权限,否则您将无法通过点击微件打开任何应用。</string>
|
||||
<string name="action_ignore">忽略</string>
|
||||
<string name="action_grant_permission">授予权限</string>
|
||||
<string name="settings_title">设置</string>
|
||||
<string name="style_header">风格</string>
|
||||
<string name="actions_header">动作</string>
|
||||
<string name="provider_header">来源</string>
|
||||
<string name="provider_header">数据源</string>
|
||||
<string name="appearance_header">外观</string>
|
||||
<string name="preferences_header">偏好</string>
|
||||
<string name="settings_privacy_policy_title"><![CDATA[法务与隐私]]></string>
|
||||
<string name="settings_privacy_policy_title">法务与隐私</string>
|
||||
<string name="settings_privacy_policy_subtitle">查看应用的隐私策略</string>
|
||||
<string name="project_header">开源项目</string>
|
||||
<string name="information_header">信息</string>
|
||||
@ -369,8 +369,8 @@
|
||||
<string name="donation_breakfast">一顿英式早餐</string>
|
||||
<string name="donation_lunch">一顿快餐</string>
|
||||
<string name="action_show_widget_preview">显示微件预览</string>
|
||||
<string name="support_dev_subtitle">这是一个独立开发者的项目,谢谢您的支持!</string>
|
||||
<string name="settings_title_integrations">整合</string>
|
||||
<string name="support_dev_subtitle">这是一个独立开发者的项目,\n谢谢您的支持!</string>
|
||||
<string name="settings_title_integrations">集成</string>
|
||||
<string name="label_count_installed_integrations">已安装%d个集成插件</string>
|
||||
<string name="nothing">无</string>
|
||||
<string name="action_dismiss">拒绝</string>
|
||||
@ -380,13 +380,13 @@
|
||||
<string name="song_info_format_activity_subtitle">更改曲目的可见信息</string>
|
||||
|
||||
<!-- More -->
|
||||
<string name="look_and_feel_header"><![CDATA[外观与视觉]]></string>
|
||||
<string name="look_and_feel_header">界面外观</string>
|
||||
<string name="typography_settings_title">文本</string>
|
||||
<string name="typography_settings_subtitle">设置文本字体、大小和颜色</string>
|
||||
<string name="layout_settings_title">布局</string>
|
||||
<string name="gestures_settings_subtitle">动作、行为与快捷方式</string>
|
||||
<string name="gestures_settings_title">交互</string>
|
||||
<string name="gestures_amp_input_header"><![CDATA[操作与交互]]></string>
|
||||
<string name="gestures_settings_title">手势</string>
|
||||
<string name="gestures_amp_input_header">手势与输入</string>
|
||||
<string name="clock_settings_subtitle">设置大小、颜色与时区</string>
|
||||
<string name="layout_settings_subtitle">微件间距及其它微调</string>
|
||||
<string name="smart_content_header">智能内容</string>
|
||||
|
@ -19,6 +19,7 @@
|
||||
<color name="colorPrimaryDark">#F8F8F8</color>
|
||||
<color name="colorAccent">#0092ca</color>
|
||||
<color name="colorAccent_op10">#1A1089FF</color>
|
||||
<color name="colorAccent_op20">#331089FF</color>
|
||||
<color name="colorAccent_op30">#4D1089FF</color>
|
||||
<color name="colorTitle">#1089FF</color>
|
||||
<color name="black_op10">#1A000000</color>
|
||||
|
10
build.gradle
@ -7,17 +7,14 @@ buildscript {
|
||||
jcenter()
|
||||
maven { url 'https://jitpack.io' }
|
||||
mavenCentral()
|
||||
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.2.0'
|
||||
classpath 'com.android.tools.build:gradle:7.0.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'com.google.gms:google-services:4.3.5'
|
||||
classpath 'com.google.gms:google-services:4.3.8'
|
||||
|
||||
// Add the Crashlytics Gradle plugin.
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.5.2'
|
||||
|
||||
classpath 'io.realm:realm-gradle-plugin:10.4.0'
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.6.1'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
@ -30,7 +27,6 @@ allprojects {
|
||||
jcenter()
|
||||
maven { url 'https://jitpack.io' }
|
||||
mavenCentral()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
|
||||
|