Compare commits
10 Commits
5dcf0afe02
...
f25ce937ce
Author | SHA1 | Date | |
---|---|---|---|
f25ce937ce | |||
564962dc9c | |||
|
f893748941 | ||
|
64ff404eac | ||
|
b95a9e8e7f | ||
|
1667d9c22c | ||
|
fb3f28d035 | ||
|
d8e204c5d9 | ||
|
388653f62b | ||
|
5763a18421 |
2
.idea/gradle.xml
generated
2
.idea/gradle.xml
generated
@ -7,13 +7,13 @@
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="Embedded JDK" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveModulePerSourceSet" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
|
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -61,7 +61,7 @@
|
||||
</profile-state>
|
||||
</entry>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
3
.idea/modules.xml
generated
3
.idea/modules.xml
generated
@ -4,6 +4,9 @@
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/Another_Widget.iml" filepath="$PROJECT_DIR$/.idea/modules/Another_Widget.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/Another_Widget.app.iml" filepath="$PROJECT_DIR$/.idea/modules/app/Another_Widget.app.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/Another_Widget.app.androidTest.iml" filepath="$PROJECT_DIR$/.idea/modules/app/Another_Widget.app.androidTest.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/Another_Widget.app.main.iml" filepath="$PROJECT_DIR$/.idea/modules/app/Another_Widget.app.main.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/Another_Widget.app.unitTest.iml" filepath="$PROJECT_DIR$/.idea/modules/app/Another_Widget.app.unitTest.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -1,16 +1,8 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
|
||||
// Apply the Crashlytics Gradle plugin
|
||||
apply plugin: 'com.google.firebase.crashlytics'
|
||||
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
def apikeyPropertiesFile = rootProject.file("apikey.properties")
|
||||
def apikeyProperties = new Properties()
|
||||
apikeyProperties.load(new FileInputStream(apikeyPropertiesFile))
|
||||
|
||||
android {
|
||||
|
||||
compileSdkVersion 30
|
||||
@ -23,7 +15,6 @@ android {
|
||||
versionName "2.3.3"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
buildConfigField("String", "GOOGLE_API_KEY", apikeyProperties['GOOGLE_API_KEY'])
|
||||
|
||||
renderscriptSupportModeEnabled true
|
||||
}
|
||||
@ -103,17 +94,8 @@ dependencies {
|
||||
implementation 'com.github.bumptech.glide:glide:4.12.0'
|
||||
kapt 'com.github.bumptech.glide:compiler:4.11.0'
|
||||
|
||||
// Fitness
|
||||
implementation 'com.google.android.gms:play-services-fitness:20.0.0'
|
||||
implementation 'com.google.android.gms:play-services-auth:19.0.0'
|
||||
|
||||
//Weather
|
||||
implementation 'com.github.KwabenBerko:OpenWeatherMap-Android-Library:2.0.2'
|
||||
implementation 'com.google.android.gms:play-services-location:18.0.0'
|
||||
|
||||
// Billing
|
||||
implementation 'com.android.billingclient:billing:3.0.3'
|
||||
implementation 'com.android.billingclient:billing-ktx:3.0.3'
|
||||
|
||||
// KTX
|
||||
implementation "androidx.core:core-ktx:1.5.0"
|
||||
@ -131,9 +113,6 @@ dependencies {
|
||||
//Coroutines
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
|
||||
|
||||
// Add the Firebase SDK for Crashlytics.
|
||||
implementation 'com.google.firebase:firebase-crashlytics:18.0.0'
|
||||
|
||||
// Preferences
|
||||
implementation 'com.chibatching.kotpref:kotpref:2.13.1'
|
||||
implementation 'com.chibatching.kotpref:livedata-support:2.13.1'
|
||||
|
@ -6,14 +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_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"
|
||||
@ -25,7 +26,7 @@
|
||||
android:usesCleartextTraffic="true"
|
||||
android:theme="@style/AppTheme"
|
||||
tools:ignore="LockedOrientationActivity">
|
||||
<activity android:name=".ui.activities.SplashActivity" 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" />
|
||||
@ -36,7 +37,6 @@
|
||||
<activity android:name=".ui.activities.tabs.CustomFontActivity" android:screenOrientation="portrait" />
|
||||
<activity android:name=".ui.activities.tabs.CustomLocationActivity" android:screenOrientation="portrait" />
|
||||
<activity android:name=".ui.activities.tabs.WeatherProviderActivity" android:screenOrientation="portrait" />
|
||||
<activity android:name=".ui.activities.settings.SupportDevActivity" android:screenOrientation="portrait" />
|
||||
<activity android:name=".ui.activities.tabs.CustomDateActivity" android:screenOrientation="portrait" />
|
||||
<activity android:name=".ui.activities.settings.IntegrationsActivity" android:screenOrientation="portrait" />
|
||||
<activity android:name=".ui.activities.tabs.MusicPlayersFilterActivity" android:screenOrientation="portrait" />
|
||||
@ -44,11 +44,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" />
|
||||
@ -108,11 +107,7 @@
|
||||
</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" />
|
||||
@ -130,27 +125,6 @@
|
||||
<action android:name="android.intent.action.BATTERY_CHANGED"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".receivers.ActivityDetectionReceiver"
|
||||
android:exported="true"
|
||||
android:permission="com.google.android.gms.permission.ACTIVITY_RECOGNITION">
|
||||
<intent-filter>
|
||||
<action android:name="com.mypackage.ACTION_PROCESS_ACTIVITY_TRANSITIONS" />
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name=".services.UpdateCalendarService"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:foregroundServiceType="dataSync" />
|
||||
<service
|
||||
android:name=".services.LocationService"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:foregroundServiceType="location" />
|
||||
</application>
|
||||
|
||||
<queries>
|
||||
|
@ -1,20 +1,14 @@
|
||||
package com.tommasoberlose.anotherwidget
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import com.chibatching.kotpref.Kotpref
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
|
||||
class AWApplication : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
// Firebase crashlitycs
|
||||
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(!BuildConfig.DEBUG)
|
||||
|
||||
// Preferences
|
||||
Kotpref.init(this)
|
||||
|
||||
|
@ -6,18 +6,9 @@ import android.app.AlarmManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.view.LayoutInflater
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.karumi.dexter.Dexter
|
||||
import com.karumi.dexter.MultiplePermissionsReport
|
||||
import com.karumi.dexter.PermissionToken
|
||||
import com.karumi.dexter.listener.PermissionRequest
|
||||
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.databinding.GlanceProviderSettingsLayoutBinding
|
||||
import com.tommasoberlose.anotherwidget.global.Constants
|
||||
@ -26,7 +17,6 @@ import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.AlarmHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.GreetingsHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
|
||||
import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.tabs.AppNotificationsFilterActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.tabs.MediaInfoFormatActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.tabs.MusicPlayersFilterActivity
|
||||
@ -90,14 +80,6 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
|
||||
checkNextAlarm()
|
||||
}
|
||||
|
||||
/* GOOGLE STEPS */
|
||||
binding.actionToggleGoogleFit.isVisible = provider == Constants.GlanceProviderId.GOOGLE_FIT_STEPS
|
||||
if (provider == Constants.GlanceProviderId.GOOGLE_FIT_STEPS) {
|
||||
binding.warningContainer.isVisible = false
|
||||
checkFitnessPermission()
|
||||
checkGoogleFitConnection()
|
||||
}
|
||||
|
||||
/* BATTERY INFO */
|
||||
if (provider == Constants.GlanceProviderId.BATTERY_LEVEL_LOW) {
|
||||
binding.warningContainer.isVisible = false
|
||||
@ -177,6 +159,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,39 +168,21 @@ 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
|
||||
GreetingsHelper.toggleGreetings(context)
|
||||
}
|
||||
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
|
||||
if (isChecked) {
|
||||
val account: GoogleSignInAccount? =
|
||||
GoogleSignIn.getLastSignedInAccount(context)
|
||||
if (!GoogleSignIn.hasPermissions(account,
|
||||
ActivityDetectionReceiver.FITNESS_OPTIONS
|
||||
)
|
||||
) {
|
||||
val mGoogleSignInClient =
|
||||
GoogleSignIn.getClient(context, GoogleSignInOptions.Builder(
|
||||
GoogleSignInOptions.DEFAULT_SIGN_IN).addExtension(
|
||||
ActivityDetectionReceiver.FITNESS_OPTIONS
|
||||
).build())
|
||||
context.startActivityForResult(mGoogleSignInClient.signInIntent,
|
||||
2)
|
||||
} else {
|
||||
Preferences.showDailySteps = true
|
||||
}
|
||||
} else {
|
||||
Preferences.showDailySteps = false
|
||||
}
|
||||
|
||||
binding.warningContainer.isVisible = false
|
||||
checkFitnessPermission()
|
||||
checkGoogleFitConnection()
|
||||
}
|
||||
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 +204,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) {
|
||||
@ -325,89 +289,4 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
|
||||
}
|
||||
statusCallback?.invoke()
|
||||
}
|
||||
|
||||
private fun checkFitnessPermission() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || context.checkGrantedPermission(
|
||||
Manifest.permission.ACTIVITY_RECOGNITION)
|
||||
) {
|
||||
if (Preferences.showDailySteps) {
|
||||
ActivityDetectionReceiver.registerFence(context)
|
||||
} else {
|
||||
ActivityDetectionReceiver.unregisterFence(context)
|
||||
}
|
||||
} else if (Preferences.showDailySteps) {
|
||||
ActivityDetectionReceiver.unregisterFence(context)
|
||||
binding.warningContainer.isVisible = true
|
||||
binding.warningTitle.text = context.getString(R.string.settings_request_fitness_access)
|
||||
binding.warningContainer.setOnClickListener {
|
||||
requireFitnessPermission()
|
||||
}
|
||||
} else {
|
||||
ActivityDetectionReceiver.unregisterFence(context)
|
||||
}
|
||||
statusCallback?.invoke()
|
||||
}
|
||||
|
||||
private fun checkGoogleFitConnection() {
|
||||
val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(context)
|
||||
if (!GoogleSignIn.hasPermissions(account,
|
||||
ActivityDetectionReceiver.FITNESS_OPTIONS
|
||||
)) {
|
||||
binding.warningContainer.isVisible = true
|
||||
binding.warningTitle.text = context.getString(R.string.settings_request_fitness_access)
|
||||
binding.warningContainer.setOnClickListener {
|
||||
GoogleSignIn.requestPermissions(
|
||||
context,
|
||||
1,
|
||||
account,
|
||||
ActivityDetectionReceiver.FITNESS_OPTIONS)
|
||||
}
|
||||
binding.actionConnectToGoogleFit.isVisible = true
|
||||
binding.actionDisconnectToGoogleFit.isVisible = false
|
||||
binding.actionConnectToGoogleFit.setOnClickListener {
|
||||
GoogleSignIn.requestPermissions(
|
||||
context,
|
||||
1,
|
||||
account,
|
||||
ActivityDetectionReceiver.FITNESS_OPTIONS)
|
||||
}
|
||||
binding.actionDisconnectToGoogleFit.setOnClickListener(null)
|
||||
binding.googleFitStatusLabel.text = context.getString(R.string.google_fit_account_not_connected)
|
||||
} else {
|
||||
binding.actionConnectToGoogleFit.isVisible = false
|
||||
binding.actionDisconnectToGoogleFit.isVisible = true
|
||||
binding.actionConnectToGoogleFit.setOnClickListener(null)
|
||||
binding.actionDisconnectToGoogleFit.setOnClickListener {
|
||||
GoogleSignIn.getClient(context, GoogleSignInOptions.Builder(
|
||||
GoogleSignInOptions.DEFAULT_SIGN_IN).addExtension(
|
||||
ActivityDetectionReceiver.FITNESS_OPTIONS
|
||||
).build()).signOut().addOnCompleteListener {
|
||||
show()
|
||||
}
|
||||
}
|
||||
binding.googleFitStatusLabel.text = context.getString(R.string.google_fit_account_connected)
|
||||
}
|
||||
}
|
||||
|
||||
private fun requireFitnessPermission() {
|
||||
Dexter.withContext(context)
|
||||
.withPermissions(
|
||||
"com.google.android.gms.permission.ACTIVITY_RECOGNITION",
|
||||
"android.gms.permission.ACTIVITY_RECOGNITION",
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACTIVITY_RECOGNITION else "com.google.android.gms.permission.ACTIVITY_RECOGNITION"
|
||||
).withListener(object: MultiplePermissionsListener {
|
||||
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
|
||||
checkFitnessPermission()
|
||||
}
|
||||
override fun onPermissionRationaleShouldBeShown(
|
||||
permissions: MutableList<PermissionRequest>?,
|
||||
token: PermissionToken?
|
||||
) {
|
||||
// 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()
|
||||
}
|
||||
})
|
||||
.check()
|
||||
}
|
||||
}
|
@ -27,7 +27,7 @@ class EventRepository(val context: Context) {
|
||||
db.runInTransaction{
|
||||
db.dao().run {
|
||||
deleteAll()
|
||||
insertAll(eventList)
|
||||
insert(eventList)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -39,12 +39,6 @@ class EventRepository(val context: Context) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +69,7 @@ class EventRepository(val context: Context) {
|
||||
val events = getEvents()
|
||||
if (events.isNotEmpty()) {
|
||||
val newNextEvent = events.first()
|
||||
Preferences.nextEventId = newNextEvent.id
|
||||
saveNextEventData(newNextEvent)
|
||||
newNextEvent
|
||||
} else {
|
||||
resetNextEventData()
|
||||
@ -93,9 +87,9 @@ class EventRepository(val context: Context) {
|
||||
if (eventList.isNotEmpty()) {
|
||||
val index = eventList.indexOfFirst { it.id == Preferences.nextEventId }
|
||||
if (index > -1 && index < eventList.size - 1) {
|
||||
Preferences.nextEventId = eventList[index + 1].id
|
||||
saveNextEventData(eventList[index + 1])
|
||||
} else {
|
||||
Preferences.nextEventId = eventList.first().id
|
||||
saveNextEventData(eventList.first())
|
||||
}
|
||||
} else {
|
||||
resetNextEventData()
|
||||
@ -111,9 +105,9 @@ class EventRepository(val context: Context) {
|
||||
if (eventList.isNotEmpty()) {
|
||||
val index = eventList.indexOfFirst { it.id == Preferences.nextEventId }
|
||||
if (index > 0) {
|
||||
Preferences.nextEventId = eventList[index - 1].id
|
||||
saveNextEventData(eventList[index - 1])
|
||||
} else {
|
||||
Preferences.nextEventId = eventList.last().id
|
||||
saveNextEventData(eventList.last())
|
||||
}
|
||||
} else {
|
||||
resetNextEventData()
|
||||
@ -125,7 +119,7 @@ class EventRepository(val context: Context) {
|
||||
}
|
||||
|
||||
fun getFutureEvents(): List<Event> {
|
||||
return db.dao().findFuture(Calendar.getInstance().timeInMillis).applyFilters().sortEvents()
|
||||
return db.dao().find(Calendar.getInstance().timeInMillis).sortEvents()
|
||||
}
|
||||
|
||||
private fun getEvents(): List<Event> {
|
||||
@ -144,7 +138,7 @@ class EventRepository(val context: Context) {
|
||||
else -> add(Calendar.HOUR, 6)
|
||||
}
|
||||
}
|
||||
return db.dao().find(now, limit.timeInMillis).applyFilters().sortEvents()
|
||||
return db.dao().find(now, limit.timeInMillis).sortEvents()
|
||||
}
|
||||
|
||||
fun getEventsCount(): Int = getEvents().size
|
||||
@ -156,16 +150,16 @@ class EventRepository(val context: Context) {
|
||||
@Dao
|
||||
interface EventDao {
|
||||
@Query("SELECT * FROM events WHERE id = :id LIMIT 1")
|
||||
fun findById(id: Long) : Event?
|
||||
fun findById(id: Long): Event?
|
||||
|
||||
@Query("SELECT * FROM events WHERE end_date > :from")
|
||||
fun findFuture(from: Long) : List<Event>
|
||||
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>
|
||||
@Query("SELECT * FROM events WHERE end_date > :from AND start_date <= :to")
|
||||
fun find(from: Long, to: Long): List<Event>
|
||||
|
||||
@Insert
|
||||
fun insertAll(events: List<Event>)
|
||||
fun insert(events: List<Event>)
|
||||
|
||||
@Query("DELETE FROM events")
|
||||
fun deleteAll()
|
||||
|
@ -10,7 +10,6 @@ object Actions {
|
||||
const val ACTION_OPEN_WEATHER_INTENT = "com.tommasoberlose.anotherwidget.action.OPEN_WEATHER_INTENT"
|
||||
const val ACTION_GO_TO_NEXT_EVENT = "com.tommasoberlose.anotherwidget.action.GO_TO_NEXT_EVENT"
|
||||
const val ACTION_GO_TO_PREVIOUS_EVENT = "com.tommasoberlose.anotherwidget.action.GO_TO_PREVIOUS_EVENT"
|
||||
const val ACTION_REPORT_CRASH = "com.tommasoberlose.anotherwidget.action.REPORT_CRASH"
|
||||
const val ACTION_CLEAR_NOTIFICATION = "com.tommasoberlose.anotherwidget.action.CLEAR_NOTIFICATION"
|
||||
const val ACTION_UPDATE_GREETINGS = "com.tommasoberlose.anotherwidget.action.UPDATE_GREETINGS"
|
||||
|
||||
|
@ -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 = "")
|
||||
|
@ -25,6 +25,7 @@ object ActiveNotificationsHelper {
|
||||
remove(Preferences::lastNotificationIcon)
|
||||
}
|
||||
MainWidget.updateWidget(context)
|
||||
NotificationListener.clearTimeout(context)
|
||||
}
|
||||
|
||||
fun checkNotificationAccess(context: Context): Boolean {
|
||||
|
@ -5,10 +5,9 @@ import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.text.format.DateFormat
|
||||
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 +43,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 +61,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,11 +4,9 @@ import android.content.Context
|
||||
import android.graphics.*
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.drawable.DrawableCompat
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
|
||||
|
||||
object BitmapHelper {
|
||||
@ -39,15 +37,6 @@ object BitmapHelper {
|
||||
1
|
||||
}
|
||||
|
||||
if (draw) {
|
||||
FirebaseCrashlytics.getInstance().setCustomKey("WIDTH SPEC", measuredWidth)
|
||||
FirebaseCrashlytics.getInstance().setCustomKey("HEIGHT SPEC", measuredHeight)
|
||||
FirebaseCrashlytics.getInstance().setCustomKey("VIEW measuredWidth", view.measuredWidth)
|
||||
FirebaseCrashlytics.getInstance().setCustomKey("VIEW measuredHeight", view.measuredHeight)
|
||||
FirebaseCrashlytics.getInstance().setCustomKey("WIDGET final width", widgetWidth)
|
||||
FirebaseCrashlytics.getInstance().setCustomKey("WIDGET final height", widgetHeight)
|
||||
}
|
||||
|
||||
return try {
|
||||
val btm = Bitmap.createBitmap(
|
||||
widgetWidth,
|
||||
@ -64,7 +53,6 @@ object BitmapHelper {
|
||||
}
|
||||
btm
|
||||
} catch (ex: Exception) {
|
||||
FirebaseCrashlytics.getInstance().recordException(ex)
|
||||
Bitmap.createBitmap(5, 5, Bitmap.Config.ALPHA_8)
|
||||
}
|
||||
}
|
||||
|
@ -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 ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,15 +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)) {
|
||||
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()
|
||||
@ -336,7 +331,9 @@ object WeatherHelper {
|
||||
}
|
||||
}
|
||||
|
||||
fun getWeatherGovIcon(iconString: String, isDaytime: Boolean): String = when (iconString.substringBefore('?').substringAfterLast('/')) {
|
||||
fun getWeatherGovIcon(iconString: String, isDaytime: Boolean): String = when (
|
||||
iconString.substringBefore('?').substringAfterLast('/').substringBefore(',')
|
||||
) {
|
||||
"skc" -> "01"
|
||||
"few" -> "02"
|
||||
"sct" -> "02"
|
||||
|
@ -6,17 +6,10 @@ import android.content.res.Configuration.ORIENTATION_PORTRAIT
|
||||
import android.graphics.Typeface
|
||||
import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import androidx.core.provider.FontRequest
|
||||
import androidx.core.provider.FontsContractCompat
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.db.EventRepository
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import com.tommasoberlose.anotherwidget.utils.toPixel
|
||||
import kotlin.math.min
|
||||
|
||||
object WidgetHelper {
|
||||
class WidgetSizeProvider(
|
||||
@ -36,8 +29,6 @@ object WidgetHelper {
|
||||
)
|
||||
val widthInPx = context.dip(width)
|
||||
val heightInPx = context.dip(height)
|
||||
FirebaseCrashlytics.getInstance().setCustomKey("widthInPx", widthInPx)
|
||||
FirebaseCrashlytics.getInstance().setCustomKey("heightInPx", heightInPx)
|
||||
return widthInPx to heightInPx
|
||||
}
|
||||
|
||||
|
@ -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 = ""
|
||||
|
@ -1,181 +0,0 @@
|
||||
package com.tommasoberlose.anotherwidget.receivers
|
||||
|
||||
import android.Manifest
|
||||
import android.app.AlarmManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import com.chibatching.kotpref.Kotpref
|
||||
import com.chibatching.kotpref.blockingBulk
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
|
||||
import com.google.android.gms.fitness.Fitness
|
||||
import com.google.android.gms.fitness.FitnessOptions
|
||||
import com.google.android.gms.fitness.data.DataType
|
||||
import com.google.android.gms.fitness.data.Field.FIELD_STEPS
|
||||
import com.google.android.gms.fitness.request.DataReadRequest
|
||||
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 java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
class ActivityDetectionReceiver : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (ActivityTransitionResult.hasResult(intent)) {
|
||||
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) {
|
||||
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)) {
|
||||
resetDailySteps(context)
|
||||
registerFence(context)
|
||||
} else {
|
||||
resetDailySteps(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
.addDataType(DataType.AGGREGATE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
|
||||
.build()
|
||||
|
||||
fun registerFence(context: Context) {
|
||||
Kotpref.init(context)
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || context.checkGrantedPermission(
|
||||
Manifest.permission.ACTIVITY_RECOGNITION)) {
|
||||
val transitions = mutableListOf<ActivityTransition>()
|
||||
|
||||
transitions +=
|
||||
ActivityTransition.Builder()
|
||||
.setActivityType(DetectedActivity.WALKING)
|
||||
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
|
||||
.build()
|
||||
|
||||
transitions +=
|
||||
ActivityTransition.Builder()
|
||||
.setActivityType(DetectedActivity.RUNNING)
|
||||
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
|
||||
.build()
|
||||
|
||||
val request = ActivityTransitionRequest(transitions)
|
||||
|
||||
// myPendingIntent is the instance of PendingIntent where the app receives callbacks.
|
||||
val task = ActivityRecognition.getClient(context)
|
||||
.requestActivityTransitionUpdates(
|
||||
request,
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
2,
|
||||
Intent(context, ActivityDetectionReceiver::class.java),
|
||||
0
|
||||
)
|
||||
)
|
||||
|
||||
task.addOnFailureListener { e: Exception ->
|
||||
e.printStackTrace()
|
||||
Preferences.showDailySteps = false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun unregisterFence(context: Context) {
|
||||
val task = ActivityRecognition.getClient(context)
|
||||
.removeActivityTransitionUpdates(
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
2,
|
||||
Intent(context, ActivityDetectionReceiver::class.java),
|
||||
0
|
||||
)
|
||||
)
|
||||
|
||||
task.addOnCompleteListener {
|
||||
if (it.isSuccessful) {
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
2,
|
||||
Intent(context, ActivityDetectionReceiver::class.java),
|
||||
0
|
||||
).cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun requestDailySteps(context: Context) {
|
||||
Kotpref.init(context)
|
||||
|
||||
val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(context)
|
||||
if (account != null && GoogleSignIn.hasPermissions(account, FITNESS_OPTIONS)) {
|
||||
|
||||
val cal: Calendar = Calendar.getInstance()
|
||||
cal.set(Calendar.HOUR_OF_DAY, 0)
|
||||
cal.set(Calendar.MINUTE, 0)
|
||||
cal.set(Calendar.SECOND, 0)
|
||||
cal.set(Calendar.MILLISECOND, 0)
|
||||
val startTime: Long = cal.timeInMillis
|
||||
|
||||
cal.add(Calendar.DAY_OF_YEAR, 1)
|
||||
val endTime: Long = cal.timeInMillis
|
||||
|
||||
val readRequest = DataReadRequest.Builder()
|
||||
.aggregate(DataType.TYPE_STEP_COUNT_DELTA)
|
||||
.aggregate(DataType.AGGREGATE_STEP_COUNT_DELTA)
|
||||
.setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
|
||||
.bucketByTime(1, TimeUnit.DAYS)
|
||||
.build()
|
||||
|
||||
Fitness.getHistoryClient(context, account)
|
||||
.readData(readRequest)
|
||||
.addOnSuccessListener { response ->
|
||||
Preferences.googleFitSteps = response.buckets.sumBy {
|
||||
try {
|
||||
it.getDataSet(DataType.AGGREGATE_STEP_COUNT_DELTA)?.dataPoints?.get(
|
||||
0
|
||||
)?.getValue(FIELD_STEPS)?.asInt() ?: 0
|
||||
} catch (ex: Exception) {
|
||||
0
|
||||
}
|
||||
}.toLong()
|
||||
MainWidget.updateWidget(context)
|
||||
setTimeout(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
AlarmManager.RTC,
|
||||
Calendar.getInstance().timeInMillis + 5 * 60 * 1000,
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
5,
|
||||
Intent(context, ActivityDetectionReceiver::class.java),
|
||||
0
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -161,7 +163,7 @@ 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(
|
||||
@ -172,7 +174,7 @@ class UpdatesReceiver : BroadcastReceiver() {
|
||||
if (event.startDate > now.timeInMillis)
|
||||
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.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 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 (ActivityCompat.checkSelfPermission(
|
||||
this@LocationService,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
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,
|
||||
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,93 @@
|
||||
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.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
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)
|
||||
) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
@ -141,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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,96 +0,0 @@
|
||||
package com.tommasoberlose.anotherwidget.ui.activities.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.android.billingclient.api.*
|
||||
import com.android.billingclient.api.BillingClient.BillingResponseCode.OK
|
||||
import com.android.billingclient.api.BillingClient.BillingResponseCode.USER_CANCELED
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.databinding.ActivitySupportDevBinding
|
||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.settings.SupportDevViewModel
|
||||
import com.tommasoberlose.anotherwidget.utils.toast
|
||||
import net.idik.lib.slimadapter.SlimAdapter
|
||||
|
||||
class SupportDevActivity : AppCompatActivity(), PurchasesUpdatedListener {
|
||||
|
||||
private lateinit var viewModel: SupportDevViewModel
|
||||
private lateinit var adapter: SlimAdapter
|
||||
private lateinit var binding: ActivitySupportDevBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
viewModel = ViewModelProvider(this).get(SupportDevViewModel::class.java)
|
||||
viewModel.billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build()
|
||||
binding = ActivitySupportDevBinding.inflate(layoutInflater)
|
||||
|
||||
|
||||
binding.listView.setHasFixedSize(true)
|
||||
val mLayoutManager = LinearLayoutManager(this)
|
||||
binding.listView.layoutManager = mLayoutManager
|
||||
|
||||
adapter = SlimAdapter.create()
|
||||
adapter
|
||||
.register<SkuDetails>(R.layout.inapp_product_layout) { item, injector ->
|
||||
item.sku
|
||||
injector
|
||||
.with<TextView>(R.id.product_title) {
|
||||
it.text = when (item.sku) {
|
||||
"donation_coffee" -> getString(R.string.donation_coffee)
|
||||
"donation_donuts" -> getString(R.string.donation_donuts)
|
||||
"donation_breakfast" -> getString(R.string.donation_breakfast)
|
||||
"donation_lunch" -> getString(R.string.donation_lunch)
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
.text(R.id.product_price, item.price)
|
||||
.clicked(R.id.item) {
|
||||
viewModel.purchase(this, item)
|
||||
}
|
||||
}
|
||||
.attachTo(binding.listView)
|
||||
|
||||
viewModel.openConnection()
|
||||
subscribeUi(viewModel)
|
||||
|
||||
binding.actionBack.setOnClickListener {
|
||||
onBackPressed()
|
||||
}
|
||||
|
||||
setContentView(binding.root)
|
||||
}
|
||||
|
||||
private fun subscribeUi(viewModel: SupportDevViewModel) {
|
||||
viewModel.products.observe(this, Observer {
|
||||
if (it.isNotEmpty()) {
|
||||
binding.loader.isVisible = false
|
||||
}
|
||||
adapter.updateData(it.sortedWith(compareBy(SkuDetails::getPriceAmountMicros)))
|
||||
})
|
||||
}
|
||||
|
||||
override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
|
||||
if (billingResult.responseCode == OK && purchases != null) {
|
||||
for (purchase in purchases) {
|
||||
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
|
||||
viewModel.handlePurchase(purchase)
|
||||
toast(getString(R.string.thanks))
|
||||
}
|
||||
}
|
||||
} else if (billingResult.responseCode == USER_CANCELED) {
|
||||
// DO nothing
|
||||
} else {
|
||||
toast(getString(R.string.error))
|
||||
}
|
||||
}
|
||||
|
||||
public override fun onDestroy() {
|
||||
viewModel.closeConnection()
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -8,13 +8,10 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.Navigation
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import com.karumi.dexter.Dexter
|
||||
import com.karumi.dexter.MultiplePermissionsReport
|
||||
@ -32,13 +29,10 @@ import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.settings.IntegrationsActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.settings.SupportDevActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import com.tommasoberlose.anotherwidget.utils.ignoreExceptions
|
||||
import com.tommasoberlose.anotherwidget.utils.openURI
|
||||
import com.tommasoberlose.anotherwidget.utils.setOnSingleClickListener
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@ -197,24 +191,18 @@ class SettingsFragment : Fragment() {
|
||||
requireActivity().openURI("https://github.com/tommasoberlose/another-widget/blob/master/privacy-policy.md")
|
||||
}
|
||||
|
||||
binding.actionHelpDev.setOnClickListener {
|
||||
startActivity(Intent(requireContext(), SupportDevActivity::class.java))
|
||||
}
|
||||
|
||||
binding.actionRefreshWidget.setOnClickListener {
|
||||
binding.actionRefreshIcon
|
||||
.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 {
|
||||
|
@ -8,26 +8,20 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.graphics.Canvas
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.AnimationUtils
|
||||
import android.view.animation.LayoutAnimationController
|
||||
import android.widget.ImageView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
|
||||
import com.google.android.gms.common.api.ApiException
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
@ -41,8 +35,6 @@ import com.tommasoberlose.anotherwidget.helpers.AlarmHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.GlanceProviderHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
|
||||
import com.tommasoberlose.anotherwidget.models.GlanceProvider
|
||||
import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver
|
||||
import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver.Companion.FITNESS_OPTIONS
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
|
||||
import com.tommasoberlose.anotherwidget.utils.*
|
||||
@ -238,40 +230,6 @@ class GlanceTabFragment : Fragment() {
|
||||
injector.visibility(R.id.info_icon, View.VISIBLE)
|
||||
isVisible = Preferences.customNotes != ""
|
||||
}
|
||||
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
|
||||
val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(
|
||||
context
|
||||
)
|
||||
if (GoogleSignIn.hasPermissions(
|
||||
account,
|
||||
FITNESS_OPTIONS
|
||||
) && (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || requireActivity().checkGrantedPermission(
|
||||
Manifest.permission.ACTIVITY_RECOGNITION
|
||||
))
|
||||
) {
|
||||
injector.text(
|
||||
R.id.label,
|
||||
if (Preferences.showDailySteps) getString(R.string.settings_visible) else getString(
|
||||
R.string.settings_not_visible
|
||||
)
|
||||
)
|
||||
injector.visibility(R.id.error_icon, View.GONE)
|
||||
injector.visibility(R.id.info_icon, View.VISIBLE)
|
||||
isVisible = Preferences.showDailySteps
|
||||
} else if (Preferences.showDailySteps) {
|
||||
ActivityDetectionReceiver.unregisterFence(requireContext())
|
||||
injector.visibility(R.id.error_icon, View.VISIBLE)
|
||||
injector.visibility(R.id.info_icon, View.GONE)
|
||||
injector.text(R.id.label, getString(R.string.settings_not_visible))
|
||||
isVisible = false
|
||||
} else {
|
||||
ActivityDetectionReceiver.unregisterFence(requireContext())
|
||||
injector.text(R.id.label, getString(R.string.settings_not_visible))
|
||||
injector.visibility(R.id.error_icon, View.GONE)
|
||||
injector.visibility(R.id.info_icon, View.VISIBLE)
|
||||
isVisible = false
|
||||
}
|
||||
}
|
||||
Constants.GlanceProviderId.EVENTS -> {
|
||||
isVisible =
|
||||
Preferences.showEventsAsGlanceProvider
|
||||
@ -491,30 +449,6 @@ class GlanceTabFragment : Fragment() {
|
||||
Preferences.showDailySteps = false
|
||||
}
|
||||
|
||||
if (dialog != null) {
|
||||
dialog?.show()
|
||||
}
|
||||
}
|
||||
2 -> {
|
||||
try {
|
||||
val account: GoogleSignInAccount? = GoogleSignIn.getSignedInAccountFromIntent(
|
||||
data
|
||||
).getResult(ApiException::class.java)
|
||||
if (!GoogleSignIn.hasPermissions(account, FITNESS_OPTIONS)) {
|
||||
GoogleSignIn.requestPermissions(
|
||||
requireActivity(),
|
||||
1,
|
||||
account,
|
||||
FITNESS_OPTIONS
|
||||
)
|
||||
} else {
|
||||
adapter.notifyItemRangeChanged(0, adapter.data.size)
|
||||
}
|
||||
} catch (e: ApiException) {
|
||||
e.printStackTrace()
|
||||
Preferences.showDailySteps = false
|
||||
}
|
||||
|
||||
if (dialog != null) {
|
||||
dialog?.show()
|
||||
}
|
||||
|
@ -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,7 +134,7 @@ class WeatherFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun checkLocationPermission() {
|
||||
if (requireActivity().checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
|
||||
if (requireActivity().checkGrantedPermission(Manifest.permission.ACCESS_COARSE_LOCATION)) {
|
||||
binding.locationPermissionAlert.isVisible = false
|
||||
} else if (Preferences.customLocationAdd == "") {
|
||||
binding.locationPermissionAlert.isVisible = true
|
||||
@ -177,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()
|
||||
}
|
||||
@ -208,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 -> {
|
||||
//}
|
||||
@ -222,15 +218,35 @@ class WeatherFragment : Fragment() {
|
||||
private fun requirePermission() {
|
||||
Dexter.withContext(requireContext())
|
||||
.withPermissions(
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -238,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()
|
||||
@ -246,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()
|
||||
|
@ -155,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 }
|
||||
|
@ -1,69 +0,0 @@
|
||||
package com.tommasoberlose.anotherwidget.ui.viewmodels.settings
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.android.billingclient.api.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class SupportDevViewModel : ViewModel() {
|
||||
|
||||
lateinit var billingClient: BillingClient
|
||||
val products: MutableLiveData<List<SkuDetails>> = MutableLiveData(emptyList())
|
||||
|
||||
fun openConnection() {
|
||||
|
||||
billingClient.startConnection(object : BillingClientStateListener {
|
||||
override fun onBillingSetupFinished(billingResult: BillingResult) {
|
||||
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
|
||||
val params = SkuDetailsParams.newBuilder()
|
||||
params.setSkusList(listOf("donation_coffee", "donation_donuts", "donation_breakfast", "donation_lunch", "donation_dinner")).setType(BillingClient.SkuType.INAPP)
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val skuDetailsList = billingClient.querySkuDetails(params.build()).skuDetailsList
|
||||
withContext(Dispatchers.Main) {
|
||||
products.value = skuDetailsList
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun onBillingServiceDisconnected() {
|
||||
// Try to restart the connection on the next request to
|
||||
// Google Play by calling the startConnection() method.
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun purchase(activity: Activity, product: SkuDetails) {
|
||||
val flowParams = BillingFlowParams.newBuilder()
|
||||
.setSkuDetails(product)
|
||||
.build()
|
||||
billingClient.launchBillingFlow(activity, flowParams)
|
||||
}
|
||||
|
||||
fun handlePurchase(purchase: Purchase) {
|
||||
if (!purchase.isAcknowledged) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val token = purchase.purchaseToken
|
||||
val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
|
||||
.setPurchaseToken(token)
|
||||
billingClient.acknowledgePurchase(acknowledgePurchaseParams.build())
|
||||
|
||||
val consumeParams =
|
||||
ConsumeParams.newBuilder()
|
||||
.setPurchaseToken(token)
|
||||
.build()
|
||||
billingClient.consumePurchase(consumeParams)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun closeConnection() {
|
||||
billingClient.endConnection()
|
||||
}
|
||||
}
|
@ -9,10 +9,9 @@ import androidx.lifecycle.viewModelScope
|
||||
import com.koolio.library.DownloadableFontList
|
||||
import com.koolio.library.Font
|
||||
import com.koolio.library.FontList
|
||||
import com.tommasoberlose.anotherwidget.BuildConfig
|
||||
//import com.tommasoberlose.anotherwidget.BuildConfig
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class CustomFontViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
@ -34,7 +33,7 @@ class CustomFontViewModel(application: Application) : AndroidViewModel(applicati
|
||||
}
|
||||
}
|
||||
|
||||
DownloadableFontList.requestDownloadableFontList(fontListCallback, BuildConfig.GOOGLE_API_KEY)
|
||||
// DownloadableFontList.requestDownloadableFontList(fontListCallback, BuildConfig.GOOGLE_API_KEY)
|
||||
}
|
||||
}
|
||||
}
|
@ -6,12 +6,10 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.text.format.DateUtils
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
@ -58,7 +56,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)
|
||||
|
||||
@ -103,7 +101,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)
|
||||
@ -133,7 +131,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)
|
||||
@ -170,7 +168,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
|
||||
)
|
||||
)
|
||||
|
||||
@ -184,7 +182,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(
|
||||
@ -221,7 +219,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 {
|
||||
@ -229,7 +227,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)
|
||||
}
|
||||
@ -249,7 +247,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
|
||||
@ -264,7 +262,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
|
||||
@ -280,7 +278,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
|
||||
@ -300,7 +298,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
|
||||
@ -321,7 +319,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,
|
||||
@ -339,7 +337,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,
|
||||
@ -349,7 +347,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,
|
||||
@ -363,7 +361,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,
|
||||
@ -496,8 +494,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,
|
||||
@ -510,7 +507,18 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
|
||||
if (!Preferences.showNextEventOnMultipleLines) {
|
||||
bindingView.nextEventDifferenceTime.isVisible = true
|
||||
} else {
|
||||
bindingView.nextEvent.text = context.getString(R.string.events_glance_provider_format).format(nextEvent.title, diffTime)
|
||||
val text = context.getString(R.string.events_glance_provider_format).format(nextEvent.title, diffTime)
|
||||
if (text.endsWith(diffTime)) {
|
||||
bindingView.nextEvent.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
|
||||
(v as TextView).layout?.run {
|
||||
val diff = diffTime.trimStart();
|
||||
val diffStart = text.length - diff.length
|
||||
if (getLineStart(lineCount - 1) > diffStart)
|
||||
v.text = (text.substring(0, diffStart).trimEnd() + '\n' + diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
bindingView.nextEvent.text = text
|
||||
bindingView.nextEventDifferenceTime.isVisible = false
|
||||
}
|
||||
} else {
|
||||
@ -577,11 +585,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)
|
||||
@ -716,8 +720,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,
|
||||
|
@ -11,7 +11,6 @@ import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.IntentHelper
|
||||
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
|
||||
import com.tommasoberlose.anotherwidget.utils.toPixel
|
||||
|
||||
class ClockWidget(val context: Context) {
|
||||
fun updateClockView(views: RemoteViews, widgetID: Int): RemoteViews {
|
||||
@ -41,7 +40,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)
|
||||
|
@ -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 {
|
||||
|
@ -6,12 +6,10 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.text.format.DateUtils
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.RemoteViews
|
||||
import android.widget.TextView
|
||||
@ -60,7 +58,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)
|
||||
|
||||
@ -105,7 +103,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)
|
||||
@ -135,7 +133,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)
|
||||
@ -178,7 +176,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
|
||||
)
|
||||
)
|
||||
|
||||
@ -197,7 +195,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
|
||||
)
|
||||
)
|
||||
|
||||
@ -213,7 +211,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)
|
||||
@ -246,7 +244,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 {
|
||||
@ -254,7 +252,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)
|
||||
}
|
||||
@ -280,7 +278,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
|
||||
@ -295,7 +293,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
|
||||
@ -311,7 +309,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
|
||||
@ -331,7 +329,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
|
||||
@ -352,7 +350,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,
|
||||
@ -370,7 +368,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,
|
||||
@ -380,7 +378,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,
|
||||
@ -394,7 +392,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,
|
||||
@ -528,8 +526,7 @@ class StandardWidget(val context: Context) {
|
||||
context,
|
||||
now.timeInMillis,
|
||||
nextEvent.startDate
|
||||
)
|
||||
.toLowerCase(Locale.getDefault())
|
||||
).toLowerCase(Locale.getDefault())
|
||||
} else {
|
||||
SettingsStringHelper.getAllDayEventDifferenceText(
|
||||
context,
|
||||
@ -542,7 +539,18 @@ class StandardWidget(val context: Context) {
|
||||
if (!Preferences.showNextEventOnMultipleLines) {
|
||||
bindingView.nextEventDifferenceTime.isVisible = true
|
||||
} else {
|
||||
bindingView.nextEvent.text = context.getString(R.string.events_glance_provider_format).format(nextEvent.title, diffTime)
|
||||
val text = context.getString(R.string.events_glance_provider_format).format(nextEvent.title, diffTime)
|
||||
if (text.endsWith(diffTime)) {
|
||||
bindingView.nextEvent.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
|
||||
(v as TextView).layout?.run {
|
||||
val diff = diffTime.trimStart();
|
||||
val diffStart = text.length - diff.length
|
||||
if (getLineStart(lineCount - 1) > diffStart)
|
||||
v.text = (text.substring(0, diffStart).trimEnd() + '\n' + diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
bindingView.nextEvent.text = text
|
||||
bindingView.nextEventDifferenceTime.isVisible = false
|
||||
}
|
||||
} else {
|
||||
@ -609,11 +617,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)
|
||||
@ -748,8 +752,7 @@ class StandardWidget(val context: Context) {
|
||||
context,
|
||||
now.timeInMillis,
|
||||
nextEvent.startDate
|
||||
)
|
||||
.toLowerCase(Locale.getDefault())
|
||||
).toLowerCase(Locale.getDefault())
|
||||
} else {
|
||||
SettingsStringHelper.getAllDayEventDifferenceText(
|
||||
context,
|
||||
|
@ -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) {
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 135 B |
Binary file not shown.
Before Width: | Height: | Size: 100 B |
Binary file not shown.
Before Width: | Height: | Size: 144 B |
Binary file not shown.
Before Width: | Height: | Size: 219 B |
Binary file not shown.
Before Width: | Height: | Size: 275 B |
@ -118,14 +118,6 @@
|
||||
<string name="api_key_hint">Clave de API de OpenWeather</string>
|
||||
<string name="default_weather_app">Clima de Google</string>
|
||||
<string name="weather_warning">El clima de Google Awareness está obsoleto. ahora se requiere de una clave de API de OpenWeather para mostrar el clima en el widget.</string>
|
||||
<string name="api_key_title_1">Registrarse para obtener una cuenta de OpenWeather</string>
|
||||
<string name="api_key_subtitle_1">Registra una cuenta gratis en OpenWeather. Solo tomará unos minutos.</string>
|
||||
<string name="api_key_title_2">Copia tu clave de API</string>
|
||||
<string name="api_key_subtitle_2">Encuentra el menu de claves API en los ajustes de tu cuenta y copia la clave predeterminada.</string>
|
||||
<string name="api_key_title_3">Ingresa la clave a la app</string>
|
||||
<string name="api_key_subtitle_3">Pega la clave en el campo de debajo y guárdalo. Una vez la clave sea activada el clima será visible.</string>
|
||||
<string name="action_open_provider">Ir a OpenWeatherMap.com</string>
|
||||
<string name="api_key_info_all_set">La activación de tu clave de API puede llegar a tomar unos diez minutos. El clima se actualizará tan pronto esté disponible.</string>
|
||||
<string name="settings_weather_icon_pack_title">Paquete de iconos</string>
|
||||
<string name="settings_weather_icon_pack_default">Paquete de iconos %d</string>
|
||||
<string name="background_location_warning">Cuando el GPS está encendido, recolectamos los datos de la ubicación para actualizar la información del clima cuando la app está cerrada o no está en uso.\nNo usaremos esos datos en caso contrario.</string>
|
||||
|
@ -97,14 +97,6 @@
|
||||
<string name="api_key_hint">Kunci API OpenWeather</string>
|
||||
<string name="default_weather_app">Google Weather</string>
|
||||
<string name="weather_warning">Layanan cuaca Google Awareness telah ditutup. Sekarang membutuhkan Kunci API OpenWeather untuk menampilkan cuaca di widget.</string>
|
||||
<string name="api_key_title_1">Daftar akun OpenWeather</string>
|
||||
<string name="api_key_subtitle_1">Daftar akun gratis pada situs OpenWeather. Hanya akan membutuhkan waktu beberapa menit saja.</string>
|
||||
<string name="api_key_title_2">Salin Kunci API anda</string>
|
||||
<string name="api_key_subtitle_2">Temukan menu kunci API dari pengaturan akun anda dan salin kunci default.</string>
|
||||
<string name="api_key_title_3">Masukkan kunci API ke aplikasi</string>
|
||||
<string name="api_key_subtitle_3">Tempel kunci API ke dalam ruas di atas dan simpan. Setelah kunci diaktifkan, cuaca akan segera ditampilkan.</string>
|
||||
<string name="action_open_provider">Kunjungi OpenWeatherMap.com</string>
|
||||
<string name="api_key_info_all_set">Mungkin membutuhkan lebih kurang 10 menit sebelum kunci API anda diaktifkan. Cuaca akan diperbarui segera setelah data tersedia.</string>
|
||||
<string name="settings_weather_icon_pack_title">Paket ikon</string>
|
||||
<string name="settings_weather_icon_pack_default">Paket ikon %d</string>
|
||||
|
||||
@ -128,7 +120,6 @@
|
||||
|
||||
<!-- Glance -->
|
||||
<string name="settings_show_next_alarm_title">Waktu alarm berikutnya</string>
|
||||
<string name="next_alarm_warning">Waktu alarm berikutnya sepertinya salah.\nTelah disetel %s.</string>
|
||||
<string name="settings_at_a_glance_title">Sekilas</string>
|
||||
<string name="settings_show_music_title">Lagu yang diputar</string>
|
||||
<string name="settings_request_notification_access">Membutuhkan izin akses notifikasi untuk menampilkan lagu yang saat ini diputar.</string>
|
||||
|
@ -117,14 +117,6 @@
|
||||
<string name="api_key_hint">Chiave API OpenWeather</string>
|
||||
<string name="default_weather_app">Meteo Google</string>
|
||||
<string name="weather_warning">Il meteo di Google Awareness è stato deprecato. È ora necessaria una chiave API OpenWeather per mostrare il meteo.</string>
|
||||
<string name="api_key_title_1">Registra un account su OpenWeather</string>
|
||||
<string name="api_key_subtitle_1">Registra un account gratuito su OpenWeather, ci vorranno solo alcuni minuti.</string>
|
||||
<string name="api_key_title_2">Copia la chiave API</string>
|
||||
<string name="api_key_subtitle_2">Trova la chiave API nelle impostazioni del tuo nuovo account e copia la chiave di default.</string>
|
||||
<string name="api_key_title_3">Inserisci la chiave nell\'app</string>
|
||||
<string name="api_key_subtitle_3">Copia qui la chiave API e salva la nuova configurazione.</string>
|
||||
<string name="action_open_provider">Apri OpenWeatherMap.com</string>
|
||||
<string name="api_key_info_all_set">La chiave API potrebbe aver bisogno di più di 20 minuti per diventare attiva, aggiorneremo il meteo non appena sarà possibile.</string>
|
||||
<string name="settings_weather_icon_pack_title">Icon pack</string>
|
||||
<string name="settings_weather_icon_pack_default">Icon pack %d</string>
|
||||
<string name="background_location_warning">Con la geolocalizzazione attiva raccoglieremo i dati sulla posizione per attivare l\'aggiornamento del meteo anche quando l\'app è chiusa o non in uso.\nNon utilizzeremo i tuoi dati in nessun altro modo.</string>
|
||||
|
@ -94,14 +94,6 @@
|
||||
<string name="api_key_hint">Ключ API OpenWeather</string>
|
||||
<string name="default_weather_app">Google Погода</string>
|
||||
<string name="weather_warning">Погода Google Awareness устарела. Для отображения погоды в виджете требуется API ключ OpenWeather.</string>
|
||||
<string name="api_key_title_1">Создайте аккаунт OpenWeather</string>
|
||||
<string name="api_key_subtitle_1">Создайте бесплатный аккаунт OpenWeather. Это займёт не более нескольких минут.</string>
|
||||
<string name="api_key_title_2">Скопируйте ключ API</string>
|
||||
<string name="api_key_subtitle_2">Найдие меню с ключами API в настройках аккаунта и скопируйте ключ.</string>
|
||||
<string name="api_key_title_3">Введите ключ в приложении</string>
|
||||
<string name="api_key_subtitle_3">Вставьте ключ в поле выше и сохраните. Как только ключ будет активирован, погода начнёт отображаться.</string>
|
||||
<string name="action_open_provider">Перейти на OpenWeatherMap.com</string>
|
||||
<string name="api_key_info_all_set">Активация ключа может занять до десяти минут. Погода обновится как только появится возможность.</string>
|
||||
<string name="settings_weather_icon_pack_title">Набор иконок</string>
|
||||
<string name="settings_weather_icon_pack_default">Набор иконок %d</string>
|
||||
|
||||
@ -125,7 +117,6 @@
|
||||
|
||||
<!-- Glance -->
|
||||
<string name="settings_show_next_alarm_title">Следующий будильник</string>
|
||||
<string name="next_alarm_warning">Проверьте следующий будильник.\nОн был установлен %s</string>
|
||||
<string name="settings_at_a_glance_title">Glance</string>
|
||||
<string name="settings_show_music_title">Текущая песня</string>
|
||||
<string name="settings_request_notification_access">Нам необходим доступ к уведомлениям для отображения информации песни.</string>
|
||||
|
@ -107,14 +107,6 @@
|
||||
<string name="api_key_hint">Kľúč k OpenWeather API</string>
|
||||
<string name="default_weather_app">Počasie Google</string>
|
||||
<string name="weather_warning">Počasie Google Awareness je zastaralé. Pre zobrazovanie počasia vo widgete je vyžadovaný kľúč k OpenWeather API.</string>
|
||||
<string name="api_key_title_1">Vytvorte si OpenWeather účet</string>
|
||||
<string name="api_key_subtitle_1">Zaregistrujte sa zdarma na OpenWeather. Bude to trvať len pár minút.</string>
|
||||
<string name="api_key_title_2">Skopírujte kľúč k API</string>
|
||||
<string name="api_key_subtitle_2">Nájdite menu s kľúčmi k API vo Vašom účte a skopírujte predvolený kľúč.</string>
|
||||
<string name="api_key_title_3">Vložte kľúč do aplikácie</string>
|
||||
<string name="api_key_subtitle_3">Vložte kľúč do poľa vyššie a uložte ho. Počasie sa zobrazí hneď ako bude kľúč aktivovaný.</string>
|
||||
<string name="action_open_provider">Prejdite na OpenWeatherMap.com</string>
|
||||
<string name="api_key_info_all_set">Aktivácia Vášho API kľúča môže trvať až 10 minút. Počasie sa aktualizuje hneď ako bude dostupné.</string>
|
||||
<string name="settings_weather_icon_pack_title">Balíček ikon</string>
|
||||
<string name="settings_weather_icon_pack_default">Balíček ikon %d</string>
|
||||
<string name="background_location_warning">Zbierame údaje o polohe pre aktualizáciu počasia aj keď je aplikácia zatvorená alebo sa nepoužíva.\nInak tieto údaje nepoužijeme.</string>
|
||||
@ -139,7 +131,6 @@
|
||||
|
||||
<!-- Glance -->
|
||||
<string name="settings_show_next_alarm_title">Nasledujúci budík</string>
|
||||
<string name="next_alarm_warning">Nasledujúci budík vyzerá nesprávne.\nBol nastavený cez %s.</string>
|
||||
<string name="settings_at_a_glance_title">Rýchle informácie</string>
|
||||
<string name="settings_show_music_title">Práve prehrávaná skladba</string>
|
||||
<string name="settings_request_notification_access">Pre získanie informácií o práve prehrávanej skladbe potrebujeme povolenie prístupu k notifikáciám.</string>
|
||||
|
@ -11,10 +11,6 @@ buildscript {
|
||||
dependencies {
|
||||
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.8'
|
||||
|
||||
// Add the Crashlytics Gradle plugin.
|
||||
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
|
||||
|
@ -8,17 +8,6 @@ If you choose to use my Service, then you agree to the collection and use of inf
|
||||
|
||||
The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at Another Widget unless otherwise defined in this Privacy Policy.
|
||||
|
||||
**Information Collection and Use**
|
||||
|
||||
For a better experience, while using our Service, I may require you to provide us with certain personally identifiable information, including but not limited to Location data. The information that I request will be retained on your device and is not collected by me in any way.
|
||||
|
||||
The app does use third party services that may collect information used to identify you.
|
||||
|
||||
Link to privacy policy of third party service providers used by the app
|
||||
|
||||
* [Google Play Services](https://www.google.com/policies/privacy/)
|
||||
* [Firebase Crashlytics](https://firebase.google.com/support/privacy/)
|
||||
|
||||
**Log Data**
|
||||
|
||||
I want to inform you that whenever you use my Service, in a case of an error in the app I collect data and information (through third party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing my Service, the time and date of your use of the Service, and other statistics.
|
||||
|
Loading…
x
Reference in New Issue
Block a user