Compare commits

...

60 Commits

Author SHA1 Message Date
77864cbef4 Suppress unused parameter warnings. 2021-09-27 00:02:50 +08:00
32c580bac7 Merge branch 'patch-1' into patch-develop 2021-09-26 22:29:09 +08:00
85fa0cae11 Add weather providers HERE.com and AccuWeather.com; fix Weather.gov. 2021-09-26 22:28:06 +08:00
05f2a745c2 Merge branch 'patch-1' into patch-develop
# Conflicts:
#	app/src/main/java/com/tommasoberlose/anotherwidget/helpers/WeatherHelper.kt
2021-09-25 11:34:02 +08:00
ef2e89b6ff Correct weather info for weatherbit.io, weather.gov and yr.no. 2021-09-25 11:31:53 +08:00
a306d92282 Make text shadows more visible. 2021-09-25 11:08:10 +08:00
da9fc362af Merge branch 'patch-3' into patch-develop 2021-09-24 10:45:07 +08:00
ff83cfd953 Update translation. 2021-09-24 10:44:05 +08:00
5d9dcd9701 Replace Realm with Room. 2021-09-23 00:23:42 +08:00
183901534c Correct the widget layout.
Adjust the layout carefully, so that remote grid views perfectly overlap the bitmap generated from the binding view, whether left-aligned, right-aligned or centered, and regardless of the size of the widget, text, margins or spacing.

Display the clock in the correct text size.
2021-09-17 12:01:41 +08:00
6d7d90e762 Correct the algorithm to check if to show weather as a glance provider. 2021-09-14 13:49:52 +08:00
e89b377b68 Fix crash in MainFragment.updateUI() on some devices. 2021-09-14 13:48:23 +08:00
e0eb6f77da Merge branch 'patch-4' into patch-develop 2021-09-14 13:40:14 +08:00
43c08204c3 Merge branch 'patch-3' into patch-develop 2021-09-14 13:39:59 +08:00
6f125573e0 Merge branch 'patch-2' into patch-develop
# Conflicts:
#	app/src/main/java/com/tommasoberlose/anotherwidget/ui/widgets/AlignedWidget.kt
#	app/src/main/java/com/tommasoberlose/anotherwidget/ui/widgets/StandardWidget.kt
2021-09-14 13:39:30 +08:00
380dc96c40 Merge branch 'patch-1' into patch-develop 2021-09-14 13:38:49 +08:00
821980e938 Use getBroadcast instead of getActivity for ACTION_REFRESH intents. 2021-09-13 23:34:54 +08:00
9f47d626a9 setExact is more reliable than setRepeating on some ROMs (e.g., MIUI). 2021-09-13 23:24:58 +08:00
d3b623cf13 Add ACCESS_BACKGROUND_LOCATION permission required by Android 11. 2021-09-13 19:28:47 +08:00
1389ddbfc0 Avoid the initial vertical position of BottomSheetDialogs being capped. 2021-09-13 11:55:29 +08:00
0dbbe0e5d2 Fix RenderScript leaks; set a reasonable default value for PREF_TEXT_CLOCK_SIZE. 2021-09-10 23:15:47 +08:00
818b4ec0ba Avoid redundant updates. 2021-09-04 18:35:39 +08:00
8c84913cd8 Remove trailing semicolons in code. 2021-09-03 20:54:02 +08:00
43f085b13c Remove extra spacing after event time when weather disabled. 2021-09-03 20:40:44 +08:00
3ab42fd163 Correct and improve calendar events updating algorithm.
1. UpdateCalendarService will fetch all events from now to the next fetch time + Preferences.showUntil, so that EventRepository holds all events need to be shown before the next fetch;
2. Every event is updated 1 minute after the corresponding time span, to make the actual time difference consistent with the displayed getRelativeTimeSpanString;
3. Update events only when necessary, eliminate redundant alarms; cancel all update alarms when showEvents is turned off.
2021-09-03 18:20:01 +08:00
ea0be72478 Do not schedule unnecessary ACTION_ALARM_UPDATE alarm. 2021-09-02 13:54:24 +08:00
c744d3c7f8 Improve translation. 2021-08-19 11:31:24 +08:00
7b93548b0b Use any available Maps app, not just explicitly Google Maps. 2021-08-16 12:39:11 +08:00
260e36b305 Show & hide all-day events at the right time. 2021-08-15 19:13:46 +08:00
00af2159ea Correct some translations. 2021-08-15 18:06:11 +08:00
c26021de03 Fix #321; Update Simplified Chinese. 2021-08-14 00:55:37 +08:00
218dae8cc2 When tapping the event time, it is more reasonable to open the calendar rather than the event details (tap the event title for the details). 2021-08-13 22:10:44 +08:00
b3e2d8d843 Fix FontRequest thread leaks and UI bugs related to custom fonts. 2021-08-13 22:09:46 +08:00
80d1077dab Make LocationService work in case GMS is not available. 2021-08-12 14:12:30 +08:00
dd2a74aaf6 Close #310, added weather as glance provider 2021-05-14 17:15:34 +02:00
fb1953d513 Added custom widget margin and padding, added new preview manager 2021-05-14 16:10:56 +02:00
b081b9adbb Merge pull request #327 from Moutony/patch-31
Update French to try fixing #321
2021-05-14 16:10:16 +02:00
fd398faf42 Update French to try fixing #321 2021-05-14 11:41:43 +02:00
e14662c534 Merge pull request #324 from Moutony/patch-30
French update v2.3.3 (139) - 13th May 2021
2021-05-14 08:57:58 +02:00
4224562512 Merge pull request #323 from Moutony/patch-29
GeoNames was missing an S
2021-05-14 08:57:51 +02:00
f13d426831 Fixed : Widget alignment 2021-05-14 02:33:56 +02:00
5dc7a1b30d Fixed : Widget alignment 2021-05-14 02:25:29 +02:00
6538cdebc2 Text alignment 2021-05-13 09:20:04 +02:00
78709ed018 Text alignment is more relevent than Widget align
Alignment is more correct too
2021-05-13 09:18:58 +02:00
c0e87047c2 French update v2.3.3 (139) - 13th May 2021
Loving the new logo!
2021-05-13 09:06:53 +02:00
1a2c97d61f GeoNames was missing an S 2021-05-13 08:58:28 +02:00
4335751749 Merge pull request #320 from Drumber/translation
Updated German translation
2021-05-12 20:44:24 +02:00
9bbc816bea Merge pull request #319 from chreddy/patch-8
Updating Danish translation
2021-05-12 20:44:02 +02:00
e8a6743dc4 Updated German translation 2021-05-12 18:28:42 +02:00
5d8ceb98cc Updating Danish translation
Adding a translation for default_apps_settings_header, making the translation complete
2021-05-11 23:02:22 +02:00
dfff4c5e1b Merge branch 'develop' into main 2021-05-11 21:05:26 +02:00
13f8814480 Bumped the version 2021-05-11 21:05:19 +02:00
8608f9adf3 Merge branch 'main' of github.com:tommasoberlose/another-widget into develop 2021-05-11 21:01:56 +02:00
ad65cf0e84 Merge branch 'main' of github.com:tommasoberlose/another-widget into main 2021-05-11 21:01:31 +02:00
285b754dd5 Merge pull request #315 from Drumber/translation
Update German translation
2021-05-10 12:36:56 +02:00
194a2ab456 Merge pull request #316 from Moutony/patch-28
Update French 9th May 2021
2021-05-10 12:36:44 +02:00
bca22d5aa8 Merge pull request #314 from chreddy/patch-7
Final update - Danish translation
2021-05-10 12:36:36 +02:00
35536a89a0 Update French 9th May 2021 2021-05-09 21:23:55 +02:00
6780a470e9 Update German translation
Added new strings
2021-05-08 09:23:38 +02:00
6ca6b5ab95 Final update - Danish translation
I realized I forgot to update a few strings. Should be all done now.

I also just noticed that there's no string for the text "Default apps" in the Gestures menu, right above the app selection for calendar, weather and clock. So this text keeps showing up in English on my phone, even though the translation is complete.
2021-05-07 20:30:29 +02:00
174 changed files with 2125 additions and 1340 deletions

View File

@ -7,8 +7,6 @@ apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'realm-android'
def apikeyPropertiesFile = rootProject.file("apikey.properties")
def apikeyProperties = new Properties()
apikeyProperties.load(new FileInputStream(apikeyPropertiesFile))
@ -22,7 +20,7 @@ android {
applicationId "com.tommasoberlose.anotherwidget"
minSdkVersion 23
targetSdkVersion 30
versionCode 138
versionCode 139
versionName "2.3.3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@ -88,6 +86,10 @@ dependencies {
// EventBus
implementation 'org.greenrobot:eventbus:3.2.0'
// Room
implementation "androidx.room:room-runtime:2.3.0"
kapt "androidx.room:room-compiler:2.3.0"
// Navigation
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'

View File

@ -6,6 +6,7 @@
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
@ -13,6 +14,7 @@
<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" />
<application
android:allowBackup="true"
@ -24,24 +26,24 @@
android:usesCleartextTraffic="true"
android:theme="@style/AppTheme"
tools:ignore="LockedOrientationActivity">
<activity android:name=".ui.activities.MainActivity" android:launchMode="singleInstance" android:theme="@style/AppTheme.Main" android:screenOrientation="portrait">
<activity android:name=".ui.activities.MainActivity" android:theme="@style/AppTheme.Main" android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ui.activities.tabs.ChooseApplicationActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.CustomFontActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.CustomLocationActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.WeatherProviderActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.settings.SupportDevActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.CustomDateActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.settings.IntegrationsActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.MusicPlayersFilterActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.AppNotificationsFilterActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.MediaInfoFormatActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.TimeZoneSelectorActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.ChooseApplicationActivity" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.CustomFontActivity" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.CustomLocationActivity" android:screenOrientation="portrait" />
<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" />
<activity android:name=".ui.activities.tabs.AppNotificationsFilterActivity" android:screenOrientation="portrait" />
<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">
<intent-filter>

View File

@ -7,10 +7,6 @@ import androidx.appcompat.app.AppCompatDelegate
import com.chibatching.kotpref.Kotpref
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import io.realm.Realm
import io.realm.RealmConfiguration
import net.danlew.android.joda.JodaTimeAndroid
class AWApplication : Application() {
override fun onCreate() {
@ -24,29 +20,5 @@ class AWApplication : Application() {
// Dark theme
AppCompatDelegate.setDefaultNightMode(Preferences.darkThemePreference)
// Realm
Realm.init(this)
val config = RealmConfiguration.Builder()
.deleteRealmIfMigrationNeeded()
.build()
Realm.setDefaultConfiguration(config)
calibrateVersions()
}
private fun calibrateVersions() {
// 2.0 Tolerance
if (Preferences.clockTextSize > 50f) {
Preferences.clockTextSize = 32f
}
if (Preferences.textMainSize > 36f) {
Preferences.textMainSize = 32f
}
if (Preferences.textSecondSize > 28f) {
Preferences.textSecondSize = 24f
}
}
}
}

View File

@ -143,7 +143,6 @@ class BottomSheetColorPicker(
withContext(Dispatchers.Main) {
binding.loader.isVisible = false
binding.listContainer.addView(listBinding.root)
this@BottomSheetColorPicker.behavior.state = BottomSheetBehavior.STATE_EXPANDED
binding.listContainer.isVisible = true
val idx = colors.toList().indexOf(getSelected?.invoke())
@ -152,6 +151,10 @@ class BottomSheetColorPicker(
})
setContentView(binding.root)
behavior.run {
skipCollapsed = true
state = BottomSheetBehavior.STATE_EXPANDED
}
super.show()
}

View File

@ -108,6 +108,10 @@ open class BottomSheetMenu<T>(context: Context, private val header: String? = nu
}
}
setContentView(binding.root)
behavior.run {
skipCollapsed = true
state = com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
}
super.show()
}

View File

@ -86,7 +86,6 @@ class BottomSheetPicker<T>(
withContext(Dispatchers.Main) {
binding.loader.isVisible = false
binding.listContainer.addView(listBinding.root)
this@BottomSheetPicker.behavior.state = BottomSheetBehavior.STATE_EXPANDED
binding.listContainer.isVisible = true
val idx = items.toList().indexOfFirst { it.value == getSelected?.invoke() }
@ -95,6 +94,10 @@ class BottomSheetPicker<T>(
})
setContentView(binding.root)
behavior.run {
skipCollapsed = true
state = BottomSheetBehavior.STATE_EXPANDED
}
super.show()
}

View File

@ -61,5 +61,9 @@ class BottomSheetWeatherProviderSettings(context: Context, callback: () -> Unit)
}
setContentView(binding.root)
behavior.run {
skipCollapsed = true
state = com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
}
}
}

View File

@ -26,5 +26,9 @@ class CustomNotesDialog(context: Context, callback: (() -> Unit)?) : BottomSheet
binding.notes.requestFocus()
setContentView(binding.root)
behavior.run {
skipCollapsed = true
state = com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
}
}
}

View File

@ -18,8 +18,8 @@ class FixedFocusScrollView @JvmOverloads constructor(
var isScrollable = true
override fun scrollTo(x: Int, y: Int) {
if (isScrollable) {
if (isScrollable || !isLaidOut) {
super.scrollTo(x, y)
}
}
}
}

View File

@ -51,6 +51,7 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
Constants.GlanceProviderId.NOTIFICATIONS -> context.getString(R.string.settings_show_notifications_title)
Constants.GlanceProviderId.GREETINGS -> context.getString(R.string.settings_show_greetings_title)
Constants.GlanceProviderId.EVENTS -> context.getString(R.string.settings_show_events_as_glance_provider_title)
Constants.GlanceProviderId.WEATHER -> context.getString(R.string.settings_show_weather_as_glance_provider_title)
}
/* SUBTITLE*/
@ -63,6 +64,7 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
Constants.GlanceProviderId.NOTIFICATIONS -> context.getString(R.string.settings_show_notifications_subtitle)
Constants.GlanceProviderId.GREETINGS -> context.getString(R.string.settings_show_greetings_subtitle)
Constants.GlanceProviderId.EVENTS -> context.getString(R.string.settings_show_events_as_glance_provider_subtitle)
Constants.GlanceProviderId.WEATHER -> context.getString(R.string.settings_show_weather_as_glance_provider_subtitle)
}
/* SONG */
@ -140,6 +142,13 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
checkCalendarConfig()
}
/* WEATHER */
if (provider == Constants.GlanceProviderId.WEATHER) {
binding.header.isVisible = false
binding.divider.isVisible = false
checkWeatherConfig()
}
/* TOGGLE */
binding.providerSwitch.setCheckedImmediatelyNoEvent(when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> Preferences.showMusic
@ -150,6 +159,7 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
Constants.GlanceProviderId.NOTIFICATIONS -> Preferences.showNotifications
Constants.GlanceProviderId.GREETINGS -> Preferences.showGreetings
Constants.GlanceProviderId.EVENTS -> Preferences.showEventsAsGlanceProvider
Constants.GlanceProviderId.WEATHER -> Preferences.showWeatherAsGlanceProvider
})
var job: Job? = null
@ -208,6 +218,9 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
Constants.GlanceProviderId.EVENTS -> {
Preferences.showEventsAsGlanceProvider = isChecked
}
Constants.GlanceProviderId.WEATHER -> {
Preferences.showWeatherAsGlanceProvider = isChecked
}
else -> {
}
}
@ -217,10 +230,16 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
}
setContentView(binding.root)
behavior.run {
skipCollapsed = true
state = com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
}
super.show()
}
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) {
@ -255,6 +274,19 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
}
}
private fun checkWeatherConfig() {
if (!Preferences.showWeather || (Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-") || Preferences.weatherProviderLocationError != "") {
binding.warningContainer.isVisible = true
binding.warningTitle.text = context.getString(R.string.settings_show_weather_as_glance_provider_error)
binding.warningContainer.setOnClickListener {
dismiss()
EventBus.getDefault().post(MainFragment.ChangeTabEvent(1))
}
} else {
binding.warningContainer.isVisible = false
}
}
private fun checkNotificationPermission() {
when {
ActiveNotificationsHelper.checkNotificationAccess(context) -> {

View File

@ -50,6 +50,10 @@ class IconPackSelector(context: Context, private val header: String? = null) : B
binding.menu.addView(itemBinding.root)
}
setContentView(binding.root)
behavior.run {
skipCollapsed = true
state = com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
}
super.show()
}
}

View File

@ -57,6 +57,10 @@ class MaterialBottomSheetDialog(
}
setContentView(binding.root)
behavior.run {
skipCollapsed = true
state = com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
}
super.show()
}

View File

@ -3,32 +3,37 @@ package com.tommasoberlose.anotherwidget.db
import android.content.Context
import android.provider.CalendarContract
import android.util.Log
import androidx.room.Dao
import androidx.room.Database
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Room
import androidx.room.RoomDatabase
import com.chibatching.kotpref.bulk
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.applyFilters
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.sortEvents
import com.tommasoberlose.anotherwidget.models.Event
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import io.realm.Realm
import io.realm.RealmResults
import java.util.*
import kotlin.Comparator
import kotlin.collections.ArrayList
class EventRepository(val context: Context) {
private val realm by lazy { Realm.getDefaultInstance() }
private val db by lazy { EventDatabase.getDatabase(context) }
fun saveEvents(eventList: List<Event>) {
realm.executeTransaction { realm ->
realm.where(Event::class.java).findAll().deleteAllFromRealm()
realm.copyToRealm(eventList)
db.runInTransaction{
db.dao().run {
deleteAll()
insertAll(eventList)
}
}
}
fun clearEvents() {
realm.executeTransaction { realm ->
realm.where(Event::class.java).findAll().deleteAllFromRealm()
}
db.dao().deleteAll()
}
fun resetNextEventData() {
@ -44,11 +49,11 @@ class EventRepository(val context: Context) {
}
fun saveNextEventData(event: Event) {
Preferences.nextEventId = event.eventID
Preferences.nextEventId = event.id
}
fun getNextEvent(): Event? {
val nextEvent = getEventByEventId(Preferences.nextEventId)
val nextEvent = getEventById(Preferences.nextEventId)
val now = Calendar.getInstance().timeInMillis
val limit = Calendar.getInstance().apply {
timeInMillis = now
@ -64,75 +69,63 @@ class EventRepository(val context: Context) {
else -> add(Calendar.HOUR, 6)
}
}
val event = if (nextEvent != null && nextEvent.endDate > now && nextEvent.startDate < limit.timeInMillis) {
return if (nextEvent != null && nextEvent.endDate > now && nextEvent.startDate <= limit.timeInMillis) {
nextEvent
} else {
val events = getEvents()
if (events.isNotEmpty()) {
val newNextEvent = events.first()
Preferences.nextEventId = newNextEvent.eventID
Preferences.nextEventId = newNextEvent.id
newNextEvent
} else {
resetNextEventData()
null
}
}
return try {
realm.copyFromRealm(event!!)
} catch (ex: Exception) {
event
}
}
fun getEventByEventId(id: Long): Event? {
val event = realm.where(Event::class.java).equalTo("eventID", id).findFirst()
return try {
realm.copyFromRealm(event!!)
} catch (ex: Exception) {
event
}
fun getEventById(id: Long): Event? {
return db.dao().findById(id)
}
fun goToNextEvent() {
val eventList = getEvents()
if (eventList.isNotEmpty()) {
val index = eventList.indexOfFirst { it.eventID == Preferences.nextEventId }
val index = eventList.indexOfFirst { it.id == Preferences.nextEventId }
if (index > -1 && index < eventList.size - 1) {
Preferences.nextEventId = eventList[index + 1].eventID
Preferences.nextEventId = eventList[index + 1].id
} else {
Preferences.nextEventId = eventList.first().eventID
Preferences.nextEventId = eventList.first().id
}
} else {
resetNextEventData()
}
UpdatesReceiver.setUpdates(context)
MainWidget.updateWidget(context)
org.greenrobot.eventbus.EventBus.getDefault().post(
com.tommasoberlose.anotherwidget.ui.fragments.MainFragment.UpdateUiMessageEvent()
)
}
fun goToPreviousEvent() {
val eventList = getEvents()
if (eventList.isNotEmpty()) {
val index = eventList.indexOfFirst { it.eventID == Preferences.nextEventId }
val index = eventList.indexOfFirst { it.id == Preferences.nextEventId }
if (index > 0) {
Preferences.nextEventId = eventList[index - 1].eventID
Preferences.nextEventId = eventList[index - 1].id
} else {
Preferences.nextEventId = eventList.last().eventID
Preferences.nextEventId = eventList.last().id
}
} else {
resetNextEventData()
}
UpdatesReceiver.setUpdates(context)
MainWidget.updateWidget(context)
org.greenrobot.eventbus.EventBus.getDefault().post(
com.tommasoberlose.anotherwidget.ui.fragments.MainFragment.UpdateUiMessageEvent()
)
}
fun getFutureEvents(): List<Event> {
val now = Calendar.getInstance().timeInMillis
realm.refresh()
return realm
.where(Event::class.java)
.greaterThan("endDate", now)
.findAll()
.applyFilters()
return db.dao().findFuture(Calendar.getInstance().timeInMillis).applyFilters().sortEvents()
}
private fun getEvents(): List<Event> {
@ -151,18 +144,54 @@ class EventRepository(val context: Context) {
else -> add(Calendar.HOUR, 6)
}
}
realm.refresh()
return realm
.where(Event::class.java)
.greaterThan("endDate", now)
.lessThanOrEqualTo("startDate", limit.timeInMillis)
.findAll()
.applyFilters()
return db.dao().find(now, limit.timeInMillis).applyFilters().sortEvents()
}
fun getEventsCount(): Int = getEvents().size
fun close() {
realm.close()
// db.close()
}
}
@Dao
interface EventDao {
@Query("SELECT * FROM events WHERE id = :id LIMIT 1")
fun findById(id: Long) : Event?
@Query("SELECT * FROM events WHERE end_date > :from")
fun findFuture(from: Long) : List<Event>
@Query("SELECT * FROM events WHERE end_date > :from and start_date <= :to")
fun find(from: Long, to: Long) : List<Event>
@Insert
fun insertAll(events: List<Event>)
@Query("DELETE FROM events")
fun deleteAll()
}
@Database(entities = arrayOf(Event::class), version = 1, exportSchema = false)
abstract class EventDatabase : RoomDatabase() {
abstract fun dao(): EventDao
companion object {
private var INSTANCE: EventDatabase? = null
fun getDatabase(context: Context): EventDatabase {
// if the INSTANCE is not null, then return it,
// if it is, then create the database
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
EventDatabase::class.java,
"events"
).allowMainThreadQueries().build()
INSTANCE = instance
// return instance
instance
}
}
}
}
}

View File

@ -23,6 +23,13 @@ object Constants {
LARGE(3)
}
enum class Dimension(val rawValue: Float) {
NONE(0f),
SMALL(8f),
MEDIUM(16f),
LARGE(24f)
}
enum class GlanceProviderId(val id: String) {
PLAYING_SONG("PLAYING_SONG"),
NEXT_CLOCK_ALARM("NEXT_CLOCK_ALARM"),
@ -31,7 +38,8 @@ object Constants {
GOOGLE_FIT_STEPS("GOOGLE_FIT_STEPS"),
NOTIFICATIONS("NOTIFICATIONS"),
GREETINGS("GREETINGS"),
EVENTS("EVENTS");
EVENTS("EVENTS"),
WEATHER("WEATHER");
companion object {
private val map = GlanceProviderId.values().associateBy(GlanceProviderId::id)

View File

@ -84,16 +84,20 @@ object Preferences : KotprefModel() {
var weatherIconPack by intPref(default = Constants.WeatherIconPack.DEFAULT.rawValue)
// UI
var widgetMargin by floatPref(default = Constants.Dimension.SMALL.rawValue)
var widgetPadding by floatPref(default = Constants.Dimension.SMALL.rawValue)
// Clock
var altTimezoneLabel by stringPref(default = "")
var altTimezoneId by stringPref(default = "")
// Global
var textMainSize by floatPref(key = "PREF_TEXT_MAIN_SIZE", default = 26f)
var textSecondSize by floatPref(key = "PREF_TEXT_SECOND_SIZE", default = 18f)
var clockTextSize by floatPref(key = "PREF_TEXT_CLOCK_SIZE", default = 90f)
var textMainSize by floatPref(key = "PREF_TEXT_MAIN_SIZE", default = 24f)
var textSecondSize by floatPref(key = "PREF_TEXT_SECOND_SIZE", default = 16f)
var clockTextSize by floatPref(key = "PREF_TEXT_CLOCK_SIZE", default = 72f)
var clockBottomMargin by intPref(default = Constants.ClockBottomMargin.MEDIUM.rawValue)
var secondRowTopMargin by intPref(default = Constants.SecondRowTopMargin.NONE.rawValue)
var secondRowTopMargin by intPref(default = Constants.SecondRowTopMargin.SMALL.rawValue)
var showClock by booleanPref(key = "PREF_SHOW_CLOCK", default = false)
var clockAppName by stringPref(key = "PREF_CLOCK_APP_NAME", default = "")
var clockAppPackage by stringPref(key = "PREF_CLOCK_APP_PACKAGE", default = "")
@ -149,6 +153,7 @@ object Preferences : KotprefModel() {
var appNotificationsFilter by stringPref(default = "")
var showEventsAsGlanceProvider by booleanPref(default = false)
var showWeatherAsGlanceProvider by booleanPref(default = false)
// Integrations
var installedIntegrations by intPref(default = 0)

View File

@ -44,7 +44,6 @@ object AlarmHelper {
val intent = Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_ALARM_UPDATE
}
cancel(PendingIntent.getBroadcast(context, ALARM_UPDATE_ID, intent, 0))
setExact(
AlarmManager.RTC,
trigger,
@ -58,5 +57,14 @@ object AlarmHelper {
}
}
fun clearTimeout(context: Context) {
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
val intent = Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_ALARM_UPDATE
}
cancel(PendingIntent.getBroadcast(context, ALARM_UPDATE_ID, intent, 0))
}
}
private const val ALARM_UPDATE_ID = 24953
}

View File

@ -44,8 +44,8 @@ object BitmapHelper {
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", measuredWidth)
FirebaseCrashlytics.getInstance().setCustomKey("WIDGET final height", view.measuredHeight)
FirebaseCrashlytics.getInstance().setCustomKey("WIDGET final width", widgetWidth)
FirebaseCrashlytics.getInstance().setCustomKey("WIDGET final height", widgetHeight)
}
return try {
@ -58,7 +58,7 @@ object BitmapHelper {
//Bind a canvas to it
val canvas = Canvas(btm)
// draw the view on the canvas
view.layout(0, 0, measuredWidth, measuredHeight)
view.layout(0, 0, widgetWidth, widgetHeight)
view.draw(canvas)
//return the bitmap
}

View File

@ -90,6 +90,12 @@ object GlanceProviderHelper {
R.drawable.round_event_note_24
)
}
Constants.GlanceProviderId.WEATHER -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_show_weather_as_glance_provider_title),
R.drawable.round_brightness_5_24
)
}
}
}
@ -108,10 +114,11 @@ object GlanceProviderHelper {
(MediaPlayerHelper.isSomeonePlaying(context)) ||
(Preferences.showBatteryCharging && Preferences.isCharging || Preferences.isBatteryLevelLow) ||
(Preferences.customNotes.isNotEmpty()) ||
(Preferences.showWeatherAsGlanceProvider && Preferences.showWeather && Preferences.weatherIcon != "") ||
(Preferences.showDailySteps && Preferences.googleFitSteps > 0) ||
(Preferences.showGreetings && GreetingsHelper.showGreetings()) ||
(Preferences.showEventsAsGlanceProvider && Preferences.showEvents && context.checkGrantedPermission(
Manifest.permission.READ_CALENDAR) && eventRepository.getNextEvent() != null)
(Preferences.showEventsAsGlanceProvider && Preferences.showEvents && context.checkGrantedPermission(
Manifest.permission.READ_CALENDAR) && eventRepository.getNextEvent() != null)
)
eventRepository.close()
return showGlance

View File

@ -17,7 +17,7 @@ object ImageHelper {
0 -> 0f * factor
1 -> 8f * factor
2 -> 16f * factor
else -> 0f * factor
else -> 8f * factor
}, resources.displayMetrics)
if (originalView.drawable != null && originalView.drawable.intrinsicWidth > 0 && originalView.drawable.intrinsicHeight > 0) {
@ -58,7 +58,7 @@ object ImageHelper {
0 -> 0f * factor
1 -> 0.8f * factor
2 -> 1f * factor
else -> 0f
else -> 0.8f * factor
}))
colorMatrixScript.setColorMatrix(matrix)
@ -73,6 +73,9 @@ object ImageHelper {
allocationIn.destroy()
allocationOut.destroy()
colorMatrixScript.destroy()
blurScript.destroy()
//rs.destroy()
return bitmap
}

View File

@ -1,5 +1,6 @@
package com.tommasoberlose.anotherwidget.helpers
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.ContentUris
@ -27,6 +28,13 @@ object IntentHelper {
const val DO_NOTHING_OPTION = "DO_NOTHING"
const val REFRESH_WIDGET_OPTION = "REFRESH_WIDGET"
fun getPendingIntent(context: Context, requestCode: Int, intent: Intent, flags: Int): PendingIntent {
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)
}
fun getWidgetUpdateIntent(context: Context): Intent {
val widgetManager = AppWidgetManager.getInstance(context)
val widgetComponent = ComponentName(context, MainWidget::class.java)
@ -40,21 +48,19 @@ object IntentHelper {
private fun getWidgetRefreshIntent(context: Context): Intent {
return Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_REFRESH
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
}
fun getGoogleMapsIntentFromAddress(context: Context, address: String): Intent {
val gmmIntentUri: Uri = Uri.parse("geo:0,0?q=$address")
val gmmIntentUri: Uri = Uri.parse("geo:0,0?q=${Uri.encode(address)}")
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
mapIntent.`package` = "com.google.android.apps.maps"
//mapIntent.`package` = "com.google.android.apps.maps"
return if (mapIntent.resolveActivity(context.packageManager) != null) {
mapIntent
mapIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
} else {
val map = "http://maps.google.co.in/maps?q=$address"
val i = Intent(Intent.ACTION_VIEW, Uri.parse(map));
i
val map = "https://www.google.com/maps/search/?api=1&query=${Uri.encode(address)}"
Intent(Intent.ACTION_VIEW, Uri.parse(map)).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
}
@ -62,7 +68,6 @@ object IntentHelper {
return when (Preferences.weatherAppPackage) {
DEFAULT_OPTION -> {
Intent(Intent.ACTION_VIEW).apply {
addCategory(Intent.CATEGORY_DEFAULT)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
data = Uri.parse("dynact://velour/weather/ProxyActivity")
component = ComponentName("com.google.android.googlequicksearchbox", "com.google.android.apps.gsa.velour.DynamicActivityTrampoline")
@ -89,15 +94,16 @@ object IntentHelper {
}
}
fun getCalendarIntent(context: Context): Intent {
fun getCalendarIntent(context: Context, time: Long? = null): Intent {
val calendarUri = CalendarContract.CONTENT_URI
.buildUpon()
.appendPath("time")
.appendPath(Calendar.getInstance().timeInMillis.toString())
.appendPath((time ?: Calendar.getInstance().timeInMillis).toString())
.build()
return when (Preferences.calendarAppPackage) {
DEFAULT_OPTION -> {
Intent(Intent.ACTION_VIEW).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
data = calendarUri
}
}
@ -111,6 +117,8 @@ object IntentHelper {
val pm: PackageManager = context.packageManager
try {
pm.getLaunchIntentForPackage(Preferences.calendarAppPackage)!!.apply {
addCategory(Intent.CATEGORY_LAUNCHER)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
action = Intent.ACTION_VIEW
data = calendarUri
}
@ -177,7 +185,7 @@ object IntentHelper {
}
}
false -> {
getCalendarIntent(context)
getCalendarIntent(context, e.startDate)
}
}
}
@ -209,7 +217,7 @@ object IntentHelper {
}
fun getBatteryIntent(): Intent {
return Intent(Intent.ACTION_POWER_USAGE_SUMMARY)
return Intent(Intent.ACTION_POWER_USAGE_SUMMARY).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
fun getMusicIntent(context: Context): Intent {
@ -222,6 +230,7 @@ object IntentHelper {
try {
pm.getLaunchIntentForPackage(Preferences.mediaPlayerPackage)!!.apply {
addCategory(Intent.CATEGORY_LAUNCHER)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
} catch (e: Exception) {
Intent()
@ -235,6 +244,7 @@ object IntentHelper {
return try {
pm.getLaunchIntentForPackage("com.google.android.apps.fitness")!!.apply {
addCategory(Intent.CATEGORY_LAUNCHER)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
} catch (e: Exception) {
Intent()
@ -246,6 +256,7 @@ object IntentHelper {
return try {
pm.getLaunchIntentForPackage(Preferences.lastNotificationPackage)!!.apply {
addCategory(Intent.CATEGORY_LAUNCHER)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
} catch (e: Exception) {
Intent()

View File

@ -88,19 +88,17 @@ object SettingsStringHelper {
fun getDifferenceText(context: Context, now: Long, start: Long): String {
val nowDate = DateTime(now)
val eventDate = DateTime(start)
var difference = start - now
difference += 60 * 1000 - (difference % (60 * 1000))
val difference = start - now
when {
difference <= 0 -> {
return ""
}
TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.HIGH.rawValue && TimeUnit.MILLISECONDS.toMinutes(difference) > 5 -> {
return DateUtils.getRelativeTimeSpanString(start, start - 1000 * 60 * (TimeUnit.MILLISECONDS.toMinutes(difference) - 1 - (TimeUnit.MILLISECONDS.toMinutes(difference) - 1) % 5), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString()
TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.HIGH.rawValue && TimeUnit.MILLISECONDS.toMinutes(difference) >= 5 -> {
return DateUtils.getRelativeTimeSpanString(start, start - 1000 * 60 * (TimeUnit.MILLISECONDS.toMinutes(difference) - TimeUnit.MILLISECONDS.toMinutes(difference) % 5), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString()
}
TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.DEFAULT.rawValue && TimeUnit.MILLISECONDS.toMinutes(difference) > 5 -> {
return DateUtils.getRelativeTimeSpanString(start, start - 1000 * 60 * (TimeUnit.MILLISECONDS.toMinutes(difference) - 1 - (TimeUnit.MILLISECONDS.toMinutes(difference) - 1) % 15), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString()
TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.DEFAULT.rawValue && TimeUnit.MILLISECONDS.toMinutes(difference) >= 15 -> {
return DateUtils.getRelativeTimeSpanString(start, start - 1000 * 60 * (TimeUnit.MILLISECONDS.toMinutes(difference) - TimeUnit.MILLISECONDS.toMinutes(difference) % 15), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString()
}
TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.LOW.rawValue -> {
return context.getString(R.string.soon)
@ -109,22 +107,7 @@ object SettingsStringHelper {
return context.getString(R.string.now)
}
TimeUnit.MILLISECONDS.toHours(difference) < 12 -> {
val minutes = TimeUnit.MILLISECONDS.toMinutes(difference) - 60 * TimeUnit.MILLISECONDS.toHours(difference)
return if (minutes < 1 || minutes > 30) {
DateUtils.getRelativeTimeSpanString(
start,
now - 1000 * 60 * 40,
DateUtils.HOUR_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE
).toString()
} else {
DateUtils.getRelativeTimeSpanString(
start,
now,
DateUtils.HOUR_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE
).toString()
}
return DateUtils.getRelativeTimeSpanString(start, now, DateUtils.HOUR_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString()
}
eventDate.dayOfYear == nowDate.plusDays(1).dayOfYear -> {
return String.format("%s", context.getString(R.string.tomorrow))
@ -143,9 +126,6 @@ object SettingsStringHelper {
val nowDate = DateTime(now)
val eventDate = DateTime(start)
var difference = start - now
difference += 60 * 1000 - (difference % (60 * 1000))
return when (eventDate.dayOfYear) {
nowDate.dayOfYear -> {
""

View File

@ -21,11 +21,20 @@ object WeatherHelper {
suspend fun updateWeather(context: Context) {
Kotpref.init(context)
val networkApi = WeatherNetworkApi(context)
if (Preferences.customLocationAdd != "") {
networkApi.updateWeather()
} else if (context.checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
WeatherNetworkApi(context).updateWeather()
} else if (context.checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION) &&
(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R ||
context.checkGrantedPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION))
) {
LocationService.requestNewLocation(context)
} else {
Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_missing_location)
Preferences.weatherProviderError = ""
removeWeather(context)
org.greenrobot.eventbus.EventBus.getDefault().post(
com.tommasoberlose.anotherwidget.ui.fragments.MainFragment.UpdateUiMessageEvent()
)
}
}
@ -313,92 +322,109 @@ object WeatherHelper {
}
}
fun getWeatherGovIcon(iconString: String, isDaytime: Boolean): String = when {
iconString.contains("skc") -> "01"
iconString.contains("few") -> "02"
iconString.contains("sct") -> "03"
iconString.contains("bkn") -> "04"
iconString.contains("ovc") -> "04"
iconString.contains("wind_skc") -> "01"
iconString.contains("wind_few") -> "02"
iconString.contains("wind_sct") -> "03"
iconString.contains("wind_bkn") -> "04"
iconString.contains("wind_ovc") -> "04"
iconString.contains("snow") -> "13"
iconString.contains("rain_snow") -> "81"
iconString.contains("rain_sleet") -> "81"
iconString.contains("snow_sleet") -> "81"
iconString.contains("fzra") -> "81"
iconString.contains("rain_fzra") -> "81"
iconString.contains("snow_fzra") -> "81"
iconString.contains("sleet") -> "81"
iconString.contains("rain") -> "10"
iconString.contains("rain_showers") -> "10"
iconString.contains("rain_showers_hi") -> "10"
iconString.contains("tsra") -> "82"
iconString.contains("tsra_sct") -> "82"
iconString.contains("tsra_hi") -> "82"
iconString.contains("tornado") -> "80"
iconString.contains("hurricane") -> "80"
iconString.contains("tropical_storm") -> "09"
iconString.contains("dust") -> "Dust"
iconString.contains("smoke") -> "Smoke"
iconString.contains("haze") -> "50"
iconString.contains("hot") -> "01"
iconString.contains("cold") -> "13"
iconString.contains("blizzard") -> "80"
iconString.contains("fog") -> "82"
fun getWeatherLabel(context: Context, icon: String): String {
return when (icon) {
"01d", "01n" -> context.getString(R.string.weather_label_clear)
"02d", "02n" -> context.getString(R.string.weather_label_partly_cloudy)
"03d", "03n" -> context.getString(R.string.weather_label_mostly_cloudy)
"04d", "04n" -> context.getString(R.string.weather_label_cloudy_weather)
"09d", "09n" -> context.getString(R.string.weather_label_storm_weather)
"10d", "10n" -> context.getString(R.string.weather_label_rainy)
"11d", "11n" -> context.getString(R.string.weather_label_thunder)
"13d", "13n" -> context.getString(R.string.weather_label_snow)
"50d", "50n", "82d", "82n" -> context.getString(R.string.weather_label_haze)
"80d", "80n" -> context.getString(R.string.weather_label_windy)
"81d", "81n" -> context.getString(R.string.weather_label_rain_snow)
else -> context.getString(R.string.weather_label_unknown)
}
}
fun getWeatherGovIcon(iconString: String, isDaytime: Boolean): String = when (iconString.substringBefore('?').substringAfterLast('/')) {
"skc" -> "01"
"few" -> "02"
"sct" -> "02"
"bkn" -> "03"
"ovc" -> "04"
"wind_skc" -> "01"
"wind_few" -> "02"
"wind_sct" -> "02"
"wind_bkn" -> "03"
"wind_ovc" -> "04"
"snow" -> "13"
"rain_snow" -> "81"
"rain_sleet" -> "81"
"snow_sleet" -> "81"
"fzra" -> "81"
"rain_fzra" -> "81"
"snow_fzra" -> "81"
"sleet" -> "81"
"rain" -> "10"
"rain_showers" -> "10"
"rain_showers_hi" -> "10"
"tsra" -> "09"
"tsra_sct" -> "11"
"tsra_hi" -> "11"
"tornado" -> "80"
"hurricane" -> "80"
"tropical_storm" -> "09"
"dust" -> "50"
"smoke" -> "50"
"haze" -> "50"
"hot" -> "01"
"cold" -> "13"
"blizzard" -> "13"
"fog" -> "82"
else -> ""
} + if (isDaytime) "d" else "n"
fun getWeatherBitIcon(iconString: String): String = when {
iconString.contains("t01") -> "11"
iconString.contains("t02") -> "09"
iconString.contains("t03") -> "09"
iconString.contains("t04") -> "09"
iconString.contains("t05") -> "09"
iconString.contains("d01") -> "10"
iconString.contains("d02") -> "10"
iconString.contains("d03") -> "10"
iconString.contains("r01") -> "10"
iconString.contains("r02") -> "10"
iconString.contains("r03") -> "10"
iconString.contains("f01") -> "10"
iconString.contains("r04") -> "10"
iconString.contains("r05") -> "10"
iconString.contains("r06") -> "10"
iconString.contains("s01") -> "13"
iconString.contains("s02") -> "13"
iconString.contains("s03") -> "13"
iconString.contains("s04") -> "81"
iconString.contains("s05") -> "90"
iconString.contains("s06") -> "13"
iconString.contains("a01") -> "82"
iconString.contains("a02") -> "82"
iconString.contains("a03") -> "82"
iconString.contains("a04") -> "82"
iconString.contains("a05") -> "82"
iconString.contains("a06") -> "82"
iconString.contains("c01") -> "01"
iconString.contains("c02") -> "02"
iconString.contains("c03") -> "04"
iconString.contains("c04") -> "04"
fun getWeatherBitIcon(iconString: String): String = when (iconString.substring(0, 3)) {
"t01" -> "11"
"t02" -> "11"
"t03" -> "09"
"t04" -> "11"
"t05" -> "11"
"d01" -> "10"
"d02" -> "10"
"d03" -> "10"
"r01" -> "10"
"r02" -> "10"
"r03" -> "10"
"f01" -> "10"
"r04" -> "10"
"r05" -> "10"
"r06" -> "10"
"s01" -> "13"
"s02" -> "13"
"s03" -> "13"
"s04" -> "81"
"s05" -> "81"
"s06" -> "13"
"a01" -> "50"
"a02" -> "50"
"a03" -> "50"
"a04" -> "50"
"a05" -> "82"
"a06" -> "82"
"c01" -> "01"
"c02" -> "02"
"c03" -> "03"
"c04" -> "04"
else -> ""
} + if (iconString.contains("d")) "d" else "n"
} + iconString.substring(3)
fun getWeatherApiIcon(icon: Int, isDaytime: Boolean): String = when(icon) {
1000 -> "01"
1003 -> "02"
1006 -> "03"
1009 -> "04"
1030 -> "82"
1030 -> "50"
1063 -> "10"
1066 -> "10"
1069 -> "10"
1066 -> "13"
1069 -> "81"
1072 -> "81"
1087 -> "11"
1114 -> "13"
1117 -> "09"
1117 -> "13"
1135 -> "82"
1147 -> "82"
1150 -> "10"
@ -413,8 +439,8 @@ object WeatherHelper {
1195 -> "10"
1198 -> "81"
1201 -> "81"
1204 -> "13"
1207 -> "13"
1204 -> "81"
1207 -> "81"
1210 -> "13"
1213 -> "13"
1216 -> "13"
@ -425,62 +451,62 @@ object WeatherHelper {
1240 -> "10"
1243 -> "10"
1246 -> "10"
1249 -> "13"
1252 -> "13"
1249 -> "81"
1252 -> "81"
1255 -> "13"
1258 -> "13"
1261 -> "13"
1264 -> "13"
1273 -> "09"
1273 -> "11"
1276 -> "09"
1279 -> "13"
1282 -> "13"
else -> ""
} + if (isDaytime) "d" else "n"
fun getYRIcon(iconCode: String, isDaytime: Boolean): String = when {
iconCode.contains("clearsky") -> "01"
iconCode.contains("cloudy") -> "04"
iconCode.contains("fair") -> "02"
iconCode.contains("fog") -> "82"
iconCode.contains("heavyrain") -> "10"
iconCode.contains("heavyrainandthunder") -> "11"
iconCode.contains("heavyrainshowers") -> "10"
iconCode.contains("heavyrainshowersandthunder") -> "11"
iconCode.contains("heavysleet") -> "10"
iconCode.contains("heavysleetandthunder") -> "11"
iconCode.contains("heavysleetshowers") -> "10"
iconCode.contains("heavysleetshowersandthunder") -> "11"
iconCode.contains("heavysnow") -> "13"
iconCode.contains("heavysnowandthunder") -> "13"
iconCode.contains("heavysnowshowers") -> "13"
iconCode.contains("heavysnowshowersandthunder") -> "13"
iconCode.contains("lightrain") -> "10"
iconCode.contains("lightrainandthunder") -> "11"
iconCode.contains("lightrainshowers") -> "10"
iconCode.contains("lightrainshowersandthunder") -> "11"
iconCode.contains("lightsleet") -> "10"
iconCode.contains("lightsleetandthunder") -> "11"
iconCode.contains("lightsleetshowers") -> "10"
iconCode.contains("lightsnow") -> "13"
iconCode.contains("lightsnowandthunder") -> "13"
iconCode.contains("lightsnowshowers") -> "13"
iconCode.contains("lightssleetshowersandthunder") -> "81"
iconCode.contains("lightssnowshowersandthunder") -> "81"
iconCode.contains("partlycloudy") -> "03"
iconCode.contains("rain") -> "10"
iconCode.contains("rainandthunder") -> "11"
iconCode.contains("rainshowers") -> "10"
iconCode.contains("rainshowersandthunder") -> "11"
iconCode.contains("sleet") -> "10"
iconCode.contains("sleetandthunder") -> "11"
iconCode.contains("sleetshowers") -> "10"
iconCode.contains("sleetshowersandthunder") -> "11"
iconCode.contains("snow") -> "13"
iconCode.contains("snowandthunder") -> "13"
iconCode.contains("snowshowers") -> "13"
iconCode.contains("snowshowersandthunder") -> "13"
fun getYRIcon(iconCode: String): String = when (iconCode.substringBefore('_')) {
"clearsky" -> "01"
"cloudy" -> "04"
"fair" -> "02"
"fog" -> "82"
"heavyrain" -> "10"
"heavyrainandthunder" -> "09"
"heavyrainshowers" -> "10"
"heavyrainshowersandthunder" -> "09"
"heavysleet" -> "81"
"heavysleetandthunder" -> "81"
"heavysleetshowers" -> "81"
"heavysleetshowersandthunder" -> "81"
"heavysnow" -> "13"
"heavysnowandthunder" -> "13"
"heavysnowshowers" -> "13"
"heavysnowshowersandthunder" -> "13"
"lightrain" -> "10"
"lightrainandthunder" -> "11"
"lightrainshowers" -> "10"
"lightrainshowersandthunder" -> "11"
"lightsleet" -> "81"
"lightsleetandthunder" -> "81"
"lightsleetshowers" -> "81"
"lightsnow" -> "13"
"lightsnowandthunder" -> "13"
"lightsnowshowers" -> "13"
"lightssleetshowersandthunder" -> "81"
"lightssnowshowersandthunder" -> "81"
"partlycloudy" -> "03"
"rain" -> "10"
"rainandthunder" -> "11"
"rainshowers" -> "10"
"rainshowersandthunder" -> "11"
"sleet" -> "81"
"sleetandthunder" -> "81"
"sleetshowers" -> "81"
"sleetshowersandthunder" -> "81"
"snow" -> "13"
"snowandthunder" -> "13"
"snowshowers" -> "13"
"snowshowersandthunder" -> "13"
else -> ""
} + if (isDaytime) "d" else "n"
} + if (iconCode.substringAfter('_', "day") == "day") "d" else "n"
}
}

View File

@ -25,8 +25,15 @@ object WidgetHelper {
) {
fun getWidgetsSize(widgetId: Int): Pair<Int, Int> {
val width = getWidgetWidth(widgetId)
val height = getWidgetHeight(widgetId)
val portrait = context.resources.configuration.orientation == ORIENTATION_PORTRAIT
val width = getWidgetSizeInDp(
widgetId,
if (portrait) AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH else AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH
)
val height = getWidgetSizeInDp(
widgetId,
if (portrait) AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT else AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT
)
val widthInPx = context.dip(width)
val heightInPx = context.dip(height)
FirebaseCrashlytics.getInstance().setCustomKey("widthInPx", widthInPx)
@ -63,21 +70,23 @@ object WidgetHelper {
R.array.com_google_android_gms_fonts_certs
)
val handlerThread = HandlerThread("generateView")
val callback = object : FontsContractCompat.FontRequestCallback() {
override fun onTypefaceRetrieved(typeface: Typeface) {
handlerThread.quit()
function.invoke(typeface)
}
override fun onTypefaceRequestFailed(reason: Int) {
handlerThread.quit()
function.invoke(null)
}
}
val handlerThread = HandlerThread("generateView")
handlerThread.start()
if (Looper.myLooper() == null) {
Looper.prepare()
}
//if (Looper.myLooper() == null) {
// Looper.prepare()
//}
Handler(handlerThread.looper).run {
FontsContractCompat.requestFont(context, request, callback, this)

View File

@ -1,26 +1,35 @@
package com.tommasoberlose.anotherwidget.models
import android.provider.CalendarContract
import io.realm.RealmObject
import java.util.Date
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
/**
* Created by tommaso on 05/10/17.
*/
open class Event(
var id: Long = 0,
var eventID: Long = 0,
var title: String = "",
var startDate: Long = 0,
var endDate: Long = 0,
var calendarID: Int = 0,
var allDay: Boolean = false,
var address: String = "",
var selfAttendeeStatus: Int = CalendarContract.Attendees.ATTENDEE_STATUS_NONE,
var availability: Int = CalendarContract.EventsEntity.AVAILABILITY_BUSY
) : RealmObject() {
@Entity(tableName = "events")
data class Event(
@PrimaryKey
val id: Long = 0,
@ColumnInfo(name = "event_id")
val eventID: Long = 0,
val title: String = "",
@ColumnInfo(name = "start_date")
val startDate: Long = 0,
@ColumnInfo(name = "end_date")
val endDate: Long = 0,
@ColumnInfo(name = "calendar_id")
val calendarID: Long = 0,
@ColumnInfo(name = "all_day")
val allDay: Boolean = false,
val address: String = "",
@ColumnInfo(name = "self_attendee_status")
val selfAttendeeStatus: Int = CalendarContract.Attendees.ATTENDEE_STATUS_NONE,
val availability: Int = CalendarContract.EventsEntity.AVAILABILITY_BUSY
)/* {
override fun toString(): String {
return "Event:\nEVENT ID: " + eventID + "\nTITLE: " + title + "\nSTART DATE: " + Date(startDate) + "\nEND DATE: " + Date(endDate) + "\nCAL ID: " + calendarID + "\nADDRESS: " + address
}
}
}*/

View File

@ -26,9 +26,7 @@ class TimeZonesApi(val context: Context) {
when (val response = repository.getTimeZone(lat, long)) {
is NetworkResponse.Success -> {
try {
Log.d("ciao", response.body.toString())
id = response.body["timezoneId"] as String
} catch(ex: Exception) {
ex.printStackTrace()
}
@ -37,4 +35,4 @@ class TimeZonesApi(val context: Context) {
return id
}
}
}

View File

@ -42,6 +42,13 @@ 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 = ""
}
WeatherHelper.removeWeather(
context
)
@ -114,25 +121,34 @@ class WeatherNetworkApi(val context: Context) {
val props =
weatherResponse.body["properties"] as LinkedTreeMap<*, *>
val periods = props["periods"] as List<*>
val now = periods[0] as LinkedTreeMap<*, *>
@android.annotation.SuppressLint("SimpleDateFormat")
val format = SimpleDateFormat(
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N)
"yyyy-MM-dd'T'HH:mm:ssXXX"
else
"yyyy-MM-dd'T'HH:mm:ssZ"
)
for (period in periods) {
val now = period as LinkedTreeMap<*, *>
val endTime = format.parse(now["endTime"] as String)!!
if (endTime.time > System.currentTimeMillis()) {
val temp = now["temperature"] as Double
val fullIcon = now["icon"] as String
val isDaytime = now["isDaytime"] as Boolean
val temp = now["temperature"] as Double
val fullIcon = now["icon"] as String
val isDaytime = now["isDaytime"] as Boolean
Preferences.weatherTemp = temp.toFloat()
Preferences.weatherIcon = WeatherHelper.getWeatherGovIcon(fullIcon, isDaytime)
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
MainWidget.updateWidget(context)
Preferences.weatherTemp = temp.toFloat()
Preferences.weatherIcon = WeatherHelper.getWeatherGovIcon(fullIcon, isDaytime)
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
Preferences.weatherProviderError = ""
Preferences.weatherProviderLocationError = ""
MainWidget.updateWidget(context)
Preferences.weatherProviderError = ""
Preferences.weatherProviderLocationError = ""
break
}
}
} catch (ex: Exception) {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
Preferences.weatherProviderLocationError = ""
} finally {
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
}
}
else -> {
@ -148,14 +164,16 @@ class WeatherNetworkApi(val context: Context) {
}
}
is NetworkResponse.ServerError -> {
if (pointsResponse.body?.containsKey("status") == true && (pointsResponse.body?.get("status") as Double).toInt() == 404) {
Preferences.weatherProviderError = ""
Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_wrong_location)
} else {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
Preferences.weatherProviderLocationError = ""
when (pointsResponse.code) {
404 -> {
Preferences.weatherProviderError = ""
Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_wrong_location)
}
else -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
Preferences.weatherProviderLocationError = ""
}
}
WeatherHelper.removeWeather(
context
)
@ -176,7 +194,18 @@ class WeatherNetworkApi(val context: Context) {
when (val response = repository.getWeather()) {
is NetworkResponse.Success -> {
try {
Log.d("ciao - here", response.body.toString())
val observations = response.body["observations"] as LinkedTreeMap<*, *>
val location = (observations["location"] as List<*>).first() as LinkedTreeMap<*, *>
val observation = (location["observation"] as List<*>).first() as LinkedTreeMap<*, *>
val iconName = observation["iconName"] as String
val daylight = observation["daylight"] as String
val temperature = observation["temperature"] as String
Preferences.weatherTemp = temperature.toFloat()
Preferences.weatherIcon = repository.getWeatherIcon(iconName, daylight != "N")
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
MainWidget.updateWidget(context)
Preferences.weatherProviderError = ""
Preferences.weatherProviderLocationError = ""
} catch(ex: Exception) {
@ -187,8 +216,16 @@ class WeatherNetworkApi(val context: Context) {
}
}
is NetworkResponse.ServerError -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
Preferences.weatherProviderLocationError = ""
when (response.code) {
401 -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_invalid_key)
Preferences.weatherProviderLocationError = ""
}
else -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
Preferences.weatherProviderLocationError = ""
}
}
WeatherHelper.removeWeather(
context
)
@ -218,10 +255,10 @@ class WeatherNetworkApi(val context: Context) {
when (val response = repository.getWeather()) {
is NetworkResponse.Success -> {
try {
val data = response.body["data"] as List<LinkedTreeMap<String, Any>>?
data?.first()?.let {
val data = response.body["data"] as List<*>?
data?.first()?.let { it as LinkedTreeMap<*, *>
val temp = it["temp"] as Double
val weatherInfo = it["weather"] as LinkedTreeMap<String, Any>
val weatherInfo = it["weather"] as LinkedTreeMap<*, *>
val iconCode = weatherInfo["icon"] as String
Preferences.weatherTemp = temp.toFloat()
@ -231,8 +268,6 @@ class WeatherNetworkApi(val context: Context) {
Preferences.weatherProviderError = ""
Preferences.weatherProviderLocationError = ""
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
}
} catch(ex: Exception) {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
@ -281,12 +316,12 @@ class WeatherNetworkApi(val context: Context) {
when (val response = repository.getWeather()) {
is NetworkResponse.Success -> {
try {
val current = response.body["current"] as LinkedTreeMap<String, Any>?
val current = response.body["current"] as LinkedTreeMap<*, *>?
current?.let {
val tempC = current["temp_c"] as Double
val tempF = current["temp_f"] as Double
val isDay = current["is_day"] as Double
val condition = current["condition"] as LinkedTreeMap<String, Any>
val condition = current["condition"] as LinkedTreeMap<*, *>
val iconCode = condition["code"] as Double
Preferences.weatherTemp = if (Preferences.weatherTempUnit == "F") tempF.toFloat() else tempC.toFloat()
@ -296,8 +331,6 @@ class WeatherNetworkApi(val context: Context) {
Preferences.weatherProviderError = ""
Preferences.weatherProviderLocationError = ""
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
}
} catch(ex: Exception) {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
@ -346,28 +379,74 @@ class WeatherNetworkApi(val context: Context) {
private suspend fun useAccuweatherProvider(context: Context) {
if (Preferences.weatherProviderApiAccuweather != "") {
// val repository = AccuweatherRepository()
val repository = AccuweatherRepository()
// when (val response = repository.getWeather()) {
// is NetworkResponse.Success -> {
// try {
// Log.d("ciao", response.body.toString())
// } catch(ex: Exception) {
//
// Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
// Preferences.weatherProviderLocationError = ""
// }
// }
// is NetworkResponse.ServerError -> {
// WeatherHelper.removeWeather(
// context
// )
// }
// Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
// Preferences.weatherProviderLocationError = ""
// EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
// }
when (val locationResponse = repository.getLocation()) {
is NetworkResponse.Success -> {
try {
val key = locationResponse.body["Key"] as String
when (val weatherResponse = repository.getWeather(key)) {
is NetworkResponse.Success -> {
try {
weatherResponse.body.first().let {
val temp = it["Temperature"] as LinkedTreeMap<*, *>
val tempC = (temp["Metric"] as LinkedTreeMap<*, *>)["Value"] as Double
val tempF = (temp["Imperial"] as LinkedTreeMap<*, *>)["Value"] as Double
val isDay = it["IsDayTime"] as Boolean
val icon = it["WeatherIcon"] as Double
Preferences.weatherTemp = if (Preferences.weatherTempUnit == "F") tempF.toFloat() else tempC.toFloat()
Preferences.weatherIcon = repository.getWeatherIcon(icon.toInt(), isDay)
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
MainWidget.updateWidget(context)
}
Preferences.weatherProviderError = ""
Preferences.weatherProviderLocationError = ""
} catch (ex: Exception) {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
Preferences.weatherProviderLocationError = ""
}
}
else -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
Preferences.weatherProviderLocationError = ""
}
}
} catch(ex: Exception) {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
Preferences.weatherProviderLocationError = ""
} finally {
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
}
}
is NetworkResponse.ServerError -> {
when (locationResponse.code) {
401 -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_invalid_key)
Preferences.weatherProviderLocationError = ""
}
503 -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_expired_key)
Preferences.weatherProviderLocationError = ""
}
else -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
Preferences.weatherProviderLocationError = ""
}
}
WeatherHelper.removeWeather(
context
)
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
}
else -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
Preferences.weatherProviderLocationError = ""
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
}
}
} else {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
Preferences.weatherProviderLocationError = ""
@ -386,38 +465,27 @@ class WeatherNetworkApi(val context: Context) {
is NetworkResponse.Success -> {
try {
val pp = response.body["properties"] as LinkedTreeMap<*, *>
val data = pp["timeseries"] as List<LinkedTreeMap<String, Any>>?
data?.let {
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
for (item in data) {
val time = Calendar.getInstance().apply { time = format.parse(item["time"] as String)!! }
val now = Calendar.getInstance()
if (time.timeInMillis >= now.timeInMillis) {
val dd = item["data"] as LinkedTreeMap<*, *>
val instant = dd["instant"] as LinkedTreeMap<*, *>
val next = dd["next_1_hours"] as LinkedTreeMap<*, *>
val data = pp["timeseries"] as List<*>?
data?.first()?.let { it as LinkedTreeMap<*, *>
val dd = it["data"] as LinkedTreeMap<*, *>
val instant = dd["instant"] as LinkedTreeMap<*, *>
val next = dd["next_1_hours"] as LinkedTreeMap<*, *>
val details = instant["details"] as LinkedTreeMap<*, *>
val temp = details["air_temperature"] as Double
val details = instant["details"] as LinkedTreeMap<*, *>
val temp = details["air_temperature"] as Double
val summary = next["summary"] as LinkedTreeMap<*, *>
val iconCode = summary["symbol_code"] as String
val summary = next["summary"] as LinkedTreeMap<*, *>
val iconCode = summary["symbol_code"] as String
Preferences.weatherTemp = temp.toFloat()
Preferences.weatherIcon = WeatherHelper.getYRIcon(iconCode, now.get(Calendar.HOUR_OF_DAY) >= 22 || now.get(Calendar.HOUR_OF_DAY) <= 8)
Preferences.weatherTempUnit = "C"
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
MainWidget.updateWidget(context)
Preferences.weatherTemp = temp.toFloat()
Preferences.weatherIcon = WeatherHelper.getYRIcon(iconCode)
Preferences.weatherTempUnit = "C"
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
MainWidget.updateWidget(context)
Preferences.weatherProviderError = ""
Preferences.weatherProviderLocationError = ""
break
}
}
Preferences.weatherProviderError = ""
Preferences.weatherProviderLocationError = ""
}
} catch(ex: Exception) {
ex.printStackTrace()
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
@ -441,4 +509,4 @@ class WeatherNetworkApi(val context: Context) {
}
}
}
}
}

View File

@ -13,7 +13,7 @@ object ApiServices {
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
@Headers("User-Agent: (Another Widget, tommaso.berlose@gmail.com)")
@GET("gridpoints/{gridId}/{gridX},{gridY}/forecast")
@GET("gridpoints/{gridId}/{gridX},{gridY}/forecast/hourly")
suspend fun getWeather(
@Path("gridId") gridId: String,
@Path("gridX") gridX: Int,
@ -54,13 +54,17 @@ object ApiServices {
}
interface AccuweatherService {
@GET("")
suspend fun getWeather(
@Path("gridId") gridId: String,
@Path("gridX") gridX: Int,
@Path("gridY") gridY: Int,
@Query("units") unit: String
@GET("locations/v1/cities/geoposition/search")
suspend fun getLocation(
@Query("apikey") apikey: String,
@Query("q") location: String
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
@GET("currentconditions/v1/{locationKey}")
suspend fun getWeather(
@Path("locationKey") locationKey: String,
@Query("apikey") apikey: String
): NetworkResponse<List<HashMap<String, Any>>, HashMap<String, Any>>
}
interface YrService {
@ -80,4 +84,4 @@ object ApiServices {
@Query("username") username: String = "tommaso.berlose",
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
}
}
}

View File

@ -1,6 +1,7 @@
package com.tommasoberlose.anotherwidget.network.repository
import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.network.api.ApiServices
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
@ -9,10 +10,11 @@ class AccuweatherRepository {
/* ACCUWEATHER */
private val apiServiceAccu: ApiServices.AccuweatherService = getRetrofit().create(ApiServices.AccuweatherService::class.java)
suspend fun getWeather(): Nothing = TODO()
suspend fun getLocation() = apiServiceAccu.getLocation(Preferences.weatherProviderApiAccuweather, "${Preferences.customLocationLat},${Preferences.customLocationLon}")
suspend fun getWeather(locationKey: String) = apiServiceAccu.getWeather(locationKey, Preferences.weatherProviderApiAccuweather)
companion object {
private const val BASE_URL_ACCU = ""
private const val BASE_URL_ACCU = "https://dataservice.accuweather.com/"
private fun getRetrofit(): Retrofit {
return Retrofit.Builder()
@ -22,4 +24,20 @@ class AccuweatherRepository {
.build()
}
}
}
fun getWeatherIcon(icon: Int, isDaytime: Boolean): String = when(icon) {
1, 2, 30, 33, 34 -> "01"
3, 4, 35, 36 -> "02"
5, 37 -> "50"
6, 38 -> "03"
7, 8 -> "04"
11 -> "82"
12, 13, 14, 18, 39, 40 -> "10"
15 -> "09"
16, 17, 41, 42 -> "11"
32 -> "80"
19, 20, 21, 22, 23, 24, 31, 43, 44 -> "13"
25, 26, 29 -> "81"
else -> ""
} + if (isDaytime) "d" else "n"
}

View File

@ -23,4 +23,157 @@ class HereRepository {
.build()
}
}
}
fun getWeatherIcon(iconName: String, isDaytime: Boolean): String = when(iconName.substringAfter("night_")) {
"sunny" -> "01"
"clear" -> "01"
"mostly_sunny" -> "01"
"mostly_clear" -> "01"
"passing_clounds" -> "02"
"more_sun_than_clouds" -> "02"
"scattered_clouds" -> "02"
"partly_cloudy" -> "02"
"a_mixture_of_sun_and_clouds" -> "03"
"increasing_cloudiness" -> "03"
"breaks_of_sun_late" -> "03"
"afternoon_clouds" -> "03"
"morning_clouds" -> "03"
"partly_sunny" -> "03"
"high_level_clouds" -> "03"
"decreasing_cloudiness" -> "03"
"clearing_skies" -> "01"
"high_clouds" -> "03"
"rain_early" -> "10"
"heavy_rain_early" -> "10"
"strong_thunderstorms" -> "09"
"severe_thunderstorms" -> "09"
"thundershowers" -> "11"
"thunderstorms" -> "11"
"tstorms_early" -> "11"
"isolated_tstorms_late" -> "11"
"scattered_tstorms_late" -> "11"
"tstorms_late" -> "11"
"tstorms" -> "11"
"ice_fog" -> "82"
"more_clouds_than_sun" -> "03"
"broken_clouds" -> "03"
"scattered_showers" -> "10"
"a_few_showers" -> "10"
"light_showers" -> "10"
"passing_showers" -> "10"
"rain_showers" -> "10"
"showers" -> "10"
"widely_scattered_tstorms" -> "11"
"isolated_tstorms" -> "11"
"a_few_tstorms" -> "11"
"scattered_tstorms" -> "11"
"hazy_sunshine" -> "50"
"haze" -> "50"
"smoke" -> "50"
"low_level_haze" -> "50"
"early_fog_followed_by_sunny_skies" -> "50"
"early_fog" -> "82"
"light_fog" -> "82"
"fog" -> "82"
"dense_fog" -> "82"
//"night_haze"
//"night_smoke"
//"night_low_level_haze"
//"night_widely_scattered_tstorms"
//"night_isolated_tstorms"
//"night_a_few_tstorms"
//"night_scattered_tstorms"
//"night_tstorms"
//"night_clear"
"mostly_cloudy" -> "03"
"cloudy" -> "04"
"overcast" -> "04"
"low_clouds" -> "03"
"hail" -> "10"
"sleet" -> "81"
"light_mixture_of_precip" -> "81"
"icy_mix" -> "81"
"mixture_of_precip" -> "81"
"heavy_mixture_of_precip" -> "81"
"snow_changing_to_rain" -> "81"
"snow_changing_to_an_icy_mix" -> "81"
"an_icy_mix_changing_to_snow" -> "81"
"an_icy_mix_changing_to_rain" -> "81"
"rain_changing_to_snow" -> "81"
"rain_changing_to_an_icy_mix" -> "81"
"light_icy_mix_early" -> "81"
"icy_mix_early" -> "81"
"light_icy_mix_late" -> "81"
"icy_mix_late" -> "81"
"snow_rain_mix" -> "81"
"scattered_flurries" -> "13"
"snow_flurries" -> "13"
"light_snow_showers" -> "13"
"snow_showers" -> "13"
"light_snow" -> "13"
"flurries_early" -> "13"
"snow_showers_early" -> "13"
"light_snow_early" -> "13"
"flurries_late" -> "13"
"snow_showers_late" -> "13"
"light_snow_late" -> "13"
//"night_decreasing_cloudiness"
//"night_clearing_skies"
//"night_high_level_clouds"
//"night_high_clouds"
//"night_scattered_showers"
//"night_a_few_showers"
//"night_light_showers"
//"night_passing_showers"
//"night_rain_showers"
//"night_sprinkles"
//"night_showers"
//"night_mostly_clear"
//"night_passing_clouds"
//"night_scattered_clouds"
//"night_partly_cloudy"
//"increasing_cloudiness"
//"night_afternoon_clouds"
//"night_morning_clouds"
//"night_broken_clouds"
//"night_mostly_cloudy"
"light_freezing_rain" -> "81"
"freezing_rain" -> "81"
"heavy_rain" -> "10"
"lots_of_rain" -> "10"
"tons_of_rain" -> "10"
//"heavy_rain_early" -> "10"
"heavy_rain_late" -> "10"
"flash_floods" -> "10"
"flood" -> "10"
"drizzle" -> "10"
"sprinkles" -> "10"
"light_rain" -> "10"
"sprinkles_early" -> "10"
"light_rain_early" -> "10"
"sprinkles_late" -> "10"
"light_rain_late" -> "10"
"rain" -> "10"
"numerous_showers" -> "10"
"showery" -> "10"
"showers_early" -> "10"
//"rain_early" -> "10"
"showers_late" -> "10"
"rain_late" -> "10"
"snow" -> "13"
"moderate_snow" -> "13"
"snow_early" -> "13"
"snow_late" -> "13"
"heavy_snow" -> "13"
"heavy_snow_early" -> "13"
"heavy_snow_late" -> "13"
"tornado" -> "80"
"tropical_storm" -> "09"
"hurricane" -> "80"
"sandstorm" -> "50"
"duststorm" -> "50"
"snowstorm" -> "13"
"blizzard" -> "13"
else -> ""
} + if (isDaytime) "d" else "n"
}

View File

@ -13,8 +13,7 @@ class NewCalendarEventReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val eventRepository = EventRepository(context)
when (intent.action) {
Intent.ACTION_PROVIDER_CHANGED,
Intent.ACTION_TIME_CHANGED -> {
Intent.ACTION_PROVIDER_CHANGED -> {
CalendarHelper.updateEventList(context)
}
Actions.ACTION_GO_TO_NEXT_EVENT -> {

View File

@ -38,6 +38,7 @@ class UpdatesReceiver : BroadcastReceiver() {
"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)) {
@ -49,9 +50,6 @@ class UpdatesReceiver : BroadcastReceiver() {
ActiveNotificationsHelper.clearLastNotification(context)
MainWidget.updateWidget(context)
}
Actions.ACTION_UPDATE_GREETINGS -> {
MainWidget.updateWidget(context)
}
Actions.ACTION_REFRESH -> {
GlobalScope.launch(Dispatchers.IO) {
@ -67,15 +65,37 @@ class UpdatesReceiver : BroadcastReceiver() {
const val EVENT_ID = "EVENT_ID"
fun setUpdates(context: Context, eventId: Long? = null) {
if (!Preferences.showEvents)
return
val eventRepository = EventRepository(context)
if (eventId == null) {
removeUpdates(context)
// schedule ACTION_CALENDAR_UPDATE at midnight (ACTION_DATE_CHANGED no longer works)
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
setExact(
AlarmManager.RTC,
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)
}.timeInMillis,
PendingIntent.getBroadcast(
context,
0,
Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_CALENDAR_UPDATE
},
0
)
)
}
eventRepository.getFutureEvents().forEach { event ->
setEventUpdate(context, event)
}
} else {
val event = eventRepository.getEventByEventId(eventId)
val event = eventRepository.getEventById(eventId)
if (event != null) {
setEventUpdate(context, event)
}
@ -84,109 +104,90 @@ class UpdatesReceiver : BroadcastReceiver() {
}
private fun setEventUpdate(context: Context, event: Event) {
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
val now = Calendar.getInstance().apply {
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
val diff = Period(now.timeInMillis, event.startDate)
val limit = when (Preferences.showUntil) {
0 -> 1000 * 60 * 60 * 3
1 -> 1000 * 60 * 60 * 6
2 -> 1000 * 60 * 60 * 12
3 -> 1000 * 60 * 60 * 24
4 -> 1000 * 60 * 60 * 24 * 3
5 -> 1000 * 60 * 60 * 24 * 7
6 -> 1000 * 60 * 30
7 -> 1000 * 60 * 60
else -> 1000 * 60 * 60 * 6
}
if (event.startDate <= limit) {
if (event.startDate > now.timeInMillis) {
// Update the widget every hour till the event
if (diff.hours == 0) {
var minutes = 0
when (Preferences.widgetUpdateFrequency) {
Constants.WidgetUpdateFrequency.DEFAULT.rawValue -> {
minutes = when {
diff.minutes > 50 -> 50
diff.minutes > 30 -> 30
diff.minutes > 15 -> 15
else -> 0
}
}
Constants.WidgetUpdateFrequency.HIGH.rawValue -> {
minutes = diff.minutes - (diff.minutes % 5)
}
val now = Calendar.getInstance().apply {
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
val diff = Period(now.timeInMillis, event.startDate, org.joda.time.PeriodType.time())
val limit = when (Preferences.showUntil) {
0 -> 1000 * 60 * 60 * 3
1 -> 1000 * 60 * 60 * 6
2 -> 1000 * 60 * 60 * 12
3 -> 1000 * 60 * 60 * 24
4 -> 1000 * 60 * 60 * 24 * 3
5 -> 1000 * 60 * 60 * 24 * 7
6 -> 1000 * 60 * 30
7 -> 1000 * 60 * 60
else -> 1000 * 60 * 60 * 6
}
val fireTime = when {
event.startDate <= now.timeInMillis
-> event.endDate
event.startDate > now.timeInMillis + limit
-> event.startDate - limit
!Preferences.showDiffTime
-> return
event.allDay
-> event.startDate
diff.hours > 12
-> event.startDate - 12 * 1000 * 60 * 60 + 1000 * 60
diff.hours > 0
-> event.startDate - diff.hours * 1000 * 60 * 60 + 1000 * 60
else
-> event.startDate - 1000 * 60 * when (Preferences.widgetUpdateFrequency) {
Constants.WidgetUpdateFrequency.DEFAULT.rawValue -> {
when {
diff.minutes >= 45 -> 44
diff.minutes >= 30 -> 29
diff.minutes >= 15 -> 14
else -> 0
}
setExact(
AlarmManager.RTC,
if (event.startDate - minutes * 1000 * 60 > (now.timeInMillis + 120 * 1000)) event.startDate - 60 * 1000 * minutes else now.timeInMillis + 120000,
PendingIntent.getBroadcast(
context,
event.eventID.toInt(),
Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE
putExtra(EVENT_ID, event.eventID)
},
PendingIntent.FLAG_UPDATE_CURRENT
)
)
} else {
setExact(
AlarmManager.RTC,
event.startDate - diff.hours * 1000 * 60 * 60 + if (diff.minutes > 30) (-30) else (+30),
PendingIntent.getBroadcast(
context,
event.eventID.toInt(),
Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE
putExtra(EVENT_ID, event.eventID)
},
PendingIntent.FLAG_UPDATE_CURRENT
)
)
}
} else {
// Update the widget one second after the event is finished
val fireTime =
if (event.endDate > now.timeInMillis + 120 * 1000) event.endDate else now.timeInMillis + 120000
setExact(
AlarmManager.RTC,
fireTime,
PendingIntent.getBroadcast(
context,
event.eventID.toInt(),
Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE
},
0
)
)
Constants.WidgetUpdateFrequency.HIGH.rawValue -> {
when {
diff.minutes >= 5 -> diff.minutes - diff.minutes % 5 - 1
else -> 0
}
}
else -> 0
}
} else {
setExact(
AlarmManager.RTC,
if (event.startDate - limit > now.timeInMillis + 120 * 1000) event.startDate - limit else now.timeInMillis + 120000,
PendingIntent.getBroadcast(
context,
event.eventID.toInt(),
Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE
putExtra(EVENT_ID, event.eventID)
},
PendingIntent.FLAG_UPDATE_CURRENT
)
}
// no need to schedule updates after the next ACTION_CALENDAR_UPDATE
if (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)
}.timeInMillis <= fireTime) return
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
setExact(
AlarmManager.RTC,
fireTime.coerceAtLeast(now.timeInMillis + 1000 * 60),
PendingIntent.getBroadcast(
context,
event.id.toInt(),
Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE
if (event.startDate > now.timeInMillis)
putExtra(EVENT_ID, event.id)
},
PendingIntent.FLAG_UPDATE_CURRENT
)
}
)
}
}
fun removeUpdates(context: Context) {
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))
val eventRepository = EventRepository(context)
eventRepository.getFutureEvents().forEach {
cancel(PendingIntent.getBroadcast(context, it.eventID.toInt(), Intent(context, UpdatesReceiver::class.java), 0))
cancel(PendingIntent.getBroadcast(context, it.id.toInt(), Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE
}, 0))
}
eventRepository.close()
}

View File

@ -22,21 +22,14 @@ class WeatherReceiver : BroadcastReceiver() {
Intent.ACTION_MY_PACKAGE_REPLACED,
Intent.ACTION_TIMEZONE_CHANGED,
Intent.ACTION_LOCALE_CHANGED,
Intent.ACTION_TIME_CHANGED -> setUpdates(context)
Actions.ACTION_WEATHER_UPDATE -> {
GlobalScope.launch(Dispatchers.IO) {
WeatherHelper.updateWeather(context)
}
}
Intent.ACTION_TIME_CHANGED,
Actions.ACTION_WEATHER_UPDATE -> setUpdates(context)
}
}
companion object {
private const val MINUTE = 60 * 1000L
fun setUpdates(context: Context) {
removeUpdates(context)
if (Preferences.showWeather) {
val interval = MINUTE * when (Preferences.weatherRefreshPeriod) {
0 -> 30
@ -48,13 +41,15 @@ class WeatherReceiver : BroadcastReceiver() {
else -> 60
}
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
setRepeating(
setExact(
AlarmManager.RTC,
Calendar.getInstance().timeInMillis,
interval,
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)
}
}
}

View File

@ -16,14 +16,15 @@ class WidgetClickListenerReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Actions.ACTION_OPEN_WEATHER_INTENT) {
try {
if (Preferences.weatherAppPackage == IntentHelper.REFRESH_WIDGET_OPTION) {
context.sendBroadcast(IntentHelper.getWeatherIntent(context))
} else {
context.startActivity(IntentHelper.getWeatherIntent(context))
IntentHelper.getWeatherIntent(context).run {
if (flags and Intent.FLAG_ACTIVITY_NEW_TASK == Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(this)
else
context.sendBroadcast(this)
}
} catch (e: Exception) {
e.printStackTrace()
val uri = Uri.parse("http://www.google.com/search?q=weather")
val uri = Uri.parse("https://www.google.com/search?q=weather")
val i = Intent(Intent.ACTION_VIEW, uri)
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
try {

View File

@ -18,6 +18,7 @@ import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus
import java.lang.Exception
@ -37,12 +38,31 @@ class LocationService : Service() {
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 (checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION) &&
(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R ||
checkGrantedPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION))
) {
LocationServices.getFusedLocationProviderClient(this@LocationService).lastLocation.addOnCompleteListener { task ->
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

View File

@ -55,18 +55,20 @@ class UpdateCalendarService : Service() {
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 begin = Calendar.getInstance().apply {
val limit = Calendar.getInstance().apply {
set(Calendar.MILLISECOND, 0)
set(Calendar.SECOND, 0)
set(Calendar.MINUTE, 0)
set(Calendar.HOUR_OF_DAY, 0)
}
val limit = Calendar.getInstance().apply {
add(Calendar.DATE, 1)
when (Preferences.showUntil) {
0 -> add(Calendar.HOUR, 3)
1 -> add(Calendar.HOUR, 6)
@ -85,29 +87,37 @@ class UpdateCalendarService : Service() {
)
) {
eventRepository.resetNextEventData()
eventRepository.clearEvents()
Preferences.showEvents = false
} else {
try {
val provider = CalendarProvider(this@UpdateCalendarService)
val data = provider.getInstances(begin.timeInMillis, limit.timeInMillis)
// 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 && instance.begin <= limit.timeInMillis && now.timeInMillis < instance.end && !CalendarHelper.getFilteredCalendarIdList()
.contains(e.calendarId)
) {
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 (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
@ -124,6 +134,7 @@ class UpdateCalendarService : Service() {
&& endDate.get(Calendar.MINUTE) == 0
&& endDate.get(Calendar.HOUR_OF_DAY) == 0
)
*/
eventList.add(
Event(
@ -132,8 +143,8 @@ class UpdateCalendarService : Service() {
title = e.title ?: "",
startDate = instance.begin,
endDate = instance.end,
calendarID = e.calendarId.toInt(),
allDay = isAllDay,
calendarID = e.calendarId,
allDay = e.allDay,
address = e.eventLocation ?: "",
selfAttendeeStatus = e.selfAttendeeStatus.toInt(),
availability = e.availability
@ -164,13 +175,14 @@ class UpdateCalendarService : Service() {
}
} else {
eventRepository.resetNextEventData()
eventRepository.clearEvents()
}
eventRepository.close()
UpdatesReceiver.setUpdates(this@UpdateCalendarService)
MainWidget.updateWidget(this@UpdateCalendarService)
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
eventRepository.close()
stopSelf()
}

View File

@ -140,6 +140,7 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh
if (Preferences.showEvents && !checkGrantedPermission(Manifest.permission.READ_CALENDAR)) {
Preferences.showEvents = false
com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver.removeUpdates(this)
}
}

View File

@ -38,12 +38,15 @@ class CustomFontActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter
private lateinit var viewModel: CustomFontViewModel
private lateinit var binding: ActivityCustomFontBinding
private lateinit var handlerThread: HandlerThread
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(CustomFontViewModel::class.java)
binding = ActivityCustomFontBinding.inflate(layoutInflater)
handlerThread = HandlerThread("listCustomFonts")
handlerThread.start()
binding.listView.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this)
@ -64,14 +67,17 @@ class CustomFontActivity : AppCompatActivity() {
injector
.text(R.id.text, item)
.with<TextView>(R.id.text) {
val googleSans: Typeface = when (Preferences.customFontVariant) {
"100" -> Typeface.createFromAsset(this.assets, "fonts/google_sans_thin.ttf")
"200" -> Typeface.createFromAsset(this.assets, "fonts/google_sans_light.ttf")
"500" -> Typeface.createFromAsset(this.assets, "fonts/google_sans_medium.ttf")
"700" -> Typeface.createFromAsset(this.assets, "fonts/google_sans_bold.ttf")
"800" -> Typeface.createFromAsset(this.assets, "fonts/google_sans_black.ttf")
else -> Typeface.createFromAsset(this.assets, "fonts/google_sans_regular.ttf")
}
val googleSans: Typeface? = androidx.core.content.res.ResourcesCompat.getFont(
this,
when (Preferences.customFontVariant) {
"100" -> R.font.google_sans_thin
"200" -> R.font.google_sans_light
"500" -> R.font.google_sans_medium
"700" -> R.font.google_sans_bold
"800" -> R.font.google_sans_black
else -> R.font.google_sans_regular
}
)
it.typeface = googleSans
}
@ -97,32 +103,49 @@ class CustomFontActivity : AppCompatActivity() {
)
val callback = object : FontsContractCompat.FontRequestCallback() {
override fun onTypefaceRetrieved(typeface: Typeface) {
it.typeface = typeface
it.isVisible = true
class Callback : FontsContractCompat.FontRequestCallback() {
var handler: Handler? = Handler(handlerThread.looper)
it.measure(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
fun cancel() {
if (handler != null) {
handler!!.removeCallbacksAndMessages(null)
handler = null
}
}
protected fun finalize() {
cancel()
}
override fun onTypefaceRetrieved(typeface: Typeface) {
if (it.tag == this) {
it.tag = null
it.typeface = typeface
it.setTextColor(getColor(R.color.colorPrimaryText))
}
}
override fun onTypefaceRequestFailed(reason: Int) {
it.isVisible = false
it.layoutParams = it.layoutParams.apply {
height = 0
if (it.tag == this) {
it.tag = null
//it.text = item.fontFamily + " ($reason)"
it.setTextColor(getColor(R.color.errorColorText))
}
}
}
val handlerThread = HandlerThread(item.fontFamily)
handlerThread.start()
val mHandler = Handler(handlerThread.looper)
(it.tag as Callback?)?.cancel()
val callback = Callback()
it.tag = callback
it.typeface = null
it.setTextColor(getColor(R.color.colorSecondaryText))
val mHandler = callback.handler!!
FontsContractCompat.requestFont(this, request, callback, mHandler)
}
injector.clicked(R.id.text) {
if ((it as TextView).typeface == null) return@clicked
val dialog = BottomSheetMenu<Int>(this, header = item.fontFamily)
if (item.fontVariants.isEmpty()) {
dialog.addItem(SettingsStringHelper.getVariantLabel(this, "regular"), -1)
@ -147,6 +170,12 @@ class CustomFontActivity : AppCompatActivity() {
setContentView(binding.root)
}
override fun onDestroy() {
handlerThread.quit()
filterJob?.cancel()
super.onDestroy()
}
private var filterJob: Job? = null
private fun subscribeUi(binding: ActivityCustomFontBinding, viewModel: CustomFontViewModel) {
@ -204,6 +233,13 @@ class CustomFontActivity : AppCompatActivity() {
adapter.updateData(filteredList)
binding.loader.visibility = View.INVISIBLE
}
} else {
delay(200)
withContext(Dispatchers.Main) {
adapter.updateData(listOf(getString(R.string.custom_font_subtitle_1)).filter {
it.contains(search ?: "", ignoreCase = true)
})
}
}
}
}

View File

@ -46,11 +46,16 @@ class CustomLocationActivity : AppCompatActivity() {
adapter = SlimAdapter.create()
adapter
.register<String>(R.layout.custom_location_item) { _, injector ->
injector
.text(R.id.text, getString(R.string.custom_location_gps))
.clicked(R.id.text) {
requirePermission()
injector.text(R.id.text, getString(R.string.custom_location_gps))
injector.clicked(R.id.text) {
Preferences.bulk {
remove(Preferences::customLocationLat)
remove(Preferences::customLocationLon)
remove(Preferences::customLocationAdd)
}
setResult(Activity.RESULT_OK)
finish()
}
}
.register<Address>(R.layout.custom_location_item) { item, injector ->
injector.text(R.id.text, item.getAddressLine(0) ?: "")
@ -59,9 +64,9 @@ class CustomLocationActivity : AppCompatActivity() {
customLocationLat = item.latitude.toString()
customLocationLon = item.longitude.toString()
customLocationAdd = item.getAddressLine(0) ?: ""
setResult(Activity.RESULT_OK)
finish()
}
setResult(Activity.RESULT_OK)
finish()
}
}
.attachTo(binding.listView)
@ -115,36 +120,6 @@ class CustomLocationActivity : AppCompatActivity() {
})
}
private fun requirePermission() {
Dexter.withContext(this)
.withPermissions(
Manifest.permission.ACCESS_FINE_LOCATION
).withListener(object: MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
report?.let {
if (report.areAllPermissionsGranted()){
Preferences.bulk {
remove(Preferences::customLocationLat)
remove(Preferences::customLocationLon)
remove(Preferences::customLocationAdd)
}
setResult(Activity.RESULT_OK)
finish()
}
}
}
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()
}
private fun setupListener() {
binding.actionBack.setOnClickListener {
onBackPressed()

View File

@ -115,8 +115,6 @@ class WeatherProviderActivity : AppCompatActivity() {
adapter.updateData(
Constants.WeatherProvider.values().asList()
.filter { it != Constants.WeatherProvider.HERE }
.filter { it != Constants.WeatherProvider.ACCUWEATHER }
)
setupListener()
@ -127,11 +125,11 @@ class WeatherProviderActivity : AppCompatActivity() {
private fun subscribeUi(viewModel: WeatherProviderViewModel) {
viewModel.weatherProviderError.observe(this) {
updateListItem()
binding.listView.postDelayed({ updateListItem() }, 300)
}
viewModel.weatherProviderLocationError.observe(this) {
updateListItem()
binding.listView.postDelayed({ updateListItem() }, 300)
}
}
@ -165,7 +163,7 @@ class WeatherProviderActivity : AppCompatActivity() {
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(ignore: MainFragment.UpdateUiMessageEvent?) {
fun onMessageEvent(@Suppress("UNUSED_PARAMETER") ignore: MainFragment.UpdateUiMessageEvent?) {
binding.loader.isVisible = Preferences.weatherProviderError == "-"
if (Preferences.weatherProviderError == "" && Preferences.weatherProviderLocationError == "") {
Snackbar.make(binding.listView, getString(R.string.settings_weather_provider_api_key_subtitle_all_set), Snackbar.LENGTH_LONG).show()

View File

@ -8,16 +8,10 @@ import android.os.Bundle
import android.provider.Settings
import android.util.DisplayMetrics
import android.util.Log
import android.util.TypedValue
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.RelativeLayout
import androidx.core.content.ContextCompat
import androidx.core.view.children
import androidx.core.view.isVisible
import android.widget.RemoteViews
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
@ -30,23 +24,20 @@ import com.tommasoberlose.anotherwidget.databinding.FragmentAppMainBinding
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.*
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.ui.widgets.StandardWidget
import com.tommasoberlose.anotherwidget.utils.*
import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
class MainFragment : Fragment() {
companion object {
fun newInstance() = MainFragment()
private val PREVIEW_BASE_HEIGHT: Int
get() = if (Preferences.widgetAlign == Constants.WidgetAlign.CENTER.rawValue) 120 else 180
}
private lateinit var viewModel: MainViewModel
@ -95,16 +86,11 @@ class MainFragment : Fragment() {
binding.actionSettings.isClickable = !show
binding.actionSettings.isFocusable = !show
binding.fragmentTitle.text = if (show) destination.label.toString() else getString(R.string.app_name)
binding.toolbar.cardElevation = 0f
}
binding.actionSettings.setOnSingleClickListener {
Navigation.findNavController(it).navigate(R.id.action_appMainFragment_to_appSettingsFragment,)
}
binding.preview.layoutParams = binding.preview.layoutParams.apply {
height = PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel(
requireContext()
) else 0
Navigation.findNavController(it).navigate(R.id.action_appMainFragment_to_appSettingsFragment)
}
subscribeUi(viewModel)
@ -113,6 +99,10 @@ class MainFragment : Fragment() {
}
private fun subscribeUi(viewModel: MainViewModel) {
viewModel.showPreview.observe(viewLifecycleOwner) {
binding.preview.visibility = if (it) View.VISIBLE else View.GONE
}
viewModel.showWallpaper.observe(viewLifecycleOwner) {
if (it) {
val wallpaper = requireActivity().getCurrentWallpaper()
@ -153,85 +143,43 @@ class MainFragment : Fragment() {
}
viewModel.fragmentScrollY.observe(viewLifecycleOwner) {
binding.toolbar.cardElevation = if (it > 0) 24f else 0f
}
viewModel.widgetAlign.observe(viewLifecycleOwner) {
updatePreviewVisibility()
lifecycleScope.launch {
delay(350)
updateClock()
}
}
viewModel.showPreview.observe(viewLifecycleOwner) {
updatePreviewVisibility()
}
viewModel.clockPreferencesUpdate.observe(viewLifecycleOwner) {
updateClock()
binding.toolbar.cardElevation = if (it > 0) 32f else 0f
}
viewModel.widgetPreferencesUpdate.observe(viewLifecycleOwner) {
onUpdateUiEvent(null)
}
viewModel.showClock.observe(viewLifecycleOwner) {
updateClockVisibility(it)
}
}
private var uiJob: Job? = null
private fun updateUI() {
if (Preferences.showPreview) {
lifecycleScope.launch(Dispatchers.IO) {
val bgColor: Int = ContextCompat.getColor(
requireContext(),
if (ColorHelper.getFontColor(requireActivity().isDarkTheme())
.isColorDark()
) android.R.color.white else R.color.colorAccent
)
val wallpaperDrawable = BitmapHelper.getTintedDrawable(
requireContext(),
R.drawable.card_background,
ColorHelper.getBackgroundColor(requireActivity().isDarkTheme())
)
withContext(Dispatchers.Main) {
binding.preview.setCardBackgroundColor(bgColor)
binding.widgetDetail.widgetShapeBackground.setImageDrawable(wallpaperDrawable)
}
}
WidgetHelper.runWithCustomTypeface(requireContext()) { typeface ->
uiJob?.cancel()
uiJob = lifecycleScope.launch(Dispatchers.IO) {
val generatedView = MainWidget.getWidgetView(requireContext(), typeface)?.root
val generatedView = MainWidget.getWidgetView(
requireContext(),
binding.widget.width - binding.widget.paddingStart - binding.widget.paddingEnd,
typeface
)
if (generatedView != null) {
withContext(Dispatchers.Main) {
val view: View = generatedView.apply(requireActivity().applicationContext, binding.widget)
view.measure(0, 0)
binding.widgetDetail.content.removeAllViews()
val container = LinearLayout(requireContext()).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
}
container.gravity = when (Preferences.widgetAlign) {
Constants.WidgetAlign.CENTER.rawValue -> Gravity.CENTER_HORIZONTAL
Constants.WidgetAlign.LEFT.rawValue -> Gravity.START
Constants.WidgetAlign.RIGHT.rawValue -> Gravity.END
else -> Gravity.NO_GRAVITY
}
container.addView(generatedView)
binding.widgetDetail.content.addView(container)
binding.widgetLoader.animate().scaleX(0f).scaleY(0f).alpha(0f)
binding.widgetLoader.animate().scaleX(1f).scaleY(1f).alpha(1f)
.setDuration(200L).start()
binding.widget.animate().alpha(1f).start()
binding.widget.animate().alpha(0f).setDuration(200L).withEndAction {
binding.widget.removeAllViews()
binding.widget.addView(view)
updatePreviewVisibility(view.measuredHeight)
binding.widgetLoader.animate().scaleX(0f).scaleY(0f).alpha(0f)
.setDuration(200L).start()
binding.widget.animate().alpha(1f).start()
}.start()
}
}
}
@ -239,136 +187,13 @@ class MainFragment : Fragment() {
}
}
private fun updateClock() {
// Clock
binding.widgetDetail.time.setTextColor(ColorHelper.getClockFontColor(requireActivity().isDarkTheme()))
binding.widgetDetail.timeAmPm.setTextColor(ColorHelper.getClockFontColor(requireActivity().isDarkTheme()))
binding.widgetDetail.time.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(requireContext())
)
binding.widgetDetail.timeAmPm.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(requireContext()) / 5 * 2
)
binding.widgetDetail.timeAmPm.isVisible = Preferences.showAMPMIndicator
// Timezones
if (Preferences.altTimezoneId != "" && Preferences.altTimezoneLabel != "") {
// Clock
binding.widgetDetail.altTimezoneTime.timeZone = Preferences.altTimezoneId
binding.widgetDetail.altTimezoneTimeAmPm.timeZone = Preferences.altTimezoneId
binding.widgetDetail.altTimezoneLabel.text = Preferences.altTimezoneLabel
binding.widgetDetail.altTimezoneTime.setTextColor(ColorHelper.getClockFontColor(requireActivity().isDarkTheme()))
binding.widgetDetail.altTimezoneTimeAmPm.setTextColor(ColorHelper.getClockFontColor(requireActivity().isDarkTheme()))
binding.widgetDetail.altTimezoneLabel.setTextColor(ColorHelper.getClockFontColor(requireActivity().isDarkTheme()))
binding.widgetDetail.altTimezoneTime.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(requireContext()) / 3
)
binding.widgetDetail.altTimezoneTimeAmPm.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
(Preferences.clockTextSize.toPixel(requireContext()) / 3) / 5 * 2
)
binding.widgetDetail.altTimezoneLabel.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
(Preferences.clockTextSize.toPixel(requireContext()) / 3) / 5 * 2
)
binding.widgetDetail.timezonesContainer.isVisible = true
} else {
binding.widgetDetail.timezonesContainer.isVisible = false
}
// Clock bottom margin
binding.widgetDetail.clockBottomMarginNone.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.NONE.rawValue
binding.widgetDetail.clockBottomMarginSmall.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.SMALL.rawValue
binding.widgetDetail.clockBottomMarginMedium.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.MEDIUM.rawValue
binding.widgetDetail.clockBottomMarginLarge.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.LARGE.rawValue
// Align
binding.widgetDetail.timeContainer.layoutParams = (binding.widgetDetail.timeContainer.layoutParams as LinearLayout.LayoutParams).apply {
gravity = when (Preferences.widgetAlign) {
Constants.WidgetAlign.CENTER.rawValue -> Gravity.CENTER_HORIZONTAL
Constants.WidgetAlign.LEFT.rawValue -> Gravity.START
Constants.WidgetAlign.RIGHT.rawValue -> Gravity.END
else -> Gravity.NO_GRAVITY
}
}
if (Preferences.widgetAlign == Constants.WidgetAlign.RIGHT.rawValue) {
with (binding.widgetDetail.timeContainer) {
val child = getChildAt(2)
if (child.id == R.id.timezones_container) {
removeViewAt(2)
child.layoutParams = (child.layoutParams as ViewGroup.MarginLayoutParams).apply {
marginEnd = 16f.convertDpToPixel(requireContext()).toInt()
}
addView(child, 0)
}
}
} else {
with (binding.widgetDetail.timeContainer) {
val child = getChildAt(0)
if (child.id == R.id.timezones_container) {
removeViewAt(0)
child.layoutParams = (child.layoutParams as ViewGroup.MarginLayoutParams).apply {
marginEnd = 0
}
addView(child, 2)
}
}
}
}
private fun updateClockVisibility(showClock: Boolean) {
binding.widgetDetail.timeContainer.clearAnimation()
binding.widgetDetail.time.clearAnimation()
updatePreviewVisibility()
if (showClock) {
binding.widgetDetail.timeContainer.layoutParams = (binding.widgetDetail.timeContainer.layoutParams as LinearLayout.LayoutParams).apply {
height = RelativeLayout.LayoutParams.WRAP_CONTENT
}
binding.widgetDetail.timeContainer.measure(0, 0)
}
if ((Preferences.showClock && binding.widgetDetail.time.alpha != 1f) || (!Preferences.showClock && binding.widgetDetail.time.alpha != 0f)) {
val initialHeight = binding.widgetDetail.timeContainer.measuredHeight
ValueAnimator.ofFloat(
if (showClock) 0f else 1f,
if (showClock) 1f else 0f
).apply {
duration = 500L
addUpdateListener {
val animatedValue = animatedValue as Float
binding.widgetDetail.timeContainer.layoutParams =
binding.widgetDetail.timeContainer.layoutParams.apply {
height = (initialHeight * animatedValue).toInt()
}
binding.widgetDetail.time.alpha = animatedValue
binding.widgetDetail.timeAmPm.alpha = animatedValue
binding.widgetDetail.altTimezoneTime.alpha = animatedValue
binding.widgetDetail.altTimezoneTimeAmPm.alpha = animatedValue
binding.widgetDetail.altTimezoneLabel.alpha = animatedValue
}
}.start()
}
}
private fun updatePreviewVisibility() {
binding.preview.clearAnimation()
if (binding.preview.layoutParams.height != (if (Preferences.showPreview) PREVIEW_BASE_HEIGHT.toPixel(requireContext()) else 0) + (if (Preferences.showClock) 100.toPixel(
requireContext()
) else 0)) {
private fun updatePreviewVisibility(widgetHeight: Int) {
val newHeight = widgetHeight + 32f.convertDpToPixel(requireContext()).toInt()
if (binding.preview.layoutParams.height != newHeight) {
binding.preview.clearAnimation()
ValueAnimator.ofInt(
binding.preview.height,
(if (Preferences.showPreview) PREVIEW_BASE_HEIGHT.toPixel(requireContext()) else 0) + (if (Preferences.showClock) 100.toPixel(
requireContext()
) else 0)
newHeight
).apply {
duration = 500L
addUpdateListener {
@ -398,7 +223,7 @@ class MainFragment : Fragment() {
class ChangeTabEvent(val page: Int)
@Subscribe(threadMode = ThreadMode.MAIN)
fun onUpdateUiEvent(ignore: UpdateUiMessageEvent?) {
fun onUpdateUiEvent(@Suppress("UNUSED_PARAMETER") ignore: UpdateUiMessageEvent?) {
delayJob?.cancel()
delayJob = lifecycleScope.launch(Dispatchers.IO) {
delay(300)
@ -409,7 +234,7 @@ class MainFragment : Fragment() {
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onChangeTabEvent(ignore: ChangeTabEvent) {
fun onChangeTabEvent(@Suppress("UNUSED_PARAMETER") ignore: ChangeTabEvent) {
val navHost = childFragmentManager.findFragmentById(R.id.settings_fragment) as? NavHostFragment?
navHost?.navController?.navigateUp()
}

View File

@ -182,7 +182,7 @@ class CalendarFragment : Fragment() {
binding.showAllDayToggle.setOnCheckedChangeListener { _, isChecked ->
Preferences.calendarAllDay = isChecked
MainWidget.updateWidget(requireContext())
updateCalendar()
}
binding.actionChangeAttendeeFilter.setOnClickListener {
@ -227,7 +227,7 @@ class CalendarFragment : Fragment() {
binding.showOnlyBusyEventsToggle.setOnCheckedChangeListener { _, isChecked ->
Preferences.showOnlyBusyEvents = isChecked
MainWidget.updateWidget(requireContext())
updateCalendar()
}
binding.actionShowDiffTime.setOnClickListener {
@ -236,6 +236,7 @@ class CalendarFragment : Fragment() {
binding.showDiffTimeToggle.setOnCheckedChangeListener { _, isChecked ->
Preferences.showDiffTime = isChecked
updateCalendar()
}
binding.actionShowNextEventOnMultipleLines.setOnClickListener {
@ -254,6 +255,7 @@ class CalendarFragment : Fragment() {
.addItem(getString(R.string.settings_widget_update_frequency_low), Constants.WidgetUpdateFrequency.LOW.rawValue)
.addOnSelectItemListener { value ->
Preferences.widgetUpdateFrequency = value
updateCalendar()
}.show()
}
}

View File

@ -17,7 +17,7 @@ import com.chibatching.kotpref.bulk
import com.google.android.material.transition.MaterialSharedAxis
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.BottomSheetColorPicker
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
import com.tommasoberlose.anotherwidget.components.BottomSheetPicker
import com.tommasoberlose.anotherwidget.databinding.FragmentTabClockBinding
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
@ -144,16 +144,15 @@ class ClockFragment : Fragment() {
private fun setupListener() {
binding.actionClockTextSize.setOnClickListener {
val dialog = BottomSheetMenu<Float>(
BottomSheetPicker(
requireContext(),
header = getString(R.string.settings_clock_text_size_title)
).setSelectedValue(Preferences.clockTextSize)
(46 downTo 12).filter { it % 2 == 0 }.forEach {
dialog.addItem("${it}sp", it.toFloat())
}
dialog.addOnSelectItemListener { value ->
Preferences.clockTextSize = value
}.show()
items = (120 downTo 30).filter { it % 2 == 0 }.map { BottomSheetPicker.MenuItem("${it}sp", it.toFloat()) },
getSelected = { Preferences.clockTextSize },
header = getString(R.string.settings_clock_text_size_title),
onItemSelected = {value ->
if (value != null) Preferences.clockTextSize = value
}
).show()
}
binding.actionAltTimezoneClock.setOnClickListener {

View File

@ -102,15 +102,7 @@ class GesturesFragment : Fragment() {
it == IntentHelper.DO_NOTHING_OPTION -> getString(R.string.gestures_do_nothing)
it == IntentHelper.REFRESH_WIDGET_OPTION -> getString(R.string.gestures_refresh_widget)
it != IntentHelper.DEFAULT_OPTION -> it
else -> {
if (IntentHelper.getCalendarIntent(requireContext()).isDefaultSet(requireContext())) {
getString(
R.string.default_calendar_app
)
} else {
getString(R.string.gestures_do_nothing)
}
}
else -> getString(R.string.default_calendar_app)
}
}
}
@ -127,15 +119,7 @@ class GesturesFragment : Fragment() {
it == IntentHelper.DO_NOTHING_OPTION -> getString(R.string.gestures_do_nothing)
it == IntentHelper.REFRESH_WIDGET_OPTION -> getString(R.string.gestures_refresh_widget)
it != IntentHelper.DEFAULT_OPTION -> it
else -> {
if (IntentHelper.getClockIntent(requireContext()).isDefaultSet(requireContext())) {
getString(
R.string.default_clock_app
)
} else {
getString(R.string.gestures_do_nothing)
}
}
else -> getString(R.string.default_clock_app)
}
}
}
@ -173,9 +157,12 @@ class GesturesFragment : Fragment() {
}
binding.actionCalendarApp.setOnClickListener {
startActivityForResult(Intent(requireContext(), ChooseApplicationActivity::class.java).apply {
putExtra(Constants.RESULT_APP_PACKAGE, Preferences.calendarAppPackage)
}, RequestCode.CALENDAR_APP_REQUEST_CODE.code)
startActivityForResult(
Intent(requireContext(), ChooseApplicationActivity::class.java).apply {
putExtra(Constants.RESULT_APP_PACKAGE, Preferences.calendarAppPackage)
},
RequestCode.CALENDAR_APP_REQUEST_CODE.code
)
}
binding.actionClockApp.setOnClickListener {

View File

@ -293,6 +293,25 @@ class GlanceTabFragment : Fragment() {
if (!(isVisible && hasError)) View.VISIBLE else View.GONE
)
}
Constants.GlanceProviderId.WEATHER -> {
isVisible =
Preferences.showWeatherAsGlanceProvider
val hasError = !Preferences.showWeather || (Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-") || Preferences.weatherProviderLocationError != ""
injector.text(
R.id.label,
if (isVisible && !hasError) getString(R.string.settings_visible) else getString(
R.string.settings_not_visible
)
)
injector.visibility(
R.id.error_icon,
if (isVisible && hasError) View.VISIBLE else View.GONE
)
injector.visibility(
R.id.info_icon,
if (!(isVisible && hasError)) View.VISIBLE else View.GONE
)
}
}
injector.alpha(R.id.title, if (isVisible) 1f else .25f)

View File

@ -86,6 +86,28 @@ class LayoutFragment : Fragment() {
viewModel: MainViewModel
) {
viewModel.widgetMargin.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.widgetMarginLabel.text = when (it) {
Constants.Dimension.NONE.rawValue -> getString(R.string.settings_widget_dim_none)
Constants.Dimension.SMALL.rawValue -> getString(R.string.settings_widget_dim_small)
Constants.Dimension.LARGE.rawValue -> getString(R.string.settings_widget_dim_large)
else -> getString(R.string.settings_widget_dim_medium)
}
}
}
viewModel.widgetPadding.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.widgetPaddingLabel.text = when (it) {
Constants.Dimension.NONE.rawValue -> getString(R.string.settings_widget_dim_none)
Constants.Dimension.SMALL.rawValue -> getString(R.string.settings_widget_dim_small)
Constants.Dimension.LARGE.rawValue -> getString(R.string.settings_widget_dim_large)
else -> getString(R.string.settings_widget_dim_medium)
}
}
}
viewModel.secondRowTopMargin.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.secondRowTopMarginLabel.text = when (it) {
@ -147,6 +169,58 @@ class LayoutFragment : Fragment() {
private fun setupListener() {
binding.actionWidgetMargin.setOnClickListener {
BottomSheetMenu<Float>(
requireContext(),
header = getString(R.string.settings_widget_margin_title)
).setSelectedValue(Preferences.widgetMargin)
.addItem(
getString(R.string.settings_widget_dim_none),
Constants.Dimension.NONE.rawValue
)
.addItem(
getString(R.string.settings_widget_dim_small),
Constants.Dimension.SMALL.rawValue
)
.addItem(
getString(R.string.settings_widget_dim_medium),
Constants.Dimension.MEDIUM.rawValue
)
.addItem(
getString(R.string.settings_widget_dim_large),
Constants.Dimension.LARGE.rawValue
)
.addOnSelectItemListener { value ->
Preferences.widgetMargin = value
}.show()
}
binding.actionWidgetPadding.setOnClickListener {
BottomSheetMenu<Float>(
requireContext(),
header = getString(R.string.settings_widget_padding_title)
).setSelectedValue(Preferences.widgetPadding)
.addItem(
getString(R.string.settings_widget_dim_none),
Constants.Dimension.NONE.rawValue
)
.addItem(
getString(R.string.settings_widget_dim_small),
Constants.Dimension.SMALL.rawValue
)
.addItem(
getString(R.string.settings_widget_dim_medium),
Constants.Dimension.MEDIUM.rawValue
)
.addItem(
getString(R.string.settings_widget_dim_large),
Constants.Dimension.LARGE.rawValue
)
.addOnSelectItemListener { value ->
Preferences.widgetPadding = value
}.show()
}
binding.actionSecondRowTopMarginSize.setOnClickListener {
BottomSheetMenu<Int>(
requireContext(),

View File

@ -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.receivers.UpdatesReceiver
import com.tommasoberlose.anotherwidget.receivers.WeatherReceiver
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
@ -123,6 +124,7 @@ class PreferencesFragment : Fragment() {
requireCalendarPermission()
} else {
Preferences.showEvents = enabled
UpdatesReceiver.removeUpdates(requireContext())
}
}
@ -133,6 +135,8 @@ class PreferencesFragment : Fragment() {
binding.showWeatherSwitch.setOnCheckedChangeListener { _, enabled: Boolean ->
Preferences.showWeather = enabled
if (enabled) {
Preferences.weatherProviderError = ""
Preferences.weatherProviderLocationError = ""
WeatherReceiver.setUpdates(requireContext())
} else {
WeatherReceiver.removeUpdates(requireContext())

View File

@ -276,7 +276,7 @@ class TypographyFragment : Fragment() {
Intent(requireContext(), CustomFontActivity::class.java),
RequestCode.CUSTOM_FONT_CHOOSER_REQUEST_CODE.code
)
} else if (value != Constants.CUSTOM_FONT_DOWNLOADED) {
} else if (value != Preferences.customFont) {
Preferences.bulk {
customFont = value
customFontFile = ""
@ -317,6 +317,17 @@ class TypographyFragment : Fragment() {
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == android.app.Activity.RESULT_OK) {
when (requestCode) {
RequestCode.CUSTOM_FONT_CHOOSER_REQUEST_CODE.code -> {
com.tommasoberlose.anotherwidget.ui.widgets.MainWidget.updateWidget(requireContext())
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
private fun maintainScrollPosition(callback: () -> Unit) {
binding.scrollView.isScrollable = false
callback.invoke()

View File

@ -91,16 +91,17 @@ class WeatherFragment : Fragment() {
viewModel.weatherProvider.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.labelWeatherProvider.text = WeatherHelper.getProviderName(requireContext(), Constants.WeatherProvider.fromInt(it)!!)
checkWeatherProviderConfig()
}
}
viewModel.weatherProviderError.observe(viewLifecycleOwner) {
checkWeatherProviderConfig()
checkLocationPermission()
}
viewModel.weatherProviderLocationError.observe(viewLifecycleOwner) {
checkWeatherProviderConfig()
checkLocationPermission()
}
viewModel.customLocationAdd.observe(viewLifecycleOwner) {
@ -108,6 +109,7 @@ class WeatherFragment : Fragment() {
binding.labelCustomLocation.text =
if (it == "") getString(R.string.custom_location_gps) else it
}
checkWeatherProviderConfig()
checkLocationPermission()
}
@ -116,43 +118,50 @@ class WeatherFragment : Fragment() {
binding.tempUnit.text =
if (it == "F") getString(R.string.fahrenheit) else getString(R.string.celsius)
}
checkLocationPermission()
}
viewModel.weatherRefreshPeriod.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.labelWeatherRefreshPeriod.text = getString(SettingsStringHelper.getRefreshPeriodString(it))
}
checkLocationPermission()
}
viewModel.weatherIconPack.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.labelWeatherIconPack.text = getString(R.string.settings_weather_icon_pack_default).format((it + 1))
}
checkLocationPermission()
}
}
private fun checkLocationPermission() {
if (requireActivity().checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
if (requireActivity().checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION) &&
(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R ||
requireActivity().checkGrantedPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION))
) {
binding.locationPermissionAlert.isVisible = false
WeatherReceiver.setUpdates(requireContext())
} else if (Preferences.showWeather && Preferences.customLocationAdd == "") {
} else if (Preferences.customLocationAdd == "") {
binding.locationPermissionAlert.isVisible = true
binding.locationPermissionAlert.setOnClickListener {
requirePermission()
}
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R &&
requireActivity().checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION)
) {
val text = getString(R.string.action_grant_permission) + " - " +
requireContext().packageManager.backgroundPermissionOptionLabel
binding.locationPermissionAlert.text = text
}
binding.weatherProviderLocationError.isVisible = false
} else {
binding.locationPermissionAlert.isVisible = false
}
}
private fun checkWeatherProviderConfig() {
binding.weatherProviderError.isVisible = Preferences.showWeather && Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-"
binding.weatherProviderError.isVisible = Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-"
binding.weatherProviderError.text = Preferences.weatherProviderError
binding.weatherProviderLocationError.isVisible = Preferences.showWeather && Preferences.weatherProviderLocationError != ""
binding.weatherProviderLocationError.isVisible = Preferences.weatherProviderLocationError != ""
binding.weatherProviderLocationError.text = Preferences.weatherProviderLocationError
}
@ -177,11 +186,11 @@ class WeatherFragment : Fragment() {
.addItem(getString(R.string.celsius), "C")
.addOnSelectItemListener { value ->
if (value != Preferences.weatherTempUnit) {
Preferences.weatherTempUnit = value
viewLifecycleOwner.lifecycleScope.launch {
WeatherHelper.updateWeather(requireContext())
}
}
Preferences.weatherTempUnit = value
}.show()
}
@ -193,7 +202,10 @@ class WeatherFragment : Fragment() {
}
dialog
.addOnSelectItemListener { value ->
Preferences.weatherRefreshPeriod = value
if (value != Preferences.weatherRefreshPeriod) {
Preferences.weatherRefreshPeriod = value
WeatherReceiver.setUpdates(requireContext())
}
}.show()
}
@ -206,12 +218,12 @@ class WeatherFragment : Fragment() {
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
Constants.RESULT_CODE_CUSTOM_LOCATION -> {
WeatherReceiver.setUpdates(requireContext())
checkLocationPermission()
}
RequestCode.WEATHER_PROVIDER_REQUEST_CODE.code -> {
checkLocationPermission()
viewLifecycleOwner.lifecycleScope.launch {
WeatherHelper.updateWeather(requireContext())
}
}
//RequestCode.WEATHER_PROVIDER_REQUEST_CODE.code -> {
//}
}
}
super.onActivityResult(requestCode, resultCode, data)
@ -220,12 +232,19 @@ class WeatherFragment : Fragment() {
private fun requirePermission() {
Dexter.withContext(requireContext())
.withPermissions(
Manifest.permission.ACCESS_FINE_LOCATION
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R &&
requireActivity().checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION))
Manifest.permission.ACCESS_BACKGROUND_LOCATION
else
Manifest.permission.ACCESS_FINE_LOCATION
).withListener(object: MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
report?.let {
if (report.areAllPermissionsGranted()){
if (report.areAllPermissionsGranted()) {
checkLocationPermission()
viewLifecycleOwner.lifecycleScope.launch {
WeatherHelper.updateWeather(requireContext())
}
}
}
}

View File

@ -50,6 +50,8 @@ class MainViewModel(context: Application) : AndroidViewModel(context) {
val showDividers = Preferences.asLiveData(Preferences::showDividers)
val secondRowTopMargin = Preferences.asLiveData(Preferences::secondRowTopMargin)
val widgetAlign = Preferences.asLiveData(Preferences::widgetAlign)
val widgetMargin = Preferences.asLiveData(Preferences::widgetMargin)
val widgetPadding = Preferences.asLiveData(Preferences::widgetPadding)
// Calendar Settings
val showEvents = Preferences.asLiveData(Preferences::showEvents)
@ -102,7 +104,8 @@ class MainViewModel(context: Application) : AndroidViewModel(context) {
// UI
val fragmentScrollY = MutableLiveData<Int>()
val clockPreferencesUpdate = MediatorLiveData<Boolean>().apply {
val widgetPreferencesUpdate = MediatorLiveData<Boolean>().apply {
addSource(Preferences.asLiveData(Preferences::showClock)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockTextSize)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockTextColor)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockTextAlpha)) { value = true }
@ -111,8 +114,6 @@ class MainViewModel(context: Application) : AndroidViewModel(context) {
addSource(Preferences.asLiveData(Preferences::showAMPMIndicator)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockBottomMargin)) { value = true }
addSource(Preferences.asLiveData(Preferences::altTimezoneLabel)) { value = true }
}
val widgetPreferencesUpdate = MediatorLiveData<Boolean>().apply {
addSource(Preferences.asLiveData(Preferences::textGlobalColor)) { value = true }
addSource(Preferences.asLiveData(Preferences::textGlobalAlpha)) { value = true }
addSource(Preferences.asLiveData(Preferences::textSecondaryColor)) { value = true }
@ -136,6 +137,8 @@ class MainViewModel(context: Application) : AndroidViewModel(context) {
addSource(Preferences.asLiveData(Preferences::customFontVariant)) { value = true }
addSource(Preferences.asLiveData(Preferences::secondRowInformation)) { value = true }
addSource(Preferences.asLiveData(Preferences::widgetAlign)) { value = true }
addSource(Preferences.asLiveData(Preferences::widgetMargin)) { value = true }
addSource(Preferences.asLiveData(Preferences::widgetPadding)) { value = true }
addSource(Preferences.asLiveData(Preferences::showDividers)) { value = true }
addSource(Preferences.asLiveData(Preferences::secondRowTopMargin)) { value = true }
addSource(Preferences.asLiveData(Preferences::isDateCapitalize)) { value = true }
@ -171,6 +174,7 @@ class MainViewModel(context: Application) : AndroidViewModel(context) {
addSource(Preferences.asLiveData(Preferences::musicPlayersFilter)) { value = true }
addSource(Preferences.asLiveData(Preferences::appNotificationsFilter)) { value = true }
addSource(Preferences.asLiveData(Preferences::showEventsAsGlanceProvider)) { value = true }
addSource(Preferences.asLiveData(Preferences::showWeatherAsGlanceProvider)) { value = true }
addSource(Preferences.asLiveData(Preferences::installedIntegrations)) { value = true }
}

View File

@ -32,6 +32,7 @@ import com.tommasoberlose.anotherwidget.receivers.WidgetClickListenerReceiver
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.convertDpToPixel
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
import com.tommasoberlose.anotherwidget.utils.toPixel
import java.text.DateFormat
import java.util.*
import java.util.concurrent.TimeUnit
@ -54,13 +55,19 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
"setImageAlpha",
ColorHelper.getBackgroundAlpha(context.isDarkTheme())
)
val refreshIntent = PendingIntent.getActivity(
val margin = Preferences.widgetMargin.convertDpToPixel(context).toInt()
views.setViewPadding(R.id.widget_shape_background, margin, margin, margin, margin)
val refreshIntent = IntentHelper.getPendingIntent(
context,
appWidgetId,
IntentHelper.getWidgetUpdateIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.widget_shape_background, refreshIntent)
// Padding
val padding = (Preferences.widgetPadding.convertDpToPixel(context) + Preferences.widgetMargin.convertDpToPixel(context)).toInt()
views.setViewPadding(R.id.main_layout, padding, padding, padding, padding)
} catch (ex: Exception) {
ex.printStackTrace()
CrashlyticsReceiver.sendCrash(context, ex)
@ -73,9 +80,10 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
try {
val generatedBinding = generateWidgetView(typeface) ?: return null
val width = w - (Preferences.widgetPadding.convertDpToPixel(context) + Preferences.widgetMargin.convertDpToPixel(context)).toInt() * 2
views.setImageViewBitmap(
R.id.bitmap_container,
BitmapHelper.getBitmapFromView(generatedBinding.root, width = w)
BitmapHelper.getBitmapFromView(generatedBinding.root, width)
)
views = updateGridView(generatedBinding, views, appWidgetId)
} catch (ex: Exception) {
@ -96,7 +104,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
// Weather
if (Preferences.showWeather && Preferences.weatherIcon != "") {
views.setViewVisibility(R.id.weather_rect, View.VISIBLE)
views.setViewVisibility(R.id.weather_sub_line, View.GONE)
views.setViewVisibility(R.id.weather_sub_line_rect, View.GONE)
val i = Intent(context, WidgetClickListenerReceiver::class.java)
i.action = Actions.ACTION_OPEN_WEATHER_INTENT
@ -116,7 +124,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
)
} else {
views.setViewVisibility(R.id.weather_rect, View.GONE)
views.setViewVisibility(R.id.weather_sub_line, View.GONE)
views.setViewVisibility(R.id.weather_sub_line_rect, View.GONE)
}
@ -126,7 +134,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
BitmapHelper.getBitmapFromView(bindingView.date, draw = false, width = bindingView.date.width, height = bindingView.date.height)
)
val calPIntent = PendingIntent.getActivity(
val calPIntent = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getCalendarIntent(context),
@ -135,8 +143,6 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
views.setOnClickPendingIntent(R.id.date_rect, calPIntent)
views.setViewVisibility(R.id.first_line_rect, View.VISIBLE)
val nextAlarm = AlarmHelper.getNextAlarm(context)
// Spacing
views.setViewVisibility(
R.id.sub_line_top_margin_small_sans,
@ -179,7 +185,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
}
// Event intent
val eventIntent = PendingIntent.getActivity(
val eventIntent = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getEventIntent(context, nextEvent),
@ -216,7 +222,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
// Event information
if (nextEvent.address != "" && Preferences.secondRowInformation == 1) {
val mapIntent = PendingIntent.getActivity(
val mapIntent = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getGoogleMapsIntentFromAddress(context, nextEvent.address),
@ -224,14 +230,10 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
)
views.setOnClickPendingIntent(R.id.sub_line_rect, mapIntent)
} else {
val pIntentDetail = PendingIntent.getActivity(
val pIntentDetail = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getEventIntent(
context,
nextEvent,
forceEventDetails = true
),
IntentHelper.getCalendarIntent(context, nextEvent.startDate),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, pIntentDetail)
@ -239,19 +241,16 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
views.setViewVisibility(R.id.calendar_layout_rect, View.VISIBLE)
views.setViewVisibility(R.id.sub_line_rect, View.VISIBLE)
views.setViewVisibility(R.id.weather_sub_line_rect, View.VISIBLE)
views.setViewVisibility(R.id.weather_sub_line_rect, if (Preferences.showWeather && Preferences.weatherIcon != "") View.VISIBLE else View.GONE)
views.setViewVisibility(R.id.first_line_rect, View.GONE)
views.setViewVisibility(R.id.sub_line_top_margin_small_sans, View.GONE)
views.setViewVisibility(R.id.sub_line_top_margin_medium_sans, View.GONE)
views.setViewVisibility(R.id.sub_line_top_margin_large_sans, View.GONE)
} else if (GlanceProviderHelper.showGlanceProviders(context)) {
var showSomething = false
var isWeatherShown = false
loop@ for (provider: Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(context)) {
when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> {
if (MediaPlayerHelper.isSomeonePlaying(context)) {
val musicIntent = PendingIntent.getActivity(
val musicIntent = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getMusicIntent(context),
@ -263,23 +262,26 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
}
}
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
if (Preferences.showNextAlarm && nextAlarm != "") {
val alarmIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getClockIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, alarmIntent)
showSomething = true
break@loop
if (Preferences.showNextAlarm) {
val nextAlarm = AlarmHelper.getNextAlarm(context)
if (nextAlarm != "") {
val alarmIntent = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getClockIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, alarmIntent)
showSomething = true
break@loop
}
}
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
if (Preferences.showBatteryCharging) {
BatteryHelper.updateBatteryInfo(context)
if (Preferences.isCharging || Preferences.isBatteryLevelLow) {
val batteryIntent = PendingIntent.getActivity(
val batteryIntent = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getBatteryIntent(),
@ -293,12 +295,13 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
}
Constants.GlanceProviderId.CUSTOM_INFO -> {
if (Preferences.customNotes.isNotEmpty()) {
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
if (Preferences.showDailySteps && Preferences.googleFitSteps > 0) {
val fitIntent = PendingIntent.getActivity(
val fitIntent = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getFitIntent(context),
@ -319,7 +322,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
remotePackageContext,
Preferences.lastNotificationIcon)
}
val notificationIntent = PendingIntent.getActivity(
val notificationIntent = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getNotificationIntent(context),
@ -343,7 +346,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
Constants.GlanceProviderId.EVENTS -> {
if (Preferences.showEventsAsGlanceProvider&& Preferences.showEvents && context.checkGrantedPermission(
Manifest.permission.READ_CALENDAR) && nextEvent != null) {
val pIntentDetail = PendingIntent.getActivity(
val pIntentDetail = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getEventIntent(
@ -361,13 +364,29 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
break@loop
}
}
Constants.GlanceProviderId.WEATHER -> {
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)
views.setOnClickPendingIntent(
R.id.sub_line_rect,
weatherPIntent
)
showSomething = true
isWeatherShown = true
break@loop
}
}
}
}
if (showSomething) {
views.setViewVisibility(R.id.first_line_rect, View.VISIBLE)
views.setViewVisibility(R.id.weather_rect, View.VISIBLE)
views.setViewVisibility(R.id.weather_rect, if (isWeatherShown) View.GONE else View.VISIBLE)
views.setViewVisibility(R.id.sub_line_rect, View.VISIBLE)
views.setViewVisibility(R.id.calendar_layout_rect, View.GONE)
@ -406,8 +425,9 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
// Generates the widget bitmap from the view
fun generateWidgetView(typeface: Typeface? = null): LeftAlignedWidgetBinding? {
private fun generateWidgetView(typeface: Typeface? = null): LeftAlignedWidgetBinding? {
try {
var isWeatherShownAsGlanceProvider = false
val eventRepository = EventRepository(context)
val nextEvent = eventRepository.getNextEvent()
val eventsCount = eventRepository.getEventsCount()
@ -461,8 +481,6 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
bindingView.date.text = DateHelper.getDateText(context, now)
val nextAlarm = AlarmHelper.getNextAlarm(context)
if (Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null && !Preferences.showEventsAsGlanceProvider) {
// Multiple counter
bindingView.actionNext.isVisible =
@ -563,18 +581,16 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
}
} else {
val flags: Int =
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_ABBREV_MONTH
val start = Calendar.getInstance().apply { timeInMillis = nextEvent.startDate }
bindingView.subLineText.text = if (now.get(Calendar.DAY_OF_YEAR) == start.get(
Calendar.DAY_OF_YEAR)) {
DateUtils.formatDateTime(context, nextEvent.startDate, flags)
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)) {
DateUtils.formatDateTime(context, now.timeInMillis, flags)
DateHelper.getDateText(context, now)
} else {
DateUtils.formatDateTime(context, nextEvent.startDate, flags)
DateHelper.getDateText(context, start)
}
}
}
@ -582,11 +598,14 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
bindingView.dateLayout.isVisible = false
bindingView.calendarLayout.isVisible = true
bindingView.subLine.isVisible = true
bindingView.weatherSubLine.isVisible = true
bindingView.weatherSubLine.isVisible = Preferences.showWeather && Preferences.weatherIcon != ""
bindingView.subLineTopMarginSmall.visibility = View.GONE
bindingView.subLineTopMarginMedium.visibility = View.GONE
bindingView.subLineTopMarginLarge.visibility = View.GONE
bindingView.subLineTopMarginSmall.visibility =
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.SMALL.rawValue) View.VISIBLE else View.GONE
bindingView.subLineTopMarginMedium.visibility =
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.MEDIUM.rawValue) View.VISIBLE else View.GONE
bindingView.subLineTopMarginLarge.visibility =
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.rawValue) View.VISIBLE else View.GONE
} else if (GlanceProviderHelper.showGlanceProviders(context)) {
bindingView.subLineIcon.isVisible = true
var showSomething = false
@ -608,16 +627,19 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
}
}
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
if (Preferences.showNextAlarm && nextAlarm != "") {
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_alarm_24
if (Preferences.showNextAlarm) {
val nextAlarm = AlarmHelper.getNextAlarm(context)
if (nextAlarm != "") {
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_alarm_24
)
)
)
bindingView.subLineText.text = AlarmHelper.getNextAlarm(context)
showSomething = true
break@loop
bindingView.subLineText.text = nextAlarm
showSomething = true
break@loop
}
}
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
@ -721,6 +743,33 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
break@loop
}
}
Constants.GlanceProviderId.WEATHER -> {
if (Preferences.showWeatherAsGlanceProvider && Preferences.showWeather && Preferences.weatherIcon != "") {
bindingView.subLineText.text = String.format(
Locale.getDefault(),
"%d°%s %s",
Preferences.weatherTemp.roundToInt(),
Preferences.weatherRealTempUnit,
WeatherHelper.getWeatherLabel(context, Preferences.weatherIcon)
)
bindingView.subLineIcon.isVisible = true
val icon: String = Preferences.weatherIcon
if (icon == "") {
bindingView.subLineIcon.isVisible = false
} else {
bindingView.subLineIcon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon))
bindingView.subLineIcon.isVisible = true
}
bindingView.weatherDateLine.isVisible = false
bindingView.weatherSubLine.isVisible = false
showSomething = true
isWeatherShownAsGlanceProvider = true
break@loop
}
}
}
}
@ -772,16 +821,22 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
it.setTextColor(ColorHelper.getSecondaryFontColor(context.applicationContext.isDarkTheme()))
}
if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.rawValue) {
listOf<ImageView>(bindingView.subLineIcon, bindingView.subLineIconShadow)
} else {
listOf<ImageView>(bindingView.subLineIcon, bindingView.weatherSubLineWeatherIcon, bindingView.subLineIconShadow)
}.forEach {
it.setColorFilter(ColorHelper.getSecondaryFontColorRgb(context.applicationContext.isDarkTheme()))
it.alpha =
(if (context.isDarkTheme()) Preferences.textSecondaryAlphaDark.toIntValue()
.toFloat() else Preferences.textSecondaryAlpha.toIntValue()
.toFloat()) / 100
if (!isWeatherShownAsGlanceProvider) {
if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.rawValue) {
listOf<ImageView>(bindingView.subLineIcon, bindingView.subLineIconShadow)
} else {
listOf<ImageView>(
bindingView.subLineIcon,
bindingView.weatherSubLineWeatherIcon,
bindingView.subLineIconShadow
)
}.forEach {
it.setColorFilter(ColorHelper.getSecondaryFontColorRgb(context.applicationContext.isDarkTheme()))
it.alpha =
(if (context.isDarkTheme()) Preferences.textSecondaryAlphaDark.toIntValue()
.toFloat() else Preferences.textSecondaryAlpha.toIntValue()
.toFloat()) / 100
}
}
// Text Size
@ -791,48 +846,54 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
bindingView.nextEvent to Preferences.textMainSize,
bindingView.nextEventDifferenceTime to Preferences.textMainSize,
bindingView.subLineText to Preferences.textSecondSize,
bindingView.weatherSubLineDivider to (Preferences.textSecondSize - 2),
bindingView.weatherSubLineDivider to (Preferences.textSecondSize * 0.9f),
bindingView.weatherSubLineTemperature to Preferences.textSecondSize,
).forEach {
it.first.setTextSize(TypedValue.COMPLEX_UNIT_SP, it.second)
if (!it.first.includeFontPadding && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P)
it.first.isFallbackLineSpacing = false
}
// Icons scale
bindingView.subLineIcon.scaleX = Preferences.textSecondSize / 18f
bindingView.subLineIcon.scaleY = Preferences.textSecondSize / 18f
bindingView.weatherSubLineWeatherIcon.scaleX = Preferences.textSecondSize / 18f
bindingView.weatherSubLineWeatherIcon.scaleY = Preferences.textSecondSize / 18f
bindingView.weatherDateLineWeatherIcon.scaleX = ((Preferences.textMainSize + Preferences.textSecondSize) / 2) / 20f
bindingView.weatherDateLineWeatherIcon.scaleY = ((Preferences.textMainSize + Preferences.textSecondSize) / 2) / 20f
bindingView.actionNext.scaleX = Preferences.textMainSize / 28f
bindingView.actionNext.scaleY = Preferences.textMainSize / 28f
listOf(
bindingView.subLineIcon to Preferences.textSecondSize / 16f,
bindingView.subLineIconShadow to Preferences.textSecondSize / 16f,
bindingView.weatherSubLineWeatherIcon to Preferences.textSecondSize / 16f,
bindingView.weatherDateLineWeatherIcon to ((Preferences.textMainSize + Preferences.textSecondSize) / 2) / 24f,
bindingView.actionNext to Preferences.textMainSize / 24f,
bindingView.actionNextShadow to Preferences.textMainSize / 24f
).forEach {
if (it.first.tag == null)
it.first.tag = it.first.layoutParams.height
it.first.layoutParams = it.first.layoutParams.apply {
height = ((it.first.tag as Int) * it.second).roundToInt()
width = height
}
}
// Shadows
val shadowRadius =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> 0f
1 -> 5f
2 -> 5f
else -> 5f
}
1 -> 2f
2 -> 3f
else -> 2f
}.toPixel(context)
val shadowColor =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> Color.TRANSPARENT
1 -> R.color.black_50
1 -> Color.DKGRAY
2 -> Color.BLACK
else -> R.color.black_50
else -> Color.DKGRAY
}
val shadowDy =
val shadowOffset =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> 0f
1 -> 0f
2 -> 1f
2 -> 0.5f
else -> 0f
}
}.toPixel(context)
listOf<TextView>(
bindingView.date,
@ -843,7 +904,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
bindingView.weatherSubLineDivider,
bindingView.weatherSubLineTemperature,
).forEach {
it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor)
it.setShadowLayer(shadowRadius, shadowOffset, shadowOffset, shadowColor)
}
// Icons shadow
@ -857,7 +918,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
it.second.isVisible = it.first.isVisible
it.second.scaleX = it.first.scaleX
it.second.scaleY = it.first.scaleY
it.second.applyShadow(it.first)
it.second.applyShadow(it.first, 0.8f)
}
}
@ -876,14 +937,17 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
// Custom Font
if (Preferences.customFont == Constants.CUSTOM_FONT_GOOGLE_SANS) {
val googleSans: Typeface = when (Preferences.customFontVariant) {
"100" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_thin.ttf")
"200" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_light.ttf")
"500" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_medium.ttf")
"700" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_bold.ttf")
"800" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_black.ttf")
else -> Typeface.createFromAsset(context.assets, "fonts/google_sans_regular.ttf")
}
val googleSans: Typeface? = androidx.core.content.res.ResourcesCompat.getFont(
context,
when (Preferences.customFontVariant) {
"100" -> R.font.google_sans_thin
"200" -> R.font.google_sans_light
"500" -> R.font.google_sans_medium
"700" -> R.font.google_sans_bold
"800" -> R.font.google_sans_black
else -> R.font.google_sans_regular
}
)
listOf<TextView>(
bindingView.date,
@ -912,10 +976,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
// Dividers
arrayOf(bindingView.weatherSubLineDivider).forEach {
it.visibility = if (Preferences.showDividers) View.VISIBLE else View.INVISIBLE
it.layoutParams = (it.layoutParams as ViewGroup.MarginLayoutParams).apply {
this.marginEnd = if (Preferences.showDividers) 8f.convertDpToPixel(context).toInt() else 0
}
it.visibility = if (Preferences.showDividers) View.VISIBLE else View.GONE
}
// Right Aligned
@ -925,8 +986,11 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
}
bindingView.mainContent.gravity = Gravity.END
bindingView.dateLayout.gravity = Gravity.END
bindingView.date.gravity = Gravity.END
bindingView.calendarLayout.gravity = Gravity.END or Gravity.CENTER_VERTICAL
bindingView.nextEvent.gravity = Gravity.END
bindingView.subLineContainer.gravity = Gravity.END or Gravity.CENTER_VERTICAL
bindingView.subLineText.gravity = Gravity.END
}
return bindingView

View File

@ -32,14 +32,14 @@ class ClockWidget(val context: Context) {
views.setTextViewTextSize(
R.id.time,
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(context)
Preferences.clockTextSize
)
views.setTextViewTextSize(
R.id.time_am_pm,
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(context) / 5 * 2
Preferences.clockTextSize / 5 * 2
)
val clockPIntent = PendingIntent.getActivity(
val clockPIntent = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getClockIntent(context),
@ -80,19 +80,29 @@ class ClockWidget(val context: Context) {
views.setTextViewTextSize(
R.id.alt_timezone_time,
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(context) / 3
Preferences.clockTextSize / 3
)
views.setTextViewTextSize(
R.id.alt_timezone_time_am_pm,
TypedValue.COMPLEX_UNIT_SP,
(Preferences.clockTextSize.toPixel(context) / 3) / 5 * 2
(Preferences.clockTextSize / 3) / 5 * 2
)
views.setTextViewTextSize(
R.id.alt_timezone_label,
TypedValue.COMPLEX_UNIT_SP,
(Preferences.clockTextSize.toPixel(context) / 3) / 5 * 2
(Preferences.clockTextSize / 3) / 5 * 2
)
val padding = (TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize,
context.resources.displayMetrics
) * 0.2).toInt()
if (Preferences.widgetAlign == Constants.WidgetAlign.RIGHT.rawValue)
views.setViewPadding(R.id.timezones_container, 0, padding, padding, 0)
else
views.setViewPadding(R.id.timezones_container, padding, padding, 0,0)
views.setOnClickPendingIntent(R.id.timezones_container, clockPIntent)
views.setViewVisibility(R.id.timezones_container, View.VISIBLE)
} else {

View File

@ -7,7 +7,7 @@ import android.content.Context
import android.content.res.Resources
import android.graphics.Typeface
import android.os.Bundle
import androidx.viewbinding.ViewBinding
import android.widget.RemoteViews
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.*
@ -63,17 +63,13 @@ class MainWidget : AppWidgetProvider() {
internal fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager,
appWidgetId: Int) {
val displayMetrics = Resources.getSystem().displayMetrics
val width = displayMetrics.widthPixels
val height = displayMetrics.heightPixels
val dimensions = WidgetHelper.WidgetSizeProvider(context, appWidgetManager).getWidgetsSize(appWidgetId)
WidgetHelper.runWithCustomTypeface(context) {
val views = when (Preferences.widgetAlign) {
Constants.WidgetAlign.LEFT.rawValue -> AlignedWidget(context).generateWidget(appWidgetId, min(dimensions.first - 8.toPixel(context), min(width, height) - 16.toPixel(context)), it)
Constants.WidgetAlign.RIGHT.rawValue -> AlignedWidget(context, rightAligned = true).generateWidget(appWidgetId, min(dimensions.first - 8.toPixel(context), min(width, height) - 16.toPixel(context)), it)
else -> StandardWidget(context).generateWidget(appWidgetId, min(dimensions.first - 8.toPixel(context), min(width, height) - 16.toPixel(context)), it)
Constants.WidgetAlign.LEFT.rawValue -> AlignedWidget(context).generateWidget(appWidgetId, dimensions.first, it)
Constants.WidgetAlign.RIGHT.rawValue -> AlignedWidget(context, rightAligned = true).generateWidget(appWidgetId, dimensions.first, it)
else -> StandardWidget(context).generateWidget(appWidgetId, dimensions.first, it)
}
try {
if (views != null) appWidgetManager.updateAppWidget(appWidgetId, views)
@ -83,11 +79,18 @@ class MainWidget : AppWidgetProvider() {
}
}
fun getWidgetView(context: Context, typeface: Typeface?): ViewBinding? {
fun getWidgetView(context: Context, width: Int, typeface: Typeface?): RemoteViews? {
return when (Preferences.widgetAlign) {
Constants.WidgetAlign.LEFT.rawValue -> AlignedWidget(context).generateWidgetView(typeface)
Constants.WidgetAlign.RIGHT.rawValue -> AlignedWidget(context, rightAligned = true).generateWidgetView(typeface)
else -> StandardWidget(context).generateWidgetView(typeface)
Constants.WidgetAlign.LEFT.rawValue -> AlignedWidget(context).generateWidget(
0,
width,
typeface
)
Constants.WidgetAlign.RIGHT.rawValue -> AlignedWidget(
context,
rightAligned = true
).generateWidget(0, width, typeface)
else -> StandardWidget(context).generateWidget(0, width, typeface)
}
}
}

View File

@ -57,13 +57,19 @@ class StandardWidget(val context: Context) {
"setImageAlpha",
ColorHelper.getBackgroundAlpha(context.isDarkTheme())
)
val refreshIntent = PendingIntent.getActivity(
val margin = Preferences.widgetMargin.convertDpToPixel(context).toInt()
views.setViewPadding(R.id.widget_shape_background, margin, margin, margin, margin)
val refreshIntent = IntentHelper.getPendingIntent(
context,
appWidgetId,
IntentHelper.getWidgetUpdateIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.widget_shape_background, refreshIntent)
// Padding
val padding = (Preferences.widgetPadding.convertDpToPixel(context) + Preferences.widgetMargin.convertDpToPixel(context)).toInt()
views.setViewPadding(R.id.main_layout, padding, padding, padding, padding)
} catch (ex: Exception) {
ex.printStackTrace()
CrashlyticsReceiver.sendCrash(context, ex)
@ -76,9 +82,10 @@ class StandardWidget(val context: Context) {
try {
val generatedBinding = generateWidgetView(typeface) ?: return null
val width = w - (Preferences.widgetPadding.convertDpToPixel(context) + Preferences.widgetMargin.convertDpToPixel(context)).toInt() * 2
views.setImageViewBitmap(
R.id.bitmap_container,
BitmapHelper.getBitmapFromView(generatedBinding.root, width = w)
BitmapHelper.getBitmapFromView(generatedBinding.root, width)
)
views = updateGridView(generatedBinding, views, appWidgetId)
} catch (ex: Exception) {
@ -99,7 +106,7 @@ class StandardWidget(val context: Context) {
// Weather
if (Preferences.showWeather && Preferences.weatherIcon != "") {
views.setViewVisibility(R.id.weather_rect, View.VISIBLE)
views.setViewVisibility(R.id.weather_sub_line, View.GONE)
views.setViewVisibility(R.id.weather_sub_line_rect, View.GONE)
val i = Intent(context, WidgetClickListenerReceiver::class.java)
i.action = Actions.ACTION_OPEN_WEATHER_INTENT
@ -119,7 +126,7 @@ class StandardWidget(val context: Context) {
)
} else {
views.setViewVisibility(R.id.weather_rect, View.GONE)
views.setViewVisibility(R.id.weather_sub_line, View.GONE)
views.setViewVisibility(R.id.weather_sub_line_rect, View.GONE)
}
@ -129,7 +136,7 @@ class StandardWidget(val context: Context) {
BitmapHelper.getBitmapFromView(bindingView.date, draw = false, width = bindingView.date.width, height = bindingView.date.height)
)
val calPIntent = PendingIntent.getActivity(
val calPIntent = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getCalendarIntent(context),
@ -144,8 +151,6 @@ class StandardWidget(val context: Context) {
BitmapHelper.getBitmapFromView(bindingView.subLine, draw = false, width = bindingView.subLine.width, height = bindingView.subLine.height)
)
val nextAlarm = AlarmHelper.getNextAlarm(context)
// Spacing
views.setViewVisibility(
R.id.sub_line_top_margin_small_sans,
@ -209,7 +214,7 @@ class StandardWidget(val context: Context) {
}
// Event intent
val eventIntent = PendingIntent.getActivity(
val eventIntent = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getEventIntent(context, nextEvent),
@ -242,7 +247,7 @@ class StandardWidget(val context: Context) {
// Event information
if (nextEvent.address != "" && Preferences.secondRowInformation == 1) {
val mapIntent = PendingIntent.getActivity(
val mapIntent = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getGoogleMapsIntentFromAddress(context, nextEvent.address),
@ -250,14 +255,10 @@ class StandardWidget(val context: Context) {
)
views.setOnClickPendingIntent(R.id.sub_line_rect, mapIntent)
} else {
val pIntentDetail = PendingIntent.getActivity(
val pIntentDetail = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getEventIntent(
context,
nextEvent,
forceEventDetails = true
),
IntentHelper.getCalendarIntent(context, nextEvent.startDate),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, pIntentDetail)
@ -269,17 +270,18 @@ class StandardWidget(val context: Context) {
)
views.setViewVisibility(R.id.calendar_layout_rect, View.VISIBLE)
views.setViewVisibility(R.id.sub_line_rect, View.VISIBLE)
views.setViewVisibility(R.id.weather_sub_line_rect, View.VISIBLE)
views.setViewVisibility(R.id.weather_sub_line_rect, if (Preferences.showWeather && Preferences.weatherIcon != "") View.VISIBLE else View.GONE)
views.setViewVisibility(R.id.first_line_rect, View.GONE)
} else if (GlanceProviderHelper.showGlanceProviders(context)) {
var showSomething = false
var isWeatherShown = false
loop@ for (provider: Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(context)) {
when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> {
if (MediaPlayerHelper.isSomeonePlaying(context)) {
val musicIntent = PendingIntent.getActivity(
val musicIntent = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getMusicIntent(context),
@ -291,23 +293,26 @@ class StandardWidget(val context: Context) {
}
}
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
if (Preferences.showNextAlarm && nextAlarm != "") {
val alarmIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getClockIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, alarmIntent)
showSomething = true
break@loop
if (Preferences.showNextAlarm) {
val nextAlarm = AlarmHelper.getNextAlarm(context)
if (nextAlarm != "") {
val alarmIntent = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getClockIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, alarmIntent)
showSomething = true
break@loop
}
}
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
if (Preferences.showBatteryCharging) {
BatteryHelper.updateBatteryInfo(context)
if (Preferences.isCharging || Preferences.isBatteryLevelLow) {
val batteryIntent = PendingIntent.getActivity(
val batteryIntent = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getBatteryIntent(),
@ -321,12 +326,13 @@ class StandardWidget(val context: Context) {
}
Constants.GlanceProviderId.CUSTOM_INFO -> {
if (Preferences.customNotes.isNotEmpty()) {
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
if (Preferences.showDailySteps && Preferences.googleFitSteps > 0) {
val fitIntent = PendingIntent.getActivity(
val fitIntent = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getFitIntent(context),
@ -347,7 +353,7 @@ class StandardWidget(val context: Context) {
remotePackageContext,
Preferences.lastNotificationIcon)
}
val notificationIntent = PendingIntent.getActivity(
val notificationIntent = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getNotificationIntent(context),
@ -371,7 +377,7 @@ class StandardWidget(val context: Context) {
Constants.GlanceProviderId.EVENTS -> {
if (Preferences.showEventsAsGlanceProvider&& Preferences.showEvents && context.checkGrantedPermission(
Manifest.permission.READ_CALENDAR) && nextEvent != null) {
val pIntentDetail = PendingIntent.getActivity(
val pIntentDetail = IntentHelper.getPendingIntent(
context,
widgetID,
IntentHelper.getEventIntent(
@ -389,6 +395,21 @@ class StandardWidget(val context: Context) {
break@loop
}
}
Constants.GlanceProviderId.WEATHER -> {
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)
views.setOnClickPendingIntent(
R.id.sub_line_rect,
weatherPIntent
)
showSomething = true
isWeatherShown = true
break@loop
}
}
}
}
@ -399,6 +420,7 @@ class StandardWidget(val context: Context) {
BitmapHelper.getBitmapFromView(bindingView.subLine, draw = false, width = bindingView.subLine.width, height = bindingView.subLine.height)
)
views.setViewVisibility(R.id.weather_rect, if (isWeatherShown) View.GONE else View.VISIBLE)
views.setViewVisibility(R.id.first_line_rect, View.VISIBLE)
views.setViewVisibility(R.id.sub_line_rect, View.VISIBLE)
@ -432,8 +454,9 @@ class StandardWidget(val context: Context) {
// Generates the widget bitmap from the view
fun generateWidgetView(typeface: Typeface? = null): TheWidgetBinding? {
private fun generateWidgetView(typeface: Typeface? = null): TheWidgetBinding? {
try {
var isWeatherShownAsGlanceProvider = false
val eventRepository = EventRepository(context)
val nextEvent = eventRepository.getNextEvent()
val eventsCount = eventRepository.getEventsCount()
@ -488,8 +511,6 @@ class StandardWidget(val context: Context) {
bindingView.date.text = DateHelper.getDateText(context, now)
val nextAlarm = AlarmHelper.getNextAlarm(context)
if (Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null && !Preferences.showEventsAsGlanceProvider) {
// Multiple counter
bindingView.actionNext.isVisible =
@ -592,18 +613,16 @@ class StandardWidget(val context: Context) {
}
} else {
val flags: Int =
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_ABBREV_MONTH
val start = Calendar.getInstance().apply { timeInMillis = nextEvent.startDate }
bindingView.subLineText.text = if (now.get(Calendar.DAY_OF_YEAR) == start.get(
Calendar.DAY_OF_YEAR)) {
DateUtils.formatDateTime(context, nextEvent.startDate, flags)
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)) {
DateUtils.formatDateTime(context, now.timeInMillis, flags)
DateHelper.getDateText(context, now)
} else {
DateUtils.formatDateTime(context, nextEvent.startDate, flags)
DateHelper.getDateText(context, start)
}
}
}
@ -611,7 +630,7 @@ class StandardWidget(val context: Context) {
bindingView.dateLayout.isVisible = false
bindingView.calendarLayout.isVisible = true
bindingView.subLine.isVisible = true
bindingView.weatherSubLine.isVisible = true
bindingView.weatherSubLine.isVisible = Preferences.showWeather && Preferences.weatherIcon != ""
bindingView.subLineTopMarginSmall.visibility =
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.SMALL.rawValue) View.VISIBLE else View.GONE
@ -640,16 +659,19 @@ class StandardWidget(val context: Context) {
}
}
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
if (Preferences.showNextAlarm && nextAlarm != "") {
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_alarm_24
if (Preferences.showNextAlarm) {
val nextAlarm = AlarmHelper.getNextAlarm(context)
if (nextAlarm != "") {
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_alarm_24
)
)
)
bindingView.subLineText.text = AlarmHelper.getNextAlarm(context)
showSomething = true
break@loop
bindingView.subLineText.text = nextAlarm
showSomething = true
break@loop
}
}
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
@ -753,6 +775,33 @@ class StandardWidget(val context: Context) {
break@loop
}
}
Constants.GlanceProviderId.WEATHER -> {
if (Preferences.showWeatherAsGlanceProvider && Preferences.showWeather && Preferences.weatherIcon != "") {
bindingView.subLineText.text = String.format(
Locale.getDefault(),
"%d°%s %s",
Preferences.weatherTemp.roundToInt(),
Preferences.weatherRealTempUnit,
WeatherHelper.getWeatherLabel(context, Preferences.weatherIcon)
)
bindingView.subLineIcon.isVisible = true
val icon: String = Preferences.weatherIcon
if (icon == "") {
bindingView.subLineIcon.isVisible = false
} else {
bindingView.subLineIcon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon))
bindingView.subLineIcon.isVisible = true
}
bindingView.weatherDateLine.isVisible = false
bindingView.weatherSubLine.isVisible = false
isWeatherShownAsGlanceProvider = true
showSomething = true
break@loop
}
}
}
}
@ -806,71 +855,81 @@ class StandardWidget(val context: Context) {
it.setTextColor(ColorHelper.getSecondaryFontColor(context.applicationContext.isDarkTheme()))
}
if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.rawValue) {
listOf<ImageView>(bindingView.subLineIcon, bindingView.subLineIconShadow)
} else {
listOf<ImageView>(bindingView.subLineIcon, bindingView.weatherSubLineWeatherIcon, bindingView.subLineIconShadow)
}.forEach {
it.setColorFilter(ColorHelper.getSecondaryFontColorRgb(context.applicationContext.isDarkTheme()))
it.alpha =
(if (context.isDarkTheme()) Preferences.textSecondaryAlphaDark.toIntValue()
.toFloat() else Preferences.textSecondaryAlpha.toIntValue()
.toFloat()) / 100
if (!isWeatherShownAsGlanceProvider) {
if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.rawValue) {
listOf<ImageView>(bindingView.subLineIcon, bindingView.subLineIconShadow)
} else {
listOf<ImageView>(
bindingView.subLineIcon,
bindingView.weatherSubLineWeatherIcon,
bindingView.subLineIconShadow
)
}.forEach {
it.setColorFilter(ColorHelper.getSecondaryFontColorRgb(context.applicationContext.isDarkTheme()))
it.alpha =
(if (context.isDarkTheme()) Preferences.textSecondaryAlphaDark.toIntValue()
.toFloat() else Preferences.textSecondaryAlpha.toIntValue()
.toFloat()) / 100
}
}
// Text Size
listOf<Pair<TextView, Float>>(
bindingView.date to Preferences.textMainSize,
bindingView.weatherDateLineDivider to (Preferences.textMainSize - 2),
bindingView.weatherDateLineDivider to (Preferences.textMainSize * 0.9f),
bindingView.weatherDateLineTemperature to Preferences.textMainSize,
bindingView.nextEvent to Preferences.textMainSize,
bindingView.nextEventDifferenceTime to Preferences.textMainSize,
bindingView.subLineText to Preferences.textSecondSize,
bindingView.weatherSubLineDivider to (Preferences.textSecondSize - 2),
bindingView.weatherSubLineDivider to (Preferences.textSecondSize * 0.9f),
bindingView.weatherSubLineTemperature to Preferences.textSecondSize,
).forEach {
it.first.setTextSize(TypedValue.COMPLEX_UNIT_SP, it.second)
if (!it.first.includeFontPadding && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P)
it.first.isFallbackLineSpacing = false
}
// Icons scale
bindingView.subLineIcon.scaleX = Preferences.textSecondSize / 18f
bindingView.subLineIcon.scaleY = Preferences.textSecondSize / 18f
bindingView.weatherSubLineWeatherIcon.scaleX = Preferences.textSecondSize / 18f
bindingView.weatherSubLineWeatherIcon.scaleY = Preferences.textSecondSize / 18f
bindingView.weatherDateLineWeatherIcon.scaleX = Preferences.textMainSize / 18f
bindingView.weatherDateLineWeatherIcon.scaleY = Preferences.textMainSize / 18f
bindingView.actionNext.scaleX = Preferences.textMainSize / 28f
bindingView.actionNext.scaleY = Preferences.textMainSize / 28f
bindingView.actionPrevious.scaleX = Preferences.textMainSize / 28f
bindingView.actionPrevious.scaleY = Preferences.textMainSize / 28f
listOf(
bindingView.subLineIcon to Preferences.textSecondSize / 16f,
bindingView.subLineIconShadow to Preferences.textSecondSize / 16f,
bindingView.weatherSubLineWeatherIcon to Preferences.textSecondSize / 16f,
bindingView.weatherDateLineWeatherIcon to Preferences.textMainSize / 24f,
bindingView.actionNext to Preferences.textMainSize / 24f,
bindingView.actionNextShadow to Preferences.textMainSize / 24f,
bindingView.actionPrevious to Preferences.textMainSize / 24f,
bindingView.actionPreviousShadow to Preferences.textMainSize / 24f
).forEach {
if (it.first.tag == null)
it.first.tag = it.first.layoutParams.height
it.first.layoutParams = it.first.layoutParams.apply {
height = ((it.first.tag as Int) * it.second).roundToInt()
width = height
}
}
// Shadows
val shadowRadius =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> 0f
1 -> 5f
2 -> 5f
else -> 5f
}
1 -> 2f
2 -> 3f
else -> 2f
}.toPixel(context)
val shadowColor =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> Color.TRANSPARENT
1 -> R.color.black_50
1 -> Color.DKGRAY
2 -> Color.BLACK
else -> R.color.black_50
else -> Color.DKGRAY
}
val shadowDy =
val shadowOffset =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> 0f
1 -> 0f
2 -> 1f
2 -> 0.5f
else -> 0f
}
}.toPixel(context)
listOf<TextView>(
bindingView.date,
@ -882,7 +941,7 @@ class StandardWidget(val context: Context) {
bindingView.weatherSubLineDivider,
bindingView.weatherSubLineTemperature,
).forEach {
it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor)
it.setShadowLayer(shadowRadius, shadowOffset, shadowOffset, shadowColor)
}
// Icons shadow
@ -896,7 +955,7 @@ class StandardWidget(val context: Context) {
it.second.isVisible = it.first.isVisible
it.second.scaleX = it.first.scaleX
it.second.scaleY = it.first.scaleY
it.second.applyShadow(it.first)
it.second.applyShadow(it.first, 0.8f)
}
}
@ -919,14 +978,17 @@ class StandardWidget(val context: Context) {
// Custom Font
if (Preferences.customFont == Constants.CUSTOM_FONT_GOOGLE_SANS) {
val googleSans: Typeface = when (Preferences.customFontVariant) {
"100" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_thin.ttf")
"200" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_light.ttf")
"500" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_medium.ttf")
"700" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_bold.ttf")
"800" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_black.ttf")
else -> Typeface.createFromAsset(context.assets, "fonts/google_sans_regular.ttf")
}
val googleSans: Typeface? = androidx.core.content.res.ResourcesCompat.getFont(
context,
when (Preferences.customFontVariant) {
"100" -> R.font.google_sans_thin
"200" -> R.font.google_sans_light
"500" -> R.font.google_sans_medium
"700" -> R.font.google_sans_bold
"800" -> R.font.google_sans_black
else -> R.font.google_sans_regular
}
)
listOf<TextView>(
bindingView.date,
@ -957,10 +1019,7 @@ class StandardWidget(val context: Context) {
// Dividers
arrayOf(bindingView.weatherDateLineDivider, bindingView.weatherSubLineDivider).forEach {
it.visibility = if (Preferences.showDividers) View.VISIBLE else View.INVISIBLE
it.layoutParams = (it.layoutParams as ViewGroup.MarginLayoutParams).apply {
this.marginEnd = if (Preferences.showDividers) 8f.convertDpToPixel(context).toInt() else 0
}
it.visibility = if (Preferences.showDividers) View.VISIBLE else View.GONE
}

View File

@ -24,18 +24,20 @@ import android.util.TypedValue
import android.view.animation.AlphaAnimation
import android.widget.RelativeLayout
import androidx.annotation.UiThread
import androidx.appcompat.app.AppCompatDelegate
import androidx.browser.customtabs.CustomTabColorSchemeParams
import androidx.core.animation.addListener
import androidx.core.view.isVisible
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.OnSingleClickListener
import com.tommasoberlose.anotherwidget.global.Preferences
import java.util.*
fun PackageManager.missingSystemFeature(name: String): Boolean = !hasSystemFeature(name)
fun Context.toast(message: String, long: Boolean = false) {
val toast = Toast.makeText(this, message, if (long) Toast.LENGTH_LONG else Toast.LENGTH_SHORT)
val toast = Toast.makeText(applicationContext, message, if (long) Toast.LENGTH_LONG else Toast.LENGTH_SHORT)
// toast.setGravity(Gravity.CENTER, 0, 0)
toast.show()
}
@ -192,7 +194,7 @@ fun String.isValidEmail(): Boolean
Patterns.EMAIL_ADDRESS.matcher(this).matches()
fun Context.isDarkTheme(): Boolean {
return resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
return Preferences.darkThemePreference == AppCompatDelegate.MODE_NIGHT_YES || Preferences.darkThemePreference == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM && resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
}
fun Activity.isNotificationAccessGranted(): Boolean = Settings.Secure.getString(this.contentResolver,"enabled_notification_listeners").contains(this.packageName)

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Some files were not shown because too many files have changed in this diff Show More