Compare commits

...

64 Commits

Author SHA1 Message Date
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
1ecaf7a11a Bumped the version 2021-05-11 21:00:57 +02:00
2578566659 Fixed #317 2021-05-11 20:56:21 +02:00
61fc0da8d0 New icon 2021-05-11 18:46:08 +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
7edb0635a7 Merge pull request #313 from chreddy/patch-5
Update Danish translation
2021-05-07 17:35:29 +02:00
d72ddd6d85 Update Danish translation 2021-05-07 17:33:24 +02:00
5d07cc8d73 Added color copy/paste, better size and color text selection 2021-05-07 17:19:23 +02:00
1ac53e09a8 Merge pull request #312 from chreddy/patch-6
Updating Danish translation
2021-05-07 15:09:55 +02:00
3412e044df Merge pull request #311 from Drumber/translation
Update German strings.xml
2021-05-07 15:09:30 +02:00
80023da430 Updating Danish translation 2021-05-07 15:09:17 +02:00
e2a2d17506 Update German strings.xml 2021-05-07 13:35:38 +02:00
b93443b736 Added right-aligned widget 2021-05-07 12:21:31 +02:00
9842ba3ea9 Bugfixes 2021-05-06 17:29:29 +02:00
75aba66987 Bugfixes 2021-05-05 18:23:01 +02:00
273 changed files with 3796 additions and 2270 deletions

View File

@ -19,6 +19,29 @@
<option name="children"> <option name="children">
<map> <map>
<entry key="clipArt"> <entry key="clipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
<entry key="imagePath" value="/private/var/folders/cw/tkrg0g5j6lzcqwr0tfkph8w80000gn/T/ic_android_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="text">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="textAsset">
<value> <value>
<PersistentState> <PersistentState>
<option name="values"> <option name="values">
@ -45,14 +68,47 @@
<PersistentState> <PersistentState>
<option name="children"> <option name="children">
<map> <map>
<entry key="foregroundClipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="imagePath" value="/private/var/folders/cw/tkrg0g5j6lzcqwr0tfkph8w80000gn/T/ic_android_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundImage"> <entry key="foregroundImage">
<value> <value>
<PersistentState> <PersistentState>
<option name="values"> <option name="values">
<map> <map>
<entry key="color" value="000000" /> <entry key="color" value="000000" />
<entry key="imagePath" value="$USER_HOME$/Desktop/logo-white.png" />
<entry key="scalingPercent" value="70" /> <entry key="scalingPercent" value="70" />
<entry key="trimmed" value="true" /> </map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundText">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundTextAsset">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map> </map>
</option> </option>
</PersistentState> </PersistentState>
@ -64,7 +120,6 @@
<map> <map>
<entry key="backgroundAssetType" value="COLOR" /> <entry key="backgroundAssetType" value="COLOR" />
<entry key="backgroundColor" value="ffffff" /> <entry key="backgroundColor" value="ffffff" />
<entry key="foregroundImage" value="$USER_HOME$/Desktop/Artboard Copy 3.png" />
<entry key="legacyIconShape" value="CIRCLE" /> <entry key="legacyIconShape" value="CIRCLE" />
</map> </map>
</option> </option>
@ -77,6 +132,29 @@
<option name="children"> <option name="children">
<map> <map>
<entry key="clipArt"> <entry key="clipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
<entry key="imagePath" value="/private/var/folders/cw/tkrg0g5j6lzcqwr0tfkph8w80000gn/T/ic_android_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="text">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="textAsset">
<value> <value>
<PersistentState> <PersistentState>
<option name="values"> <option name="values">
@ -98,6 +176,104 @@
<option name="children"> <option name="children">
<map> <map>
<entry key="clipArt"> <entry key="clipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
<entry key="imagePath" value="/private/var/folders/cw/tkrg0g5j6lzcqwr0tfkph8w80000gn/T/ic_android_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="text">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="textAsset">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="tvBanner">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="foregroundText">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="tvChannel">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="foregroundClipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="imagePath" value="/private/var/folders/cw/tkrg0g5j6lzcqwr0tfkph8w80000gn/T/ic_android_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundImage">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundText">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundTextAsset">
<value> <value>
<PersistentState> <PersistentState>
<option name="values"> <option name="values">

Binary file not shown.

2
.idea/misc.xml generated
View File

@ -61,7 +61,7 @@
</profile-state> </profile-state>
</entry> </entry>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View File

@ -3,6 +3,7 @@
<component name="RunConfigurationProducerService"> <component name="RunConfigurationProducerService">
<option name="ignoredProducers"> <option name="ignoredProducers">
<set> <set>
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" /> <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" /> <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" /> <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />

View File

@ -22,8 +22,8 @@ android {
applicationId "com.tommasoberlose.anotherwidget" applicationId "com.tommasoberlose.anotherwidget"
minSdkVersion 23 minSdkVersion 23
targetSdkVersion 30 targetSdkVersion 30
versionCode 130 versionCode 139
versionName "2.3.1" versionName "2.3.3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("String", "GOOGLE_API_KEY", apikeyProperties['GOOGLE_API_KEY']) buildConfigField("String", "GOOGLE_API_KEY", apikeyProperties['GOOGLE_API_KEY'])

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

View File

@ -31,22 +31,5 @@ class AWApplication : Application() {
.deleteRealmIfMigrationNeeded() .deleteRealmIfMigrationNeeded()
.build() .build()
Realm.setDefaultConfiguration(config) 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

@ -3,40 +3,30 @@ package com.tommasoberlose.anotherwidget.components
import android.content.Context import android.content.Context
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color import android.graphics.Color
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.GridLayout import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.SeekBar
import androidx.annotation.ColorInt
import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatImageView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.BottomSheetMenuHorBinding import com.tommasoberlose.anotherwidget.databinding.BottomSheetMenuHorBinding
import com.tommasoberlose.anotherwidget.databinding.BottomSheetMenuListBinding import com.tommasoberlose.anotherwidget.databinding.BottomSheetMenuListBinding
import com.tommasoberlose.anotherwidget.helpers.ColorHelper import com.tommasoberlose.anotherwidget.helpers.ColorHelper.copyToClipboard
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isClipboardColor
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark
import com.tommasoberlose.anotherwidget.utils.expand import com.tommasoberlose.anotherwidget.helpers.ColorHelper.pasteFromClipboard
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toIntValue
import com.tommasoberlose.anotherwidget.utils.isDarkTheme import com.tommasoberlose.anotherwidget.utils.isDarkTheme
import com.tommasoberlose.anotherwidget.utils.reveal
import com.tommasoberlose.anotherwidget.utils.toPixel
import com.warkiz.widget.IndicatorSeekBar import com.warkiz.widget.IndicatorSeekBar
import com.warkiz.widget.OnSeekChangeListener import com.warkiz.widget.OnSeekChangeListener
import com.warkiz.widget.SeekParams import com.warkiz.widget.SeekParams
import kotlinx.coroutines.* import kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter import net.idik.lib.slimadapter.SlimAdapter
import java.lang.Exception
import java.util.prefs.Preferences
class BottomSheetColorPicker( class BottomSheetColorPicker(
context: Context, context: Context,
@ -46,20 +36,45 @@ class BottomSheetColorPicker(
private val onColorSelected: ((selectedValue: Int) -> Unit)? = null, private val onColorSelected: ((selectedValue: Int) -> Unit)? = null,
private val showAlphaSelector: Boolean = false, private val showAlphaSelector: Boolean = false,
private val alpha: Int = 0, private val alpha: Int = 0,
private val onAlphaChangeListener: ((alpha: Int) -> Unit)? = null private val onAlphaChangeListener: ((alpha: Int) -> Unit)? = null,
private val hideCopyPaste: Boolean = false,
) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) { ) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
private var loadingJobs: ArrayList<Job> = ArrayList() private var loadingJobs: ArrayList<Job> = ArrayList()
private lateinit var adapter: SlimAdapter private lateinit var adapter: SlimAdapter
private var alphaDebouncing: Job? = null
private var binding: BottomSheetMenuHorBinding = BottomSheetMenuHorBinding.inflate(LayoutInflater.from(context)) private var binding: BottomSheetMenuHorBinding = BottomSheetMenuHorBinding.inflate(LayoutInflater.from(context))
private var listBinding: BottomSheetMenuListBinding = BottomSheetMenuListBinding.inflate(LayoutInflater.from(context)) private var listBinding: BottomSheetMenuListBinding = BottomSheetMenuListBinding.inflate(LayoutInflater.from(context))
override fun show() { override fun show() {
window?.setDimAmount(0f)
// Header // Header
binding.header.isVisible = header != null binding.header.isVisible = header != null
binding.headerText.text = header ?: "" binding.headerText.text = header ?: ""
if (hideCopyPaste) {
binding.actionContainer.isVisible = false
} else {
binding.actionContainer.isVisible = true
binding.actionCopy.setOnClickListener {
context.copyToClipboard(getSelected?.invoke(), alpha)
}
binding.actionPaste.setOnClickListener {
context.pasteFromClipboard { color, alpha ->
binding.alphaSelector.setProgress(alpha.toIntValue().toFloat())
adapter.notifyItemChanged(adapter.data.indexOf(getSelected?.invoke()))
onColorSelected?.invoke(Color.parseColor(color))
val idx = colors.toList().indexOf(getSelected?.invoke())
adapter.notifyItemChanged(idx)
(listBinding.root.layoutManager as GridLayoutManager).scrollToPositionWithOffset(idx,0)
}
}
binding.actionPaste.isVisible = context.isClipboardColor()
}
// Alpha // Alpha
binding.alphaSelectorContainer.isVisible = showAlphaSelector binding.alphaSelectorContainer.isVisible = showAlphaSelector
binding.alphaSelector.setProgress(alpha.toFloat()) binding.alphaSelector.setProgress(alpha.toFloat())
@ -67,8 +82,15 @@ class BottomSheetColorPicker(
binding.alphaSelector.onSeekChangeListener = object : OnSeekChangeListener { binding.alphaSelector.onSeekChangeListener = object : OnSeekChangeListener {
override fun onSeeking(seekParams: SeekParams?) { override fun onSeeking(seekParams: SeekParams?) {
seekParams?.let { seekParams?.let {
binding.textAlpha.text = "%s: %s%%".format(context.getString(R.string.alpha), it.progress) binding.textAlpha.text =
onAlphaChangeListener?.invoke(it.progress) "%s: %s%%".format(context.getString(R.string.alpha), it.progress)
alphaDebouncing?.cancel()
alphaDebouncing = GlobalScope.launch(Dispatchers.IO) {
delay(150)
withContext(Dispatchers.Main) {
onAlphaChangeListener?.invoke(it.progress)
}
}
} }
} }
override fun onStartTrackingTouch(seekBar: IndicatorSeekBar?) { override fun onStartTrackingTouch(seekBar: IndicatorSeekBar?) {
@ -78,7 +100,6 @@ class BottomSheetColorPicker(
} }
// List // List
adapter = SlimAdapter.create() adapter = SlimAdapter.create()
loadingJobs.add(GlobalScope.launch(Dispatchers.IO) { loadingJobs.add(GlobalScope.launch(Dispatchers.IO) {
@ -120,14 +141,20 @@ class BottomSheetColorPicker(
adapter.updateData(colors.toList()) adapter.updateData(colors.toList())
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
binding.colorLoader.isVisible = false binding.loader.isVisible = false
binding.listContainer.addView(listBinding.root) binding.listContainer.addView(listBinding.root)
this@BottomSheetColorPicker.behavior.state = BottomSheetBehavior.STATE_EXPANDED
binding.listContainer.isVisible = true binding.listContainer.isVisible = true
val idx = colors.toList().indexOf(getSelected?.invoke())
(listBinding.root.layoutManager as GridLayoutManager).scrollToPositionWithOffset(idx,0)
} }
}) })
setContentView(binding.root) setContentView(binding.root)
behavior.run {
skipCollapsed = true
state = BottomSheetBehavior.STATE_EXPANDED
}
super.show() super.show()
} }

View File

@ -6,6 +6,7 @@ import android.view.View
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
@ -107,6 +108,10 @@ open class BottomSheetMenu<T>(context: Context, private val header: String? = nu
} }
} }
setContentView(binding.root) setContentView(binding.root)
behavior.run {
skipCollapsed = true
state = com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
}
super.show() super.show()
} }

View File

@ -0,0 +1,111 @@
package com.tommasoberlose.anotherwidget.components
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.widget.TextView
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.card.MaterialCardView
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.BottomSheetMenuHorBinding
import com.tommasoberlose.anotherwidget.databinding.BottomSheetMenuListBinding
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.copyToClipboard
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isClipboardColor
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.pasteFromClipboard
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toIntValue
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
import com.warkiz.widget.IndicatorSeekBar
import com.warkiz.widget.OnSeekChangeListener
import com.warkiz.widget.SeekParams
import kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter
class BottomSheetPicker<T>(
context: Context,
private val items: List<MenuItem<T>> = arrayListOf(),
private val getSelected: (() -> T)? = null,
private val header: String? = null,
private val onItemSelected: ((selectedValue: T?) -> Unit)? = null,
) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
private var loadingJobs: ArrayList<Job> = ArrayList()
private lateinit var adapter: SlimAdapter
private var binding: BottomSheetMenuHorBinding = BottomSheetMenuHorBinding.inflate(
LayoutInflater.from(context))
private var listBinding: BottomSheetMenuListBinding = BottomSheetMenuListBinding.inflate(
LayoutInflater.from(context))
override fun show() {
window?.setDimAmount(0f)
// Header
binding.header.isVisible = header != null
binding.headerText.text = header ?: ""
// Alpha
binding.alphaSelectorContainer.isVisible = false
binding.actionContainer.isVisible = false
// List
adapter = SlimAdapter.create()
loadingJobs.add(GlobalScope.launch(Dispatchers.IO) {
listBinding.root.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(context)
listBinding.root.layoutManager = mLayoutManager
adapter
.register<Int>(R.layout.bottom_sheet_menu_item) { position, injector ->
val item = items[position]
val isSelected = item.value == getSelected?.invoke()
injector
.text(R.id.label, item.title)
.textColor(R.id.label, ContextCompat.getColor(context, if (isSelected) R.color.colorAccent else R.color.colorSecondaryText))
.selected(R.id.item, isSelected)
.clicked(R.id.item) {
val oldIdx = items.toList().indexOfFirst { it.value == getSelected?.invoke() }
onItemSelected?.invoke(item.value)
adapter.notifyItemChanged(position)
adapter.notifyItemChanged(oldIdx)
(listBinding.root.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(position,0)
}
}
.attachTo(listBinding.root)
adapter.updateData((items.indices).toList())
withContext(Dispatchers.Main) {
binding.loader.isVisible = false
binding.listContainer.addView(listBinding.root)
binding.listContainer.isVisible = true
val idx = items.toList().indexOfFirst { it.value == getSelected?.invoke() }
(listBinding.root.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(idx,0)
}
})
setContentView(binding.root)
behavior.run {
skipCollapsed = true
state = BottomSheetBehavior.STATE_EXPANDED
}
super.show()
}
override fun onStop() {
loadingJobs.forEach { it.cancel() }
super.onStop()
}
class MenuItem<T>(val title: String, val value: T? = null)
}

View File

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

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.NOTIFICATIONS -> context.getString(R.string.settings_show_notifications_title)
Constants.GlanceProviderId.GREETINGS -> context.getString(R.string.settings_show_greetings_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.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*/ /* 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.NOTIFICATIONS -> context.getString(R.string.settings_show_notifications_subtitle)
Constants.GlanceProviderId.GREETINGS -> context.getString(R.string.settings_show_greetings_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.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 */ /* SONG */
@ -140,6 +142,13 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
checkCalendarConfig() checkCalendarConfig()
} }
/* WEATHER */
if (provider == Constants.GlanceProviderId.WEATHER) {
binding.header.isVisible = false
binding.divider.isVisible = false
checkWeatherConfig()
}
/* TOGGLE */ /* TOGGLE */
binding.providerSwitch.setCheckedImmediatelyNoEvent(when (provider) { binding.providerSwitch.setCheckedImmediatelyNoEvent(when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> Preferences.showMusic 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.NOTIFICATIONS -> Preferences.showNotifications
Constants.GlanceProviderId.GREETINGS -> Preferences.showGreetings Constants.GlanceProviderId.GREETINGS -> Preferences.showGreetings
Constants.GlanceProviderId.EVENTS -> Preferences.showEventsAsGlanceProvider Constants.GlanceProviderId.EVENTS -> Preferences.showEventsAsGlanceProvider
Constants.GlanceProviderId.WEATHER -> Preferences.showWeatherAsGlanceProvider
}) })
var job: Job? = null var job: Job? = null
@ -208,6 +218,9 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
Constants.GlanceProviderId.EVENTS -> { Constants.GlanceProviderId.EVENTS -> {
Preferences.showEventsAsGlanceProvider = isChecked Preferences.showEventsAsGlanceProvider = isChecked
} }
Constants.GlanceProviderId.WEATHER -> {
Preferences.showWeatherAsGlanceProvider = isChecked
}
else -> { else -> {
} }
} }
@ -217,10 +230,16 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
} }
setContentView(binding.root) setContentView(binding.root)
behavior.run {
skipCollapsed = true
state = com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
}
super.show() super.show()
} }
private fun checkNextAlarm() { private fun checkNextAlarm() {
if (!Preferences.showNextAlarm)
AlarmHelper.clearTimeout(context)
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) { with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
val alarm = nextAlarmClock val alarm = nextAlarmClock
if (alarm != null && alarm.showIntent != null) { 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() { private fun checkNotificationPermission() {
when { when {
ActiveNotificationsHelper.checkNotificationAccess(context) -> { ActiveNotificationsHelper.checkNotificationAccess(context) -> {

View File

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

View File

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

View File

@ -64,7 +64,7 @@ class EventRepository(val context: Context) {
else -> add(Calendar.HOUR, 6) else -> add(Calendar.HOUR, 6)
} }
} }
val event = if (nextEvent != null && nextEvent.endDate > now && nextEvent.startDate < limit.timeInMillis) { val event = if (nextEvent != null && nextEvent.endDate > now && nextEvent.startDate <= limit.timeInMillis) {
nextEvent nextEvent
} else { } else {
val events = getEvents() val events = getEvents()
@ -105,8 +105,10 @@ class EventRepository(val context: Context) {
} else { } else {
resetNextEventData() resetNextEventData()
} }
UpdatesReceiver.setUpdates(context)
MainWidget.updateWidget(context) MainWidget.updateWidget(context)
org.greenrobot.eventbus.EventBus.getDefault().post(
com.tommasoberlose.anotherwidget.ui.fragments.MainFragment.UpdateUiMessageEvent()
)
} }
fun goToPreviousEvent() { fun goToPreviousEvent() {
@ -121,8 +123,10 @@ class EventRepository(val context: Context) {
} else { } else {
resetNextEventData() resetNextEventData()
} }
UpdatesReceiver.setUpdates(context)
MainWidget.updateWidget(context) MainWidget.updateWidget(context)
org.greenrobot.eventbus.EventBus.getDefault().post(
com.tommasoberlose.anotherwidget.ui.fragments.MainFragment.UpdateUiMessageEvent()
)
} }
fun getFutureEvents(): List<Event> { fun getFutureEvents(): List<Event> {

View File

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

View File

@ -84,14 +84,18 @@ object Preferences : KotprefModel() {
var weatherIconPack by intPref(default = Constants.WeatherIconPack.DEFAULT.rawValue) 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 // Clock
var altTimezoneLabel by stringPref(default = "") var altTimezoneLabel by stringPref(default = "")
var altTimezoneId by stringPref(default = "") var altTimezoneId by stringPref(default = "")
// Global // Global
var textMainSize by floatPref(key = "PREF_TEXT_MAIN_SIZE", default = 26f) var textMainSize by floatPref(key = "PREF_TEXT_MAIN_SIZE", default = 24f)
var textSecondSize by floatPref(key = "PREF_TEXT_SECOND_SIZE", default = 18f) var textSecondSize by floatPref(key = "PREF_TEXT_SECOND_SIZE", default = 16f)
var clockTextSize by floatPref(key = "PREF_TEXT_CLOCK_SIZE", default = 90f) var clockTextSize by floatPref(key = "PREF_TEXT_CLOCK_SIZE", default = 72f)
var clockBottomMargin by intPref(default = Constants.ClockBottomMargin.MEDIUM.rawValue) 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.NONE.rawValue)
var showClock by booleanPref(key = "PREF_SHOW_CLOCK", default = false) var showClock by booleanPref(key = "PREF_SHOW_CLOCK", default = false)
@ -110,6 +114,7 @@ object Preferences : KotprefModel() {
var customFontName by stringPref(default = "") var customFontName by stringPref(default = "")
var customFontVariant by stringPref(default = "regular") var customFontVariant by stringPref(default = "regular")
var showNextEvent by booleanPref(key = "PREF_SHOW_NEXT_EVENT", default = true) var showNextEvent by booleanPref(key = "PREF_SHOW_NEXT_EVENT", default = true)
var showNextEventOnMultipleLines by booleanPref(default = false)
var showDividers by booleanPref(default = true) var showDividers by booleanPref(default = true)
@ -148,6 +153,7 @@ object Preferences : KotprefModel() {
var appNotificationsFilter by stringPref(default = "") var appNotificationsFilter by stringPref(default = "")
var showEventsAsGlanceProvider by booleanPref(default = false) var showEventsAsGlanceProvider by booleanPref(default = false)
var showWeatherAsGlanceProvider by booleanPref(default = false)
// Integrations // Integrations
var installedIntegrations by intPref(default = 0) var installedIntegrations by intPref(default = 0)

View File

@ -44,7 +44,6 @@ object AlarmHelper {
val intent = Intent(context, UpdatesReceiver::class.java).apply { val intent = Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_ALARM_UPDATE action = Actions.ACTION_ALARM_UPDATE
} }
cancel(PendingIntent.getBroadcast(context, ALARM_UPDATE_ID, intent, 0))
setExact( setExact(
AlarmManager.RTC, AlarmManager.RTC,
trigger, 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 private const val ALARM_UPDATE_ID = 24953
} }

View File

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

View File

@ -1,10 +1,20 @@
package com.tommasoberlose.anotherwidget.helpers package com.tommasoberlose.anotherwidget.helpers
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Context.CLIPBOARD_SERVICE
import android.graphics.Color import android.graphics.Color
import android.util.Log
import androidx.core.content.ContextCompat
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
import com.tommasoberlose.anotherwidget.utils.toast
import kotlin.math.roundToInt import kotlin.math.roundToInt
object ColorHelper { object ColorHelper {
fun getFontColor(isDark: Boolean): Int { fun getFontColor(isDark: Boolean): Int {
return try { return try {
@ -144,4 +154,40 @@ object ColorHelper {
false false
} }
} }
@SuppressLint("DefaultLocale")
fun Context.copyToClipboard(color: Int?, alpha: Int) {
if (color == null) return toast(getString(R.string.error_copy_color))
with(getSystemService(CLIPBOARD_SERVICE) as ClipboardManager) {
try {
val colorString = Integer.toHexString(color)
val clip = "#%s%s".format(
alpha.toHexValue(),
if (colorString.length > 6) colorString.substring(2) else colorString
).toUpperCase()
setPrimaryClip(ClipData.newPlainText(clip, clip))
toast(getString(R.string.color_copied))
} catch (ex: Exception) {
ex.printStackTrace()
toast(getString(R.string.error_copy_color))
}
}
}
fun Context.isClipboardColor(): Boolean {
with(getSystemService(CLIPBOARD_SERVICE) as ClipboardManager) {
return try { primaryClip?.getItemAt(0)?.text?.toString()?.isColor() ?: false } catch (ex: Exception) { false }
}
}
fun Context.pasteFromClipboard(pasteColor: (color: String, alpha: String) -> Unit) {
with(getSystemService(CLIPBOARD_SERVICE) as ClipboardManager) {
primaryClip?.let {
val item = it.getItemAt(0).text.toString().replace("#", "")
val color = if (item.length > 6) item.substring(2) else item
val alpha = if (item.length > 6) item.substring(0, 2) else "00"
pasteColor("#$color", alpha)
}
}
}
} }

View File

@ -90,6 +90,12 @@ object GlanceProviderHelper {
R.drawable.round_event_note_24 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)) || (MediaPlayerHelper.isSomeonePlaying(context)) ||
(Preferences.showBatteryCharging && Preferences.isCharging || Preferences.isBatteryLevelLow) || (Preferences.showBatteryCharging && Preferences.isCharging || Preferences.isBatteryLevelLow) ||
(Preferences.customNotes.isNotEmpty()) || (Preferences.customNotes.isNotEmpty()) ||
(Preferences.showWeatherAsGlanceProvider && Preferences.showWeather && Preferences.weatherIcon != "") ||
(Preferences.showDailySteps && Preferences.googleFitSteps > 0) || (Preferences.showDailySteps && Preferences.googleFitSteps > 0) ||
(Preferences.showGreetings && GreetingsHelper.showGreetings()) || (Preferences.showGreetings && GreetingsHelper.showGreetings()) ||
(Preferences.showEventsAsGlanceProvider && Preferences.showEvents && context.checkGrantedPermission( (Preferences.showEventsAsGlanceProvider && Preferences.showEvents && context.checkGrantedPermission(
Manifest.permission.READ_CALENDAR) && eventRepository.getNextEvent() != null) Manifest.permission.READ_CALENDAR) && eventRepository.getNextEvent() != null)
) )
eventRepository.close() eventRepository.close()
return showGlance return showGlance

View File

@ -73,6 +73,9 @@ object ImageHelper {
allocationIn.destroy() allocationIn.destroy()
allocationOut.destroy() allocationOut.destroy()
colorMatrixScript.destroy()
blurScript.destroy()
//rs.destroy()
return bitmap return bitmap
} }

View File

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

View File

@ -12,6 +12,7 @@ import com.chibatching.kotpref.bulk
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.receivers.NotificationListener import com.tommasoberlose.anotherwidget.receivers.NotificationListener
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.ignoreExceptions
import java.lang.Exception import java.lang.Exception
object MediaPlayerHelper { object MediaPlayerHelper {
@ -69,15 +70,24 @@ object MediaPlayerHelper {
isSomeonePlaying = true isSomeonePlaying = true
if (metadata != null) { if (metadata != null) {
Preferences.bulk { Preferences.bulk {
mediaPlayerTitle = ignoreExceptions {
metadata.getText(MediaMetadata.METADATA_KEY_TITLE)?.toString() mediaPlayerTitle =
?: "" metadata.getText(MediaMetadata.METADATA_KEY_TITLE)
mediaPlayerArtist = ?.toString()
metadata.getText(MediaMetadata.METADATA_KEY_ARTIST)?.toString() ?: ""
?: "" }
mediaPlayerAlbum = ignoreExceptions {
metadata.getText(MediaMetadata.METADATA_KEY_ALBUM)?.toString() mediaPlayerArtist =
?: "" metadata.getText(MediaMetadata.METADATA_KEY_ARTIST)
?.toString()
?: ""
}
ignoreExceptions {
mediaPlayerAlbum =
metadata.getText(MediaMetadata.METADATA_KEY_ALBUM)
?.toString()
?: ""
}
} }
} }

View File

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

View File

@ -21,11 +21,20 @@ object WeatherHelper {
suspend fun updateWeather(context: Context) { suspend fun updateWeather(context: Context) {
Kotpref.init(context) Kotpref.init(context)
val networkApi = WeatherNetworkApi(context)
if (Preferences.customLocationAdd != "") { if (Preferences.customLocationAdd != "") {
networkApi.updateWeather() WeatherNetworkApi(context).updateWeather()
} else if (context.checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION)) { } 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) 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,6 +322,23 @@ object WeatherHelper {
} }
} }
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 { fun getWeatherGovIcon(iconString: String, isDaytime: Boolean): String = when {
iconString.contains("skc") -> "01" iconString.contains("skc") -> "01"
iconString.contains("few") -> "02" iconString.contains("few") -> "02"

View File

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

View File

@ -42,6 +42,13 @@ class WeatherNetworkApi(val context: Context) {
Constants.WeatherProvider.YR -> useYrProvider(context) Constants.WeatherProvider.YR -> useYrProvider(context)
} }
} else { } 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( WeatherHelper.removeWeather(
context context
) )

View File

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

View File

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

View File

@ -22,21 +22,14 @@ class WeatherReceiver : BroadcastReceiver() {
Intent.ACTION_MY_PACKAGE_REPLACED, Intent.ACTION_MY_PACKAGE_REPLACED,
Intent.ACTION_TIMEZONE_CHANGED, Intent.ACTION_TIMEZONE_CHANGED,
Intent.ACTION_LOCALE_CHANGED, Intent.ACTION_LOCALE_CHANGED,
Intent.ACTION_TIME_CHANGED -> setUpdates(context) Intent.ACTION_TIME_CHANGED,
Actions.ACTION_WEATHER_UPDATE -> setUpdates(context)
Actions.ACTION_WEATHER_UPDATE -> {
GlobalScope.launch(Dispatchers.IO) {
WeatherHelper.updateWeather(context)
}
}
} }
} }
companion object { companion object {
private const val MINUTE = 60 * 1000L private const val MINUTE = 60 * 1000L
fun setUpdates(context: Context) { fun setUpdates(context: Context) {
removeUpdates(context)
if (Preferences.showWeather) { if (Preferences.showWeather) {
val interval = MINUTE * when (Preferences.weatherRefreshPeriod) { val interval = MINUTE * when (Preferences.weatherRefreshPeriod) {
0 -> 30 0 -> 30
@ -48,13 +41,15 @@ class WeatherReceiver : BroadcastReceiver() {
else -> 60 else -> 60
} }
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) { with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
setRepeating( setExact(
AlarmManager.RTC, AlarmManager.RTC,
Calendar.getInstance().timeInMillis, System.currentTimeMillis() + interval,
interval,
PendingIntent.getBroadcast(context, 0, Intent(context, WeatherReceiver::class.java).apply { action = Actions.ACTION_WEATHER_UPDATE }, 0) 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) { override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Actions.ACTION_OPEN_WEATHER_INTENT) { if (intent.action == Actions.ACTION_OPEN_WEATHER_INTENT) {
try { try {
if (Preferences.weatherAppPackage == IntentHelper.REFRESH_WIDGET_OPTION) { IntentHelper.getWeatherIntent(context).run {
context.sendBroadcast(IntentHelper.getWeatherIntent(context)) if (flags and Intent.FLAG_ACTIVITY_NEW_TASK == Intent.FLAG_ACTIVITY_NEW_TASK)
} else { context.startActivity(this)
context.startActivity(IntentHelper.getWeatherIntent(context)) else
context.sendBroadcast(this)
} }
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() 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) val i = Intent(Intent.ACTION_VIEW, uri)
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
try { try {

View File

@ -18,6 +18,7 @@ import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import java.lang.Exception import java.lang.Exception
@ -37,12 +38,31 @@ class LocationService : Service() {
startForeground(LOCATION_ACCESS_NOTIFICATION_ID, getLocationAccessNotification()) startForeground(LOCATION_ACCESS_NOTIFICATION_ID, getLocationAccessNotification())
job?.cancel() job?.cancel()
job = GlobalScope.launch(Dispatchers.IO) { job = GlobalScope.launch(Dispatchers.IO) {
if (ActivityCompat.checkSelfPermission( if (checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION) &&
this@LocationService, (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R ||
Manifest.permission.ACCESS_FINE_LOCATION checkGrantedPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION))
) == PackageManager.PERMISSION_GRANTED
) { ) {
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) val networkApi = WeatherNetworkApi(this@LocationService)
if (task.isSuccessful) { if (task.isSuccessful) {
val location = task.result val location = task.result

View File

@ -55,18 +55,20 @@ class UpdateCalendarService : Service() {
job?.cancel() job?.cancel()
job = GlobalScope.launch(Dispatchers.IO) { job = GlobalScope.launch(Dispatchers.IO) {
UpdatesReceiver.removeUpdates(this@UpdateCalendarService)
val eventRepository = EventRepository(this@UpdateCalendarService) val eventRepository = EventRepository(this@UpdateCalendarService)
if (Preferences.showEvents) { if (Preferences.showEvents) {
val eventList = ArrayList<Event>() val eventList = ArrayList<Event>()
// fetch all events from now to next ACTION_CALENDAR_UPDATE + limit
val now = Calendar.getInstance() val now = Calendar.getInstance()
val begin = Calendar.getInstance().apply { val limit = Calendar.getInstance().apply {
set(Calendar.MILLISECOND, 0) set(Calendar.MILLISECOND, 0)
set(Calendar.SECOND, 0) set(Calendar.SECOND, 0)
set(Calendar.MINUTE, 0) set(Calendar.MINUTE, 0)
set(Calendar.HOUR_OF_DAY, 0) set(Calendar.HOUR_OF_DAY, 0)
} add(Calendar.DATE, 1)
val limit = Calendar.getInstance().apply {
when (Preferences.showUntil) { when (Preferences.showUntil) {
0 -> add(Calendar.HOUR, 3) 0 -> add(Calendar.HOUR, 3)
1 -> add(Calendar.HOUR, 6) 1 -> add(Calendar.HOUR, 6)
@ -85,29 +87,37 @@ class UpdateCalendarService : Service() {
) )
) { ) {
eventRepository.resetNextEventData() eventRepository.resetNextEventData()
eventRepository.clearEvents()
Preferences.showEvents = false
} else { } else {
try { try {
val provider = CalendarProvider(this@UpdateCalendarService) 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) { if (data != null) {
val instances = data.list val instances = data.list
for (instance in instances) { for (instance in instances) {
try { try {
val e = provider.getEvent(instance.eventId) val e = provider.getEvent(instance.eventId)
if (e != null && !e.deleted && instance.begin <= limit.timeInMillis && now.timeInMillis < instance.end && !CalendarHelper.getFilteredCalendarIdList() if (e == null || e.deleted || CalendarHelper.getFilteredCalendarIdList().contains(e.calendarId))
.contains(e.calendarId) continue
) { if (e.allDay) {
if (e.allDay) { val start = Calendar.getInstance()
val start = Calendar.getInstance() start.timeInMillis = instance.begin
start.timeInMillis = instance.begin val end = Calendar.getInstance()
val end = Calendar.getInstance() end.timeInMillis = instance.end
end.timeInMillis = instance.end instance.begin =
instance.begin = start.timeInMillis - start.timeZone.getOffset(start.timeInMillis)
start.timeInMillis - start.timeZone.getOffset(start.timeInMillis) instance.end =
instance.end = end.timeInMillis - end.timeZone.getOffset(end.timeInMillis)
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 // Check all day events
val startDate = Calendar.getInstance() val startDate = Calendar.getInstance()
startDate.timeInMillis = instance.begin startDate.timeInMillis = instance.begin
@ -124,6 +134,7 @@ class UpdateCalendarService : Service() {
&& endDate.get(Calendar.MINUTE) == 0 && endDate.get(Calendar.MINUTE) == 0
&& endDate.get(Calendar.HOUR_OF_DAY) == 0 && endDate.get(Calendar.HOUR_OF_DAY) == 0
) )
*/
eventList.add( eventList.add(
Event( Event(
@ -133,7 +144,7 @@ class UpdateCalendarService : Service() {
startDate = instance.begin, startDate = instance.begin,
endDate = instance.end, endDate = instance.end,
calendarID = e.calendarId.toInt(), calendarID = e.calendarId.toInt(),
allDay = isAllDay, allDay = e.allDay,
address = e.eventLocation ?: "", address = e.eventLocation ?: "",
selfAttendeeStatus = e.selfAttendeeStatus.toInt(), selfAttendeeStatus = e.selfAttendeeStatus.toInt(),
availability = e.availability availability = e.availability
@ -164,13 +175,14 @@ class UpdateCalendarService : Service() {
} }
} else { } else {
eventRepository.resetNextEventData() eventRepository.resetNextEventData()
eventRepository.clearEvents()
} }
eventRepository.close()
UpdatesReceiver.setUpdates(this@UpdateCalendarService) UpdatesReceiver.setUpdates(this@UpdateCalendarService)
MainWidget.updateWidget(this@UpdateCalendarService) MainWidget.updateWidget(this@UpdateCalendarService)
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent()) EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
eventRepository.close()
stopSelf() stopSelf()
} }

View File

@ -140,6 +140,7 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh
if (Preferences.showEvents && !checkGrantedPermission(Manifest.permission.READ_CALENDAR)) { if (Preferences.showEvents && !checkGrantedPermission(Manifest.permission.READ_CALENDAR)) {
Preferences.showEvents = false 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 adapter: SlimAdapter
private lateinit var viewModel: CustomFontViewModel private lateinit var viewModel: CustomFontViewModel
private lateinit var binding: ActivityCustomFontBinding private lateinit var binding: ActivityCustomFontBinding
private lateinit var handlerThread: HandlerThread
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(CustomFontViewModel::class.java) viewModel = ViewModelProvider(this).get(CustomFontViewModel::class.java)
binding = ActivityCustomFontBinding.inflate(layoutInflater) binding = ActivityCustomFontBinding.inflate(layoutInflater)
handlerThread = HandlerThread("listCustomFonts")
handlerThread.start()
binding.listView.setHasFixedSize(true) binding.listView.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this) val mLayoutManager = LinearLayoutManager(this)
@ -64,14 +67,17 @@ class CustomFontActivity : AppCompatActivity() {
injector injector
.text(R.id.text, item) .text(R.id.text, item)
.with<TextView>(R.id.text) { .with<TextView>(R.id.text) {
val googleSans: Typeface = when (Preferences.customFontVariant) { val googleSans: Typeface? = androidx.core.content.res.ResourcesCompat.getFont(
"100" -> Typeface.createFromAsset(this.assets, "fonts/google_sans_thin.ttf") this,
"200" -> Typeface.createFromAsset(this.assets, "fonts/google_sans_light.ttf") when (Preferences.customFontVariant) {
"500" -> Typeface.createFromAsset(this.assets, "fonts/google_sans_medium.ttf") "100" -> R.font.google_sans_thin
"700" -> Typeface.createFromAsset(this.assets, "fonts/google_sans_bold.ttf") "200" -> R.font.google_sans_light
"800" -> Typeface.createFromAsset(this.assets, "fonts/google_sans_black.ttf") "500" -> R.font.google_sans_medium
else -> Typeface.createFromAsset(this.assets, "fonts/google_sans_regular.ttf") "700" -> R.font.google_sans_bold
} "800" -> R.font.google_sans_black
else -> R.font.google_sans_regular
}
)
it.typeface = googleSans it.typeface = googleSans
} }
@ -97,32 +103,49 @@ class CustomFontActivity : AppCompatActivity() {
) )
val callback = object : FontsContractCompat.FontRequestCallback() { class Callback : FontsContractCompat.FontRequestCallback() {
override fun onTypefaceRetrieved(typeface: Typeface) { var handler: Handler? = Handler(handlerThread.looper)
it.typeface = typeface
it.isVisible = true
it.measure( fun cancel() {
ViewGroup.LayoutParams.MATCH_PARENT, if (handler != null) {
ViewGroup.LayoutParams.WRAP_CONTENT 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) { override fun onTypefaceRequestFailed(reason: Int) {
it.isVisible = false if (it.tag == this) {
it.layoutParams = it.layoutParams.apply { it.tag = null
height = 0 //it.text = item.fontFamily + " ($reason)"
it.setTextColor(getColor(R.color.errorColorText))
} }
} }
} }
val handlerThread = HandlerThread(item.fontFamily) (it.tag as Callback?)?.cancel()
handlerThread.start() val callback = Callback()
val mHandler = Handler(handlerThread.looper) it.tag = callback
it.typeface = null
it.setTextColor(getColor(R.color.colorSecondaryText))
val mHandler = callback.handler!!
FontsContractCompat.requestFont(this, request, callback, mHandler) FontsContractCompat.requestFont(this, request, callback, mHandler)
} }
injector.clicked(R.id.text) { injector.clicked(R.id.text) {
if ((it as TextView).typeface == null) return@clicked
val dialog = BottomSheetMenu<Int>(this, header = item.fontFamily) val dialog = BottomSheetMenu<Int>(this, header = item.fontFamily)
if (item.fontVariants.isEmpty()) { if (item.fontVariants.isEmpty()) {
dialog.addItem(SettingsStringHelper.getVariantLabel(this, "regular"), -1) dialog.addItem(SettingsStringHelper.getVariantLabel(this, "regular"), -1)
@ -147,6 +170,12 @@ class CustomFontActivity : AppCompatActivity() {
setContentView(binding.root) setContentView(binding.root)
} }
override fun onDestroy() {
handlerThread.quit()
filterJob?.cancel()
super.onDestroy()
}
private var filterJob: Job? = null private var filterJob: Job? = null
private fun subscribeUi(binding: ActivityCustomFontBinding, viewModel: CustomFontViewModel) { private fun subscribeUi(binding: ActivityCustomFontBinding, viewModel: CustomFontViewModel) {
@ -204,6 +233,13 @@ class CustomFontActivity : AppCompatActivity() {
adapter.updateData(filteredList) adapter.updateData(filteredList)
binding.loader.visibility = View.INVISIBLE 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,22 +46,27 @@ class CustomLocationActivity : AppCompatActivity() {
adapter = SlimAdapter.create() adapter = SlimAdapter.create()
adapter adapter
.register<String>(R.layout.custom_location_item) { _, injector -> .register<String>(R.layout.custom_location_item) { _, injector ->
injector injector.text(R.id.text, getString(R.string.custom_location_gps))
.text(R.id.text, getString(R.string.custom_location_gps)) injector.clicked(R.id.text) {
.clicked(R.id.text) { Preferences.bulk {
requirePermission() remove(Preferences::customLocationLat)
remove(Preferences::customLocationLon)
remove(Preferences::customLocationAdd)
} }
setResult(Activity.RESULT_OK)
finish()
}
} }
.register<Address>(R.layout.custom_location_item) { item, injector -> .register<Address>(R.layout.custom_location_item) { item, injector ->
injector.text(R.id.text, item.getAddressLine(0)) injector.text(R.id.text, item.getAddressLine(0) ?: "")
injector.clicked(R.id.item) { injector.clicked(R.id.item) {
Preferences.bulk { Preferences.bulk {
customLocationLat = item.latitude.toString() customLocationLat = item.latitude.toString()
customLocationLon = item.longitude.toString() customLocationLon = item.longitude.toString()
customLocationAdd = item.getAddressLine(0) customLocationAdd = item.getAddressLine(0) ?: ""
setResult(Activity.RESULT_OK)
finish()
} }
setResult(Activity.RESULT_OK)
finish()
} }
} }
.attachTo(binding.listView) .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() { private fun setupListener() {
binding.actionBack.setOnClickListener { binding.actionBack.setOnClickListener {
onBackPressed() onBackPressed()

View File

@ -74,7 +74,11 @@ class TimeZoneSelectorActivity : AppCompatActivity() {
if (id != null) { if (id != null) {
Preferences.bulk { Preferences.bulk {
altTimezoneId = id altTimezoneId = id
altTimezoneLabel = item.locality altTimezoneLabel = try {
item.locality
} catch (ex: Exception) {
item.getAddressLine(0)
}
} }
MainWidget.updateWidget(this@TimeZoneSelectorActivity) MainWidget.updateWidget(this@TimeZoneSelectorActivity)
setResult(Activity.RESULT_OK) setResult(Activity.RESULT_OK)

View File

@ -127,11 +127,11 @@ class WeatherProviderActivity : AppCompatActivity() {
private fun subscribeUi(viewModel: WeatherProviderViewModel) { private fun subscribeUi(viewModel: WeatherProviderViewModel) {
viewModel.weatherProviderError.observe(this) { viewModel.weatherProviderError.observe(this) {
updateListItem() binding.listView.postDelayed({ updateListItem() }, 300)
} }
viewModel.weatherProviderLocationError.observe(this) { viewModel.weatherProviderLocationError.observe(this) {
updateListItem() binding.listView.postDelayed({ updateListItem() }, 300)
} }
} }

View File

@ -8,15 +8,10 @@ import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.util.Log import android.util.Log
import android.util.TypedValue
import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.RemoteViews
import android.widget.RelativeLayout
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -29,23 +24,20 @@ import com.tommasoberlose.anotherwidget.databinding.FragmentAppMainBinding
import com.tommasoberlose.anotherwidget.global.Constants import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.* import com.tommasoberlose.anotherwidget.helpers.*
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.ui.widgets.StandardWidget
import com.tommasoberlose.anotherwidget.utils.* import com.tommasoberlose.anotherwidget.utils.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
class MainFragment : Fragment() { class MainFragment : Fragment() {
companion object { companion object {
fun newInstance() = MainFragment() 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 private lateinit var viewModel: MainViewModel
@ -97,13 +89,7 @@ class MainFragment : Fragment() {
} }
binding.actionSettings.setOnSingleClickListener { binding.actionSettings.setOnSingleClickListener {
Navigation.findNavController(it).navigate(R.id.action_appMainFragment_to_appSettingsFragment,) 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
} }
subscribeUi(viewModel) subscribeUi(viewModel)
@ -155,186 +141,56 @@ class MainFragment : Fragment() {
binding.toolbar.cardElevation = if (it > 0) 24f else 0f 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()
}
viewModel.widgetPreferencesUpdate.observe(viewLifecycleOwner) { viewModel.widgetPreferencesUpdate.observe(viewLifecycleOwner) {
onUpdateUiEvent(null) onUpdateUiEvent(null)
} }
viewModel.showClock.observe(viewLifecycleOwner) {
updateClockVisibility(it)
}
} }
private var uiJob: Job? = null private var uiJob: Job? = null
private fun updateUI() { private fun updateUI() {
uiJob?.cancel()
if (Preferences.showPreview) { 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 -> WidgetHelper.runWithCustomTypeface(requireContext()) { typeface ->
uiJob?.cancel()
uiJob = lifecycleScope.launch(Dispatchers.IO) { uiJob = lifecycleScope.launch(Dispatchers.IO) {
val generatedView = MainWidget.getWidgetView(requireContext(), typeface).root val generatedView = MainWidget.getWidgetView(
requireContext(),
withContext(Dispatchers.Main) { binding.widget.width - binding.widget.paddingStart - binding.widget.paddingEnd,
generatedView.measure(0, 0) typeface
binding.preview.measure(0, 0)
}
val bitmap = BitmapHelper.getBitmapFromView(
generatedView,
if (binding.preview.width > 0) binding.preview.width else generatedView.measuredWidth,
generatedView.measuredHeight
) )
withContext(Dispatchers.Main) { if (generatedView != null) {
binding.widgetDetail.bitmapContainer.apply { withContext(Dispatchers.Main) {
setImageBitmap(bitmap) val view: View = generatedView.apply(requireActivity().applicationContext, binding.widget)
} view.measure(0, 0)
binding.widgetLoader.animate().scaleX(0f).scaleY(0f).alpha(0f) binding.widgetLoader.animate().scaleX(1f).scaleY(1f).alpha(1f)
.setDuration(200L).start() .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()
}
} }
} }
} }
} }
} }
private fun updateClock() { private fun updatePreviewVisibility(widgetHeight: Int) {
// Clock val newHeight = widgetHeight + 32f.convertDpToPixel(requireContext()).toInt()
binding.widgetDetail.time.setTextColor(ColorHelper.getClockFontColor(requireActivity().isDarkTheme())) if (binding.preview.layoutParams.height != newHeight) {
binding.widgetDetail.timeAmPm.setTextColor(ColorHelper.getClockFontColor(requireActivity().isDarkTheme())) binding.preview.clearAnimation()
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 = if (Preferences.widgetAlign == Constants.WidgetAlign.CENTER.rawValue) Gravity.CENTER_HORIZONTAL else Gravity.NO_GRAVITY
}
}
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 = 300L
addUpdateListener {
val animatedValue = animatedValue as Float
binding.widgetDetail.timeContainer.layoutParams =
binding.widgetDetail.timeContainer.layoutParams.apply {
height = (initialHeight * animatedValue).toInt()
}
binding.widgetDetail.time.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)) {
ValueAnimator.ofInt( ValueAnimator.ofInt(
binding.preview.height, binding.preview.height,
(if (Preferences.showPreview) PREVIEW_BASE_HEIGHT.toPixel(requireContext()) else 0) + (if (Preferences.showClock) 100.toPixel( newHeight
requireContext()
) else 0)
).apply { ).apply {
duration = 300L duration = 500L
addUpdateListener { addUpdateListener {
val animatedValue = animatedValue as Int val animatedValue = animatedValue as Int
val layoutParams = binding.preview.layoutParams val layoutParams = binding.preview.layoutParams

View File

@ -35,6 +35,7 @@ import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.activities.settings.SupportDevActivity import com.tommasoberlose.anotherwidget.ui.activities.settings.SupportDevActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.ignoreExceptions
import com.tommasoberlose.anotherwidget.utils.openURI import com.tommasoberlose.anotherwidget.utils.openURI
import com.tommasoberlose.anotherwidget.utils.setOnSingleClickListener import com.tommasoberlose.anotherwidget.utils.setOnSingleClickListener
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -206,10 +207,14 @@ class SettingsFragment : Fragment() {
.rotation((binding.actionRefreshIcon.rotation - binding.actionRefreshIcon.rotation % 360f) + 360f) .rotation((binding.actionRefreshIcon.rotation - binding.actionRefreshIcon.rotation % 360f) + 360f)
.withEndAction { .withEndAction {
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
WeatherHelper.updateWeather(requireContext()) try {
CalendarHelper.updateEventList(requireContext()) WeatherHelper.updateWeather(requireContext())
MediaPlayerHelper.updatePlayingMediaInfo(requireContext()) CalendarHelper.updateEventList(requireContext())
ActiveNotificationsHelper.clearLastNotification(requireContext()) MediaPlayerHelper.updatePlayingMediaInfo(requireContext())
ActiveNotificationsHelper.clearLastNotification(requireContext())
} catch (ex: Exception) {
ex.printStackTrace()
}
} }
} }
.start() .start()

View File

@ -63,6 +63,7 @@ class CalendarFragment : Fragment() {
binding.showAllDayToggle.setCheckedImmediatelyNoEvent(Preferences.calendarAllDay) binding.showAllDayToggle.setCheckedImmediatelyNoEvent(Preferences.calendarAllDay)
binding.showOnlyBusyEventsToggle.setCheckedImmediatelyNoEvent(Preferences.showOnlyBusyEvents) binding.showOnlyBusyEventsToggle.setCheckedImmediatelyNoEvent(Preferences.showOnlyBusyEvents)
binding.showDiffTimeToggle.setCheckedImmediatelyNoEvent(Preferences.showDiffTime) binding.showDiffTimeToggle.setCheckedImmediatelyNoEvent(Preferences.showDiffTime)
binding.showNextEventOnMultipleLinesToggle.setCheckedImmediatelyNoEvent(Preferences.showNextEventOnMultipleLines)
setupListener() setupListener()
@ -90,6 +91,12 @@ class CalendarFragment : Fragment() {
} }
} }
viewModel.showNextEventOnMultipleLines.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.showNextEventOnMultipleLinesLabel.text = if (it) getString(R.string.settings_enabled) else getString(R.string.settings_disabled)
}
}
viewModel.showDiffTime.observe(viewLifecycleOwner) { viewModel.showDiffTime.observe(viewLifecycleOwner) {
maintainScrollPosition { maintainScrollPosition {
binding.showDiffTimeLabel.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible) binding.showDiffTimeLabel.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
@ -175,7 +182,7 @@ class CalendarFragment : Fragment() {
binding.showAllDayToggle.setOnCheckedChangeListener { _, isChecked -> binding.showAllDayToggle.setOnCheckedChangeListener { _, isChecked ->
Preferences.calendarAllDay = isChecked Preferences.calendarAllDay = isChecked
MainWidget.updateWidget(requireContext()) updateCalendar()
} }
binding.actionChangeAttendeeFilter.setOnClickListener { binding.actionChangeAttendeeFilter.setOnClickListener {
@ -220,7 +227,7 @@ class CalendarFragment : Fragment() {
binding.showOnlyBusyEventsToggle.setOnCheckedChangeListener { _, isChecked -> binding.showOnlyBusyEventsToggle.setOnCheckedChangeListener { _, isChecked ->
Preferences.showOnlyBusyEvents = isChecked Preferences.showOnlyBusyEvents = isChecked
MainWidget.updateWidget(requireContext()) updateCalendar()
} }
binding.actionShowDiffTime.setOnClickListener { binding.actionShowDiffTime.setOnClickListener {
@ -229,6 +236,15 @@ class CalendarFragment : Fragment() {
binding.showDiffTimeToggle.setOnCheckedChangeListener { _, isChecked -> binding.showDiffTimeToggle.setOnCheckedChangeListener { _, isChecked ->
Preferences.showDiffTime = isChecked Preferences.showDiffTime = isChecked
updateCalendar()
}
binding.actionShowNextEventOnMultipleLines.setOnClickListener {
binding.showNextEventOnMultipleLinesToggle.isChecked = !binding.showNextEventOnMultipleLinesToggle.isChecked
}
binding.showNextEventOnMultipleLinesToggle.setOnCheckedChangeListener { _, isChecked ->
Preferences.showNextEventOnMultipleLines = isChecked
} }
binding.actionWidgetUpdateFrequency.setOnClickListener { binding.actionWidgetUpdateFrequency.setOnClickListener {
@ -239,6 +255,7 @@ class CalendarFragment : Fragment() {
.addItem(getString(R.string.settings_widget_update_frequency_low), Constants.WidgetUpdateFrequency.LOW.rawValue) .addItem(getString(R.string.settings_widget_update_frequency_low), Constants.WidgetUpdateFrequency.LOW.rawValue)
.addOnSelectItemListener { value -> .addOnSelectItemListener { value ->
Preferences.widgetUpdateFrequency = value Preferences.widgetUpdateFrequency = value
updateCalendar()
}.show() }.show()
} }
} }

View File

@ -17,7 +17,7 @@ import com.chibatching.kotpref.bulk
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.BottomSheetColorPicker 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.databinding.FragmentTabClockBinding
import com.tommasoberlose.anotherwidget.global.Constants import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
@ -144,16 +144,15 @@ class ClockFragment : Fragment() {
private fun setupListener() { private fun setupListener() {
binding.actionClockTextSize.setOnClickListener { binding.actionClockTextSize.setOnClickListener {
val dialog = BottomSheetMenu<Float>( BottomSheetPicker(
requireContext(), requireContext(),
header = getString(R.string.settings_clock_text_size_title) items = (120 downTo 30).filter { it % 2 == 0 }.map { BottomSheetPicker.MenuItem("${it}sp", it.toFloat()) },
).setSelectedValue(Preferences.clockTextSize) getSelected = { Preferences.clockTextSize },
(46 downTo 12).filter { it % 2 == 0 }.forEach { header = getString(R.string.settings_clock_text_size_title),
dialog.addItem("${it}sp", it.toFloat()) onItemSelected = {value ->
} if (value != null) Preferences.clockTextSize = value
dialog.addOnSelectItemListener { value -> }
Preferences.clockTextSize = value ).show()
}.show()
} }
binding.actionAltTimezoneClock.setOnClickListener { 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.DO_NOTHING_OPTION -> getString(R.string.gestures_do_nothing)
it == IntentHelper.REFRESH_WIDGET_OPTION -> getString(R.string.gestures_refresh_widget) it == IntentHelper.REFRESH_WIDGET_OPTION -> getString(R.string.gestures_refresh_widget)
it != IntentHelper.DEFAULT_OPTION -> it it != IntentHelper.DEFAULT_OPTION -> it
else -> { else -> getString(R.string.default_calendar_app)
if (IntentHelper.getCalendarIntent(requireContext()).isDefaultSet(requireContext())) {
getString(
R.string.default_calendar_app
)
} else {
getString(R.string.gestures_do_nothing)
}
}
} }
} }
} }
@ -127,15 +119,7 @@ class GesturesFragment : Fragment() {
it == IntentHelper.DO_NOTHING_OPTION -> getString(R.string.gestures_do_nothing) it == IntentHelper.DO_NOTHING_OPTION -> getString(R.string.gestures_do_nothing)
it == IntentHelper.REFRESH_WIDGET_OPTION -> getString(R.string.gestures_refresh_widget) it == IntentHelper.REFRESH_WIDGET_OPTION -> getString(R.string.gestures_refresh_widget)
it != IntentHelper.DEFAULT_OPTION -> it it != IntentHelper.DEFAULT_OPTION -> it
else -> { else -> getString(R.string.default_clock_app)
if (IntentHelper.getClockIntent(requireContext()).isDefaultSet(requireContext())) {
getString(
R.string.default_clock_app
)
} else {
getString(R.string.gestures_do_nothing)
}
}
} }
} }
} }
@ -173,9 +157,12 @@ class GesturesFragment : Fragment() {
} }
binding.actionCalendarApp.setOnClickListener { binding.actionCalendarApp.setOnClickListener {
startActivityForResult(Intent(requireContext(), ChooseApplicationActivity::class.java).apply { startActivityForResult(
putExtra(Constants.RESULT_APP_PACKAGE, Preferences.calendarAppPackage) Intent(requireContext(), ChooseApplicationActivity::class.java).apply {
}, RequestCode.CALENDAR_APP_REQUEST_CODE.code) putExtra(Constants.RESULT_APP_PACKAGE, Preferences.calendarAppPackage)
},
RequestCode.CALENDAR_APP_REQUEST_CODE.code
)
} }
binding.actionClockApp.setOnClickListener { binding.actionClockApp.setOnClickListener {

View File

@ -293,6 +293,25 @@ class GlanceTabFragment : Fragment() {
if (!(isVisible && hasError)) View.VISIBLE else View.GONE 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) injector.alpha(R.id.title, if (isVisible) 1f else .25f)

View File

@ -86,6 +86,28 @@ class LayoutFragment : Fragment() {
viewModel: MainViewModel 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) { viewModel.secondRowTopMargin.observe(viewLifecycleOwner) {
maintainScrollPosition { maintainScrollPosition {
binding.secondRowTopMarginLabel.text = when (it) { binding.secondRowTopMarginLabel.text = when (it) {
@ -101,12 +123,14 @@ class LayoutFragment : Fragment() {
maintainScrollPosition { maintainScrollPosition {
binding.widgetAlignIcon.setImageDrawable(when (it) { binding.widgetAlignIcon.setImageDrawable(when (it) {
Constants.WidgetAlign.LEFT.rawValue -> ContextCompat.getDrawable(requireContext(), R.drawable.round_align_horizontal_left_24) Constants.WidgetAlign.LEFT.rawValue -> ContextCompat.getDrawable(requireContext(), R.drawable.round_align_horizontal_left_24)
Constants.WidgetAlign.RIGHT.rawValue -> ContextCompat.getDrawable(requireContext(), R.drawable.round_align_horizontal_right_24)
Constants.WidgetAlign.CENTER.rawValue -> ContextCompat.getDrawable(requireContext(), R.drawable.round_align_horizontal_center_24) Constants.WidgetAlign.CENTER.rawValue -> ContextCompat.getDrawable(requireContext(), R.drawable.round_align_horizontal_center_24)
else -> ContextCompat.getDrawable(requireContext(), R.drawable.round_align_horizontal_center_24) else -> ContextCompat.getDrawable(requireContext(), R.drawable.round_align_horizontal_center_24)
}) })
binding.widgetAlignLabel.text = when (it) { binding.widgetAlignLabel.text = when (it) {
Constants.WidgetAlign.LEFT.rawValue -> getString(R.string.settings_widget_align_left_subtitle) Constants.WidgetAlign.LEFT.rawValue -> getString(R.string.settings_widget_align_left_subtitle)
Constants.WidgetAlign.RIGHT.rawValue -> getString(R.string.settings_widget_align_right_subtitle)
Constants.WidgetAlign.CENTER.rawValue -> getString(R.string.settings_widget_align_center_subtitle) Constants.WidgetAlign.CENTER.rawValue -> getString(R.string.settings_widget_align_center_subtitle)
else -> getString(R.string.settings_widget_align_center_subtitle) else -> getString(R.string.settings_widget_align_center_subtitle)
} }
@ -145,6 +169,58 @@ class LayoutFragment : Fragment() {
private fun setupListener() { 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 { binding.actionSecondRowTopMarginSize.setOnClickListener {
BottomSheetMenu<Int>( BottomSheetMenu<Int>(
requireContext(), requireContext(),
@ -210,6 +286,10 @@ class LayoutFragment : Fragment() {
getString(R.string.settings_widget_align_left_subtitle), getString(R.string.settings_widget_align_left_subtitle),
Constants.WidgetAlign.LEFT.rawValue Constants.WidgetAlign.LEFT.rawValue
) )
.addItem(
getString(R.string.settings_widget_align_right_subtitle),
Constants.WidgetAlign.RIGHT.rawValue
)
.addOnSelectItemListener { value -> .addOnSelectItemListener { value ->
Preferences.widgetAlign = value Preferences.widgetAlign = value
}.show() }.show()

View File

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

View File

@ -15,6 +15,7 @@ import com.google.android.material.transition.MaterialSharedAxis
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.BottomSheetColorPicker import com.tommasoberlose.anotherwidget.components.BottomSheetColorPicker
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
import com.tommasoberlose.anotherwidget.components.BottomSheetPicker
import com.tommasoberlose.anotherwidget.databinding.FragmentTabTypographyBinding import com.tommasoberlose.anotherwidget.databinding.FragmentTabTypographyBinding
import com.tommasoberlose.anotherwidget.global.Constants import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
@ -167,25 +168,27 @@ class TypographyFragment : Fragment() {
private fun setupListener() { private fun setupListener() {
binding.actionMainTextSize.setOnClickListener { binding.actionMainTextSize.setOnClickListener {
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.title_main_text_size)).setSelectedValue( BottomSheetPicker(
Preferences.textMainSize) requireContext(),
(40 downTo 10).filter { it % 2 == 0 }.forEach { items = (40 downTo 10).map { BottomSheetPicker.MenuItem("${it}sp", it.toFloat()) },
dialog.addItem("${it}sp", it.toFloat()) getSelected = { Preferences.textMainSize },
} header = getString(R.string.title_main_text_size),
dialog.addOnSelectItemListener { value -> onItemSelected = {value ->
Preferences.textMainSize = value if (value != null) Preferences.textMainSize = value
}.show() }
).show()
} }
binding.actionSecondTextSize.setOnClickListener { binding.actionSecondTextSize.setOnClickListener {
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.title_second_text_size)).setSelectedValue( BottomSheetPicker(
Preferences.textSecondSize) requireContext(),
(40 downTo 10).filter { it % 2 == 0 }.forEach { items = (40 downTo 10).map { BottomSheetPicker.MenuItem("${it}sp", it.toFloat()) },
dialog.addItem("${it}sp", it.toFloat()) getSelected = { Preferences.textSecondSize },
} header = getString(R.string.title_second_text_size),
dialog.addOnSelectItemListener { value -> onItemSelected = {value ->
Preferences.textSecondSize = value if (value != null) Preferences.textSecondSize = value
}.show() }
).show()
} }
binding.actionFontColor.setOnClickListener { binding.actionFontColor.setOnClickListener {
@ -209,7 +212,7 @@ class TypographyFragment : Fragment() {
} else { } else {
Preferences.textGlobalAlpha = alpha.toHexValue() Preferences.textGlobalAlpha = alpha.toHexValue()
} }
} },
).show() ).show()
} }
@ -236,7 +239,7 @@ class TypographyFragment : Fragment() {
} else { } else {
Preferences.textSecondaryAlpha = alpha.toHexValue() Preferences.textSecondaryAlpha = alpha.toHexValue()
} }
} },
).show() ).show()
} }
@ -273,7 +276,7 @@ class TypographyFragment : Fragment() {
Intent(requireContext(), CustomFontActivity::class.java), Intent(requireContext(), CustomFontActivity::class.java),
RequestCode.CUSTOM_FONT_CHOOSER_REQUEST_CODE.code RequestCode.CUSTOM_FONT_CHOOSER_REQUEST_CODE.code
) )
} else if (value != Constants.CUSTOM_FONT_DOWNLOADED) { } else if (value != Preferences.customFont) {
Preferences.bulk { Preferences.bulk {
customFont = value customFont = value
customFontFile = "" customFontFile = ""
@ -314,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) { private fun maintainScrollPosition(callback: () -> Unit) {
binding.scrollView.isScrollable = false binding.scrollView.isScrollable = false
callback.invoke() callback.invoke()

View File

@ -91,16 +91,17 @@ class WeatherFragment : Fragment() {
viewModel.weatherProvider.observe(viewLifecycleOwner) { viewModel.weatherProvider.observe(viewLifecycleOwner) {
maintainScrollPosition { maintainScrollPosition {
binding.labelWeatherProvider.text = WeatherHelper.getProviderName(requireContext(), Constants.WeatherProvider.fromInt(it)!!) binding.labelWeatherProvider.text = WeatherHelper.getProviderName(requireContext(), Constants.WeatherProvider.fromInt(it)!!)
checkWeatherProviderConfig()
} }
} }
viewModel.weatherProviderError.observe(viewLifecycleOwner) { viewModel.weatherProviderError.observe(viewLifecycleOwner) {
checkWeatherProviderConfig() checkWeatherProviderConfig()
checkLocationPermission()
} }
viewModel.weatherProviderLocationError.observe(viewLifecycleOwner) { viewModel.weatherProviderLocationError.observe(viewLifecycleOwner) {
checkWeatherProviderConfig() checkWeatherProviderConfig()
checkLocationPermission()
} }
viewModel.customLocationAdd.observe(viewLifecycleOwner) { viewModel.customLocationAdd.observe(viewLifecycleOwner) {
@ -108,6 +109,7 @@ class WeatherFragment : Fragment() {
binding.labelCustomLocation.text = binding.labelCustomLocation.text =
if (it == "") getString(R.string.custom_location_gps) else it if (it == "") getString(R.string.custom_location_gps) else it
} }
checkWeatherProviderConfig()
checkLocationPermission() checkLocationPermission()
} }
@ -116,43 +118,50 @@ class WeatherFragment : Fragment() {
binding.tempUnit.text = binding.tempUnit.text =
if (it == "F") getString(R.string.fahrenheit) else getString(R.string.celsius) if (it == "F") getString(R.string.fahrenheit) else getString(R.string.celsius)
} }
checkLocationPermission()
} }
viewModel.weatherRefreshPeriod.observe(viewLifecycleOwner) { viewModel.weatherRefreshPeriod.observe(viewLifecycleOwner) {
maintainScrollPosition { maintainScrollPosition {
binding.labelWeatherRefreshPeriod.text = getString(SettingsStringHelper.getRefreshPeriodString(it)) binding.labelWeatherRefreshPeriod.text = getString(SettingsStringHelper.getRefreshPeriodString(it))
} }
checkLocationPermission()
} }
viewModel.weatherIconPack.observe(viewLifecycleOwner) { viewModel.weatherIconPack.observe(viewLifecycleOwner) {
maintainScrollPosition { maintainScrollPosition {
binding.labelWeatherIconPack.text = getString(R.string.settings_weather_icon_pack_default).format((it + 1)) binding.labelWeatherIconPack.text = getString(R.string.settings_weather_icon_pack_default).format((it + 1))
} }
checkLocationPermission()
} }
} }
private fun 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 binding.locationPermissionAlert.isVisible = false
WeatherReceiver.setUpdates(requireContext()) } else if (Preferences.customLocationAdd == "") {
} else if (Preferences.showWeather && Preferences.customLocationAdd == "") {
binding.locationPermissionAlert.isVisible = true binding.locationPermissionAlert.isVisible = true
binding.locationPermissionAlert.setOnClickListener { binding.locationPermissionAlert.setOnClickListener {
requirePermission() 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 { } else {
binding.locationPermissionAlert.isVisible = false binding.locationPermissionAlert.isVisible = false
} }
} }
private fun checkWeatherProviderConfig() { 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.weatherProviderError.text = Preferences.weatherProviderError
binding.weatherProviderLocationError.isVisible = Preferences.showWeather && Preferences.weatherProviderLocationError != "" binding.weatherProviderLocationError.isVisible = Preferences.weatherProviderLocationError != ""
binding.weatherProviderLocationError.text = Preferences.weatherProviderLocationError binding.weatherProviderLocationError.text = Preferences.weatherProviderLocationError
} }
@ -177,11 +186,11 @@ class WeatherFragment : Fragment() {
.addItem(getString(R.string.celsius), "C") .addItem(getString(R.string.celsius), "C")
.addOnSelectItemListener { value -> .addOnSelectItemListener { value ->
if (value != Preferences.weatherTempUnit) { if (value != Preferences.weatherTempUnit) {
Preferences.weatherTempUnit = value
viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.launch {
WeatherHelper.updateWeather(requireContext()) WeatherHelper.updateWeather(requireContext())
} }
} }
Preferences.weatherTempUnit = value
}.show() }.show()
} }
@ -193,7 +202,10 @@ class WeatherFragment : Fragment() {
} }
dialog dialog
.addOnSelectItemListener { value -> .addOnSelectItemListener { value ->
Preferences.weatherRefreshPeriod = value if (value != Preferences.weatherRefreshPeriod) {
Preferences.weatherRefreshPeriod = value
WeatherReceiver.setUpdates(requireContext())
}
}.show() }.show()
} }
@ -206,12 +218,12 @@ class WeatherFragment : Fragment() {
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
when (requestCode) { when (requestCode) {
Constants.RESULT_CODE_CUSTOM_LOCATION -> { Constants.RESULT_CODE_CUSTOM_LOCATION -> {
WeatherReceiver.setUpdates(requireContext()) viewLifecycleOwner.lifecycleScope.launch {
checkLocationPermission() WeatherHelper.updateWeather(requireContext())
} }
RequestCode.WEATHER_PROVIDER_REQUEST_CODE.code -> {
checkLocationPermission()
} }
//RequestCode.WEATHER_PROVIDER_REQUEST_CODE.code -> {
//}
} }
} }
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
@ -220,12 +232,19 @@ class WeatherFragment : Fragment() {
private fun requirePermission() { private fun requirePermission() {
Dexter.withContext(requireContext()) Dexter.withContext(requireContext())
.withPermissions( .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 { ).withListener(object: MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) { override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
report?.let { report?.let {
if (report.areAllPermissionsGranted()){ if (report.areAllPermissionsGranted()) {
checkLocationPermission() 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 showDividers = Preferences.asLiveData(Preferences::showDividers)
val secondRowTopMargin = Preferences.asLiveData(Preferences::secondRowTopMargin) val secondRowTopMargin = Preferences.asLiveData(Preferences::secondRowTopMargin)
val widgetAlign = Preferences.asLiveData(Preferences::widgetAlign) val widgetAlign = Preferences.asLiveData(Preferences::widgetAlign)
val widgetMargin = Preferences.asLiveData(Preferences::widgetMargin)
val widgetPadding = Preferences.asLiveData(Preferences::widgetPadding)
// Calendar Settings // Calendar Settings
val showEvents = Preferences.asLiveData(Preferences::showEvents) val showEvents = Preferences.asLiveData(Preferences::showEvents)
@ -57,6 +59,7 @@ class MainViewModel(context: Application) : AndroidViewModel(context) {
val showUntil = Preferences.asLiveData(Preferences::showUntil) val showUntil = Preferences.asLiveData(Preferences::showUntil)
val showDiffTime = Preferences.asLiveData(Preferences::showDiffTime) val showDiffTime = Preferences.asLiveData(Preferences::showDiffTime)
val showNextEvent = Preferences.asLiveData(Preferences::showNextEvent) val showNextEvent = Preferences.asLiveData(Preferences::showNextEvent)
val showNextEventOnMultipleLines = Preferences.asLiveData(Preferences::showNextEventOnMultipleLines)
val openEventDetails = Preferences.asLiveData(Preferences::openEventDetails) val openEventDetails = Preferences.asLiveData(Preferences::openEventDetails)
val calendarAppName = Preferences.asLiveData(Preferences::calendarAppName) val calendarAppName = Preferences.asLiveData(Preferences::calendarAppName)
val widgetUpdateFrequency = Preferences.asLiveData(Preferences::widgetUpdateFrequency) val widgetUpdateFrequency = Preferences.asLiveData(Preferences::widgetUpdateFrequency)
@ -101,7 +104,8 @@ class MainViewModel(context: Application) : AndroidViewModel(context) {
// UI // UI
val fragmentScrollY = MutableLiveData<Int>() 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::clockTextSize)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockTextColor)) { value = true } addSource(Preferences.asLiveData(Preferences::clockTextColor)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockTextAlpha)) { value = true } addSource(Preferences.asLiveData(Preferences::clockTextAlpha)) { value = true }
@ -110,8 +114,6 @@ class MainViewModel(context: Application) : AndroidViewModel(context) {
addSource(Preferences.asLiveData(Preferences::showAMPMIndicator)) { value = true } addSource(Preferences.asLiveData(Preferences::showAMPMIndicator)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockBottomMargin)) { value = true } addSource(Preferences.asLiveData(Preferences::clockBottomMargin)) { value = true }
addSource(Preferences.asLiveData(Preferences::altTimezoneLabel)) { 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::textGlobalColor)) { value = true }
addSource(Preferences.asLiveData(Preferences::textGlobalAlpha)) { value = true } addSource(Preferences.asLiveData(Preferences::textGlobalAlpha)) { value = true }
addSource(Preferences.asLiveData(Preferences::textSecondaryColor)) { value = true } addSource(Preferences.asLiveData(Preferences::textSecondaryColor)) { value = true }
@ -135,6 +137,8 @@ class MainViewModel(context: Application) : AndroidViewModel(context) {
addSource(Preferences.asLiveData(Preferences::customFontVariant)) { value = true } addSource(Preferences.asLiveData(Preferences::customFontVariant)) { value = true }
addSource(Preferences.asLiveData(Preferences::secondRowInformation)) { value = true } addSource(Preferences.asLiveData(Preferences::secondRowInformation)) { value = true }
addSource(Preferences.asLiveData(Preferences::widgetAlign)) { 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::showDividers)) { value = true }
addSource(Preferences.asLiveData(Preferences::secondRowTopMargin)) { value = true } addSource(Preferences.asLiveData(Preferences::secondRowTopMargin)) { value = true }
addSource(Preferences.asLiveData(Preferences::isDateCapitalize)) { value = true } addSource(Preferences.asLiveData(Preferences::isDateCapitalize)) { value = true }
@ -144,6 +148,7 @@ class MainViewModel(context: Application) : AndroidViewModel(context) {
addSource(Preferences.asLiveData(Preferences::calendarAllDay)) { value = true } addSource(Preferences.asLiveData(Preferences::calendarAllDay)) { value = true }
addSource(Preferences.asLiveData(Preferences::showDiffTime)) { value = true } addSource(Preferences.asLiveData(Preferences::showDiffTime)) { value = true }
addSource(Preferences.asLiveData(Preferences::showNextEvent)) { value = true } addSource(Preferences.asLiveData(Preferences::showNextEvent)) { value = true }
addSource(Preferences.asLiveData(Preferences::showNextEventOnMultipleLines)) { value = true }
addSource(Preferences.asLiveData(Preferences::showDeclinedEvents)) { value = true } addSource(Preferences.asLiveData(Preferences::showDeclinedEvents)) { value = true }
addSource(Preferences.asLiveData(Preferences::showInvitedEvents)) { value = true } addSource(Preferences.asLiveData(Preferences::showInvitedEvents)) { value = true }
addSource(Preferences.asLiveData(Preferences::showAcceptedEvents)) { value = true } addSource(Preferences.asLiveData(Preferences::showAcceptedEvents)) { value = true }
@ -169,6 +174,7 @@ class MainViewModel(context: Application) : AndroidViewModel(context) {
addSource(Preferences.asLiveData(Preferences::musicPlayersFilter)) { value = true } addSource(Preferences.asLiveData(Preferences::musicPlayersFilter)) { value = true }
addSource(Preferences.asLiveData(Preferences::appNotificationsFilter)) { value = true } addSource(Preferences.asLiveData(Preferences::appNotificationsFilter)) { value = true }
addSource(Preferences.asLiveData(Preferences::showEventsAsGlanceProvider)) { value = true } addSource(Preferences.asLiveData(Preferences::showEventsAsGlanceProvider)) { value = true }
addSource(Preferences.asLiveData(Preferences::showWeatherAsGlanceProvider)) { value = true }
addSource(Preferences.asLiveData(Preferences::installedIntegrations)) { value = true } addSource(Preferences.asLiveData(Preferences::installedIntegrations)) { value = true }
} }

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@ package com.tommasoberlose.anotherwidget.ui.widgets
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.util.TypedValue import android.util.TypedValue
import android.view.Gravity
import android.view.View import android.view.View
import android.widget.RemoteViews import android.widget.RemoteViews
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
@ -24,20 +25,21 @@ class ClockWidget(val context: Context) {
views.setViewVisibility(R.id.clock_bottom_margin_small, View.GONE) views.setViewVisibility(R.id.clock_bottom_margin_small, View.GONE)
views.setViewVisibility(R.id.clock_bottom_margin_medium, View.GONE) views.setViewVisibility(R.id.clock_bottom_margin_medium, View.GONE)
views.setViewVisibility(R.id.clock_bottom_margin_large, View.GONE) views.setViewVisibility(R.id.clock_bottom_margin_large, View.GONE)
views.setViewVisibility(R.id.timezones_container, View.GONE)
} else { } else {
views.setTextColor(R.id.time, ColorHelper.getClockFontColor(context.isDarkTheme())) views.setTextColor(R.id.time, ColorHelper.getClockFontColor(context.isDarkTheme()))
views.setTextColor(R.id.time_am_pm, ColorHelper.getClockFontColor(context.isDarkTheme())) views.setTextColor(R.id.time_am_pm, ColorHelper.getClockFontColor(context.isDarkTheme()))
views.setTextViewTextSize( views.setTextViewTextSize(
R.id.time, R.id.time,
TypedValue.COMPLEX_UNIT_SP, TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(context) Preferences.clockTextSize
) )
views.setTextViewTextSize( views.setTextViewTextSize(
R.id.time_am_pm, R.id.time_am_pm,
TypedValue.COMPLEX_UNIT_SP, TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(context) / 5 * 2 Preferences.clockTextSize / 5 * 2
) )
val clockPIntent = PendingIntent.getActivity( val clockPIntent = IntentHelper.getPendingIntent(
context, context,
widgetID, widgetID,
IntentHelper.getClockIntent(context), IntentHelper.getClockIntent(context),
@ -78,19 +80,29 @@ class ClockWidget(val context: Context) {
views.setTextViewTextSize( views.setTextViewTextSize(
R.id.alt_timezone_time, R.id.alt_timezone_time,
TypedValue.COMPLEX_UNIT_SP, TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(context) / 3 Preferences.clockTextSize / 3
) )
views.setTextViewTextSize( views.setTextViewTextSize(
R.id.alt_timezone_time_am_pm, R.id.alt_timezone_time_am_pm,
TypedValue.COMPLEX_UNIT_SP, TypedValue.COMPLEX_UNIT_SP,
(Preferences.clockTextSize.toPixel(context) / 3) / 5 * 2 (Preferences.clockTextSize / 3) / 5 * 2
) )
views.setTextViewTextSize( views.setTextViewTextSize(
R.id.alt_timezone_label, R.id.alt_timezone_label,
TypedValue.COMPLEX_UNIT_SP, 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.setOnClickPendingIntent(R.id.timezones_container, clockPIntent)
views.setViewVisibility(R.id.timezones_container, View.VISIBLE) views.setViewVisibility(R.id.timezones_container, View.VISIBLE)
} else { } else {

View File

@ -1,890 +0,0 @@
package com.tommasoberlose.anotherwidget.ui.widgets
import android.Manifest
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.graphics.Typeface
import android.text.format.DateUtils
import android.util.Log
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.RemoteViews
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.view.updateMargins
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.LeftAlignedWidgetBinding
import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.global.Actions
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.*
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toIntValue
import com.tommasoberlose.anotherwidget.helpers.ImageHelper.applyShadow
import com.tommasoberlose.anotherwidget.receivers.CrashlyticsReceiver
import com.tommasoberlose.anotherwidget.receivers.NewCalendarEventReceiver
import com.tommasoberlose.anotherwidget.receivers.WidgetClickListenerReceiver
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.convertDpToPixel
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
import java.text.DateFormat
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt
class LeftAlignedWidget(val context: Context) {
fun generateWidget(appWidgetId: Int, w: Int, typeface: Typeface? = null): RemoteViews {
var views = RemoteViews(context.packageName, R.layout.left_aligned_widget_sans)
try {
// Background
views.setInt(
R.id.widget_shape_background,
"setColorFilter",
ColorHelper.getBackgroundColorRgb(context.isDarkTheme())
)
views.setInt(
R.id.widget_shape_background,
"setImageAlpha",
ColorHelper.getBackgroundAlpha(context.isDarkTheme())
)
val refreshIntent = PendingIntent.getActivity(
context,
appWidgetId,
IntentHelper.getWidgetUpdateIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.widget_shape_background, refreshIntent)
} catch (ex: Exception) {
ex.printStackTrace()
CrashlyticsReceiver.sendCrash(context, ex)
}
// Clock
views = ClockWidget(context).updateClockView(views, appWidgetId)
// Setup listener
try {
val generatedBinding = generateWidgetView(typeface)
views.setImageViewBitmap(
R.id.bitmap_container,
BitmapHelper.getBitmapFromView(generatedBinding.root, width = w)
)
views = updateGridView(generatedBinding, views, appWidgetId)
} catch (ex: Exception) {
ex.printStackTrace()
CrashlyticsReceiver.sendCrash(context, ex)
}
return views
}
private fun updateGridView(bindingView: LeftAlignedWidgetBinding, views: RemoteViews, widgetID: Int): RemoteViews {
val eventRepository = EventRepository(context)
try {
// Weather
if (Preferences.showWeather && Preferences.weatherIcon != "") {
views.setViewVisibility(R.id.weather_rect, View.VISIBLE)
views.setViewVisibility(R.id.weather_sub_line, View.GONE)
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.weather_rect, weatherPIntent)
views.setOnClickPendingIntent(R.id.weather_sub_line_rect, weatherPIntent)
views.setImageViewBitmap(
R.id.weather_rect,
BitmapHelper.getBitmapFromView(bindingView.weatherDateLine, draw = false)
)
views.setImageViewBitmap(
R.id.weather_sub_line_rect,
BitmapHelper.getBitmapFromView(bindingView.weatherSubLine, draw = false)
)
} else {
views.setViewVisibility(R.id.weather_rect, View.GONE)
views.setViewVisibility(R.id.weather_sub_line, View.GONE)
}
// Calendar
views.setImageViewBitmap(
R.id.date_rect,
BitmapHelper.getBitmapFromView(bindingView.date, draw = false)
)
val calPIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getCalendarIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.date_rect, calPIntent)
views.setViewVisibility(R.id.first_line_rect, View.VISIBLE)
val nextEvent = eventRepository.getNextEvent()
val nextAlarm = AlarmHelper.getNextAlarm(context)
// Spacing
views.setViewVisibility(
R.id.sub_line_top_margin_small_sans,
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.SMALL.rawValue) View.VISIBLE else View.GONE
)
views.setViewVisibility(
R.id.sub_line_top_margin_medium_sans,
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.MEDIUM.rawValue) View.VISIBLE else View.GONE
)
views.setViewVisibility(
R.id.sub_line_top_margin_large_sans,
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.rawValue) View.VISIBLE else View.GONE
)
if (Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null && !Preferences.showEventsAsGlanceProvider) {
if (Preferences.showNextEvent && eventRepository.getEventsCount() > 1) {
// Action next event
views.setImageViewBitmap(
R.id.action_next_rect,
BitmapHelper.getBitmapFromView(bindingView.actionNext, draw = false)
)
views.setViewVisibility(R.id.action_next_rect, View.VISIBLE)
views.setOnClickPendingIntent(
R.id.action_next_rect,
PendingIntent.getBroadcast(
context,
widgetID,
Intent(
context,
NewCalendarEventReceiver::class.java
).apply { action = Actions.ACTION_GO_TO_NEXT_EVENT },
PendingIntent.FLAG_UPDATE_CURRENT
)
)
views.setViewVisibility(R.id.action_next_rect, View.VISIBLE)
} else {
views.setViewVisibility(R.id.action_next_rect, View.GONE)
}
// Event intent
val eventIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getEventIntent(context, nextEvent),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.next_event_rect, eventIntent)
views.setViewVisibility(R.id.next_event_rect, View.VISIBLE)
// Event time difference
if (Preferences.showDiffTime && Calendar.getInstance().timeInMillis < nextEvent.startDate) {
views.setImageViewBitmap(
R.id.next_event_difference_time_rect,
BitmapHelper.getBitmapFromView(
bindingView.nextEventDifferenceTime,
draw = false
)
)
views.setOnClickPendingIntent(R.id.next_event_difference_time_rect, eventIntent)
views.setViewVisibility(R.id.next_event_difference_time_rect, View.VISIBLE)
} else {
views.setViewVisibility(R.id.next_event_difference_time_rect, View.GONE)
}
// Event information
if (nextEvent.address != "" && Preferences.secondRowInformation == 1) {
val mapIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getGoogleMapsIntentFromAddress(context, nextEvent.address),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, mapIntent)
} else {
val pIntentDetail = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getEventIntent(
context,
nextEvent,
forceEventDetails = true
),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, pIntentDetail)
}
views.setImageViewBitmap(
R.id.next_event_rect,
BitmapHelper.getBitmapFromView(bindingView.nextEvent, draw = false)
)
views.setViewVisibility(R.id.calendar_layout_rect, View.VISIBLE)
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.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
loop@ for (provider: Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(context)) {
when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> {
if (MediaPlayerHelper.isSomeonePlaying(context)) {
val musicIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getMusicIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, musicIntent)
showSomething = true
break@loop
}
}
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
}
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
if (Preferences.showBatteryCharging) {
BatteryHelper.updateBatteryInfo(context)
if (Preferences.isCharging || Preferences.isBatteryLevelLow) {
val batteryIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getBatteryIntent(),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, batteryIntent)
showSomething = true
break@loop
}
}
}
Constants.GlanceProviderId.CUSTOM_INFO -> {
if (Preferences.customNotes.isNotEmpty()) {
break@loop
}
}
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
if (Preferences.showDailySteps && Preferences.googleFitSteps > 0) {
val fitIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getFitIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, fitIntent)
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.NOTIFICATIONS -> {
if (Preferences.showNotifications && ActiveNotificationsHelper.showLastNotification()) {
try {
if (Preferences.lastNotificationIcon != 0) {
val remotePackageContext = context.createPackageContext(
Preferences.lastNotificationPackage, 0)
ContextCompat.getDrawable(
remotePackageContext,
Preferences.lastNotificationIcon)
}
val notificationIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getNotificationIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(
R.id.sub_line_rect,
notificationIntent
)
showSomething = true
break@loop
} catch (ex: Exception) {}
}
}
Constants.GlanceProviderId.GREETINGS -> {
if (Preferences.showGreetings && GreetingsHelper.showGreetings() && GreetingsHelper.getRandomString(context).isNotBlank()) {
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.EVENTS -> {
if (Preferences.showEventsAsGlanceProvider&& Preferences.showEvents && context.checkGrantedPermission(
Manifest.permission.READ_CALENDAR) && nextEvent != null) {
val pIntentDetail = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getEventIntent(
context,
nextEvent,
forceEventDetails = true
),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(
R.id.sub_line_rect,
pIntentDetail
)
showSomething = 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.sub_line_rect, View.VISIBLE)
views.setViewVisibility(R.id.calendar_layout_rect, View.GONE)
views.setViewVisibility(R.id.weather_sub_line_rect, View.GONE)
} else {
// Spacing
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)
}
}
// Second row
views.setImageViewBitmap(
R.id.sub_line_rect,
BitmapHelper.getBitmapFromView(bindingView.subLine, draw = false)
)
} catch (ex: Exception) {
ex.printStackTrace()
CrashlyticsReceiver.sendCrash(context, ex)
} finally {
eventRepository.close()
}
return views
}
// Generates the widget bitmap from the view
fun generateWidgetView(typeface: Typeface? = null): LeftAlignedWidgetBinding {
val eventRepository = EventRepository(context)
val bindingView = LeftAlignedWidgetBinding.inflate(LayoutInflater.from(context))
bindingView.loader.isVisible = false
// Weather
if (Preferences.showWeather && Preferences.weatherIcon != "") {
bindingView.weatherDateLine.isVisible = true
val currentTemp = String.format(
Locale.getDefault(),
"%d°%s",
Preferences.weatherTemp.roundToInt(),
Preferences.weatherRealTempUnit
)
val icon: String = Preferences.weatherIcon
if (icon == "") {
bindingView.weatherSubLineWeatherIcon.isVisible = false
bindingView.weatherDateLineWeatherIcon.isVisible = false
} else {
bindingView.weatherSubLineWeatherIcon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon))
bindingView.weatherDateLineWeatherIcon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon))
bindingView.weatherSubLineWeatherIcon.isVisible = true
bindingView.weatherDateLineWeatherIcon.isVisible = true
}
bindingView.weatherDateLineTemperature.text = currentTemp
bindingView.weatherSubLineTemperature.text = currentTemp
if (GlanceProviderHelper.showGlanceProviders(context)) {
bindingView.weatherSubLine.isVisible = false
}
} else {
bindingView.weatherDateLine.isVisible = false
bindingView.weatherSubLine.isVisible = false
}
val now = Calendar.getInstance().apply {
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
bindingView.dateLayout.isVisible = true
bindingView.calendarLayout.isVisible = false
bindingView.nextEventDifferenceTime.isVisible = false
bindingView.actionNext.isVisible = false
bindingView.date.text = DateHelper.getDateText(context, now)
val nextEvent = eventRepository.getNextEvent()
val nextAlarm = AlarmHelper.getNextAlarm(context)
if (Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null && !Preferences.showEventsAsGlanceProvider) {
// Multiple counter
bindingView.actionNext.isVisible =
Preferences.showNextEvent && eventRepository.getEventsCount() > 1
bindingView.nextEvent.text = nextEvent.title
if (Preferences.showDiffTime && now.timeInMillis < nextEvent.startDate) {
bindingView.nextEventDifferenceTime.text = if (!nextEvent.allDay) {
SettingsStringHelper.getDifferenceText(
context,
now.timeInMillis,
nextEvent.startDate
)
.toLowerCase(Locale.getDefault())
} else {
SettingsStringHelper.getAllDayEventDifferenceText(
context,
now.timeInMillis,
nextEvent.startDate
).toLowerCase(Locale.getDefault())
}
bindingView.nextEventDifferenceTime.isVisible = true
} else {
bindingView.nextEventDifferenceTime.isVisible = false
}
if (nextEvent.address != "" && Preferences.secondRowInformation == 1) {
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_place_24
)
)
bindingView.subLineText.text = nextEvent.address
} else {
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_today_24
)
)
if (!nextEvent.allDay) {
val startHour =
DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault())
.format(nextEvent.startDate)
val endHour =
DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault())
.format(nextEvent.endDate)
var dayDiff =
TimeUnit.MILLISECONDS.toDays(nextEvent.endDate - nextEvent.startDate)
val startCal = Calendar.getInstance()
startCal.timeInMillis = nextEvent.startDate
val endCal = Calendar.getInstance()
endCal.timeInMillis = nextEvent.endDate
if (startCal.get(Calendar.HOUR_OF_DAY) > endCal.get(Calendar.HOUR_OF_DAY)) {
dayDiff++
} else if (startCal.get(Calendar.HOUR_OF_DAY) == endCal.get(Calendar.HOUR_OF_DAY) && startCal.get(
Calendar.MINUTE
) > endCal.get(Calendar.MINUTE)
) {
dayDiff++
}
var multipleDay = ""
if (dayDiff > 0) {
multipleDay = String.format(
" (+%s%s)",
dayDiff,
context.getString(R.string.day_char)
)
}
if (nextEvent.startDate != nextEvent.endDate) {
bindingView.subLineText.text =
String.format("%s - %s%s", startHour, endHour, multipleDay)
} else {
bindingView.subLineText.text =
String.format("%s", startHour)
}
} 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)
} 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)
} else {
DateUtils.formatDateTime(context, nextEvent.startDate, flags)
}
}
}
bindingView.dateLayout.isVisible = false
bindingView.calendarLayout.isVisible = true
bindingView.subLine.isVisible = true
bindingView.weatherSubLine.isVisible = true
bindingView.subLineTopMarginSmall.visibility = View.GONE
bindingView.subLineTopMarginMedium.visibility = View.GONE
bindingView.subLineTopMarginLarge.visibility = View.GONE
} else if (GlanceProviderHelper.showGlanceProviders(context)) {
bindingView.subLineIcon.isVisible = true
var showSomething = false
loop@ for (provider: Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(
context
)) {
when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> {
if (MediaPlayerHelper.isSomeonePlaying(context)) {
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_music_note_24
)
)
bindingView.subLineText.text = MediaPlayerHelper.getMediaInfo()
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
if (Preferences.showNextAlarm && nextAlarm != "") {
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_alarm_24
)
)
bindingView.subLineText.text = AlarmHelper.getNextAlarm(context)
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
if (Preferences.showBatteryCharging) {
BatteryHelper.updateBatteryInfo(context)
if (Preferences.isCharging) {
bindingView.subLineIcon.isVisible = false
val batteryLevel = BatteryHelper.getBatteryLevel(context)
if (batteryLevel != 100) {
bindingView.subLineText.text = context.getString(R.string.charging)
} else {
bindingView.subLineText.text =
context.getString(R.string.charged)
}
showSomething = true
break@loop
} else if (Preferences.isBatteryLevelLow) {
bindingView.subLineIcon.isVisible = false
bindingView.subLineText.text =
context.getString(R.string.battery_low_warning)
showSomething = true
break@loop
}
}
}
Constants.GlanceProviderId.CUSTOM_INFO -> {
if (Preferences.customNotes.isNotEmpty()) {
bindingView.subLineIcon.isVisible = false
bindingView.subLineText.text = Preferences.customNotes
bindingView.subLineText.maxLines = 2
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
if (Preferences.showDailySteps && Preferences.googleFitSteps > 0) {
bindingView.subLineIcon.isVisible = false
bindingView.subLineText.text =
context.getString(R.string.daily_steps_counter)
.format(Preferences.googleFitSteps)
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.NOTIFICATIONS -> {
if (Preferences.showNotifications && ActiveNotificationsHelper.showLastNotification()) {
try {
if (Preferences.lastNotificationIcon != 0) {
val remotePackageContext = context.createPackageContext(
Preferences.lastNotificationPackage, 0)
val icon = ContextCompat.getDrawable(remotePackageContext,
Preferences.lastNotificationIcon)
bindingView.subLineIcon.isVisible = true
bindingView.subLineIcon.setImageDrawable(icon)
} else {
bindingView.subLineIcon.isVisible = false
}
bindingView.subLineText.text = Preferences.lastNotificationTitle
showSomething = true
break@loop
} catch (ex: Exception) {}
}
}
Constants.GlanceProviderId.GREETINGS -> {
val greetingsText = GreetingsHelper.getRandomString(context)
if (Preferences.showGreetings && GreetingsHelper.showGreetings() && greetingsText.isNotBlank()) {
bindingView.subLineText.text = greetingsText
bindingView.subLineText.maxLines = 2
bindingView.subLineIcon.isVisible = false
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.EVENTS -> {
if (Preferences.showEventsAsGlanceProvider && Preferences.showEvents && context.checkGrantedPermission(
Manifest.permission.READ_CALENDAR) && nextEvent != null) {
bindingView.subLineText.text = context.getString(R.string.events_glance_provider_format).format(nextEvent.title, if (Preferences.showDiffTime && now.timeInMillis < nextEvent.startDate) {
if (!nextEvent.allDay) {
SettingsStringHelper.getDifferenceText(
context,
now.timeInMillis,
nextEvent.startDate
)
.toLowerCase(Locale.getDefault())
} else {
SettingsStringHelper.getAllDayEventDifferenceText(
context,
now.timeInMillis,
nextEvent.startDate
).toLowerCase(Locale.getDefault())
}
} else "").trimEnd()
bindingView.subLineIcon.isVisible = true
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_today_24
)
)
showSomething = true
break@loop
}
}
}
}
if (showSomething) {
bindingView.dateLayout.isVisible = true
bindingView.calendarLayout.isVisible = false
bindingView.subLine.isVisible = true
bindingView.weatherSubLine.isVisible = false
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 {
bindingView.subLineIcon.isVisible = false
}
}
// Color
listOf<TextView>(
bindingView.date,
bindingView.weatherDateLineTemperature,
bindingView.nextEvent,
bindingView.nextEventDifferenceTime,
).forEach {
it.setTextColor(ColorHelper.getFontColor(context.applicationContext.isDarkTheme()))
}
if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.rawValue) {
listOf<ImageView>(bindingView.actionNext)
} else {
listOf<ImageView>(
bindingView.actionNext,
bindingView.weatherDateLineWeatherIcon,
bindingView.weatherSubLineWeatherIcon
)
}.forEach {
it.setColorFilter(ColorHelper.getFontColorRgb(context.applicationContext.isDarkTheme()))
it.alpha =
(if (context.isDarkTheme()) Preferences.textGlobalAlphaDark.toIntValue()
.toFloat() else Preferences.textGlobalAlpha.toIntValue()
.toFloat()) / 100
}
listOf<TextView>(bindingView.subLineText, bindingView.weatherSubLineDivider, bindingView.weatherSubLineTemperature).forEach {
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
}
// Text Size
listOf<Pair<TextView, Float>>(
bindingView.date to Preferences.textMainSize,
bindingView.weatherDateLineTemperature to ((Preferences.textMainSize + Preferences.textSecondSize) / 2),
bindingView.nextEvent to Preferences.textMainSize,
bindingView.nextEventDifferenceTime to Preferences.textMainSize,
bindingView.subLineText to Preferences.textSecondSize,
bindingView.weatherSubLineDivider to (Preferences.textSecondSize - 2),
bindingView.weatherSubLineTemperature to Preferences.textSecondSize,
).forEach {
it.first.setTextSize(TypedValue.COMPLEX_UNIT_SP, it.second)
}
// 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
// Shadows
val shadowRadius =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> 0f
1 -> 5f
2 -> 5f
else -> 5f
}
val shadowColor =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> Color.TRANSPARENT
1 -> R.color.black_50
2 -> Color.BLACK
else -> R.color.black_50
}
val shadowDy =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> 0f
1 -> 0f
2 -> 1f
else -> 0f
}
listOf<TextView>(
bindingView.date,
bindingView.weatherDateLineTemperature,
bindingView.nextEvent,
bindingView.nextEventDifferenceTime,
bindingView.subLineText,
bindingView.weatherSubLineDivider,
bindingView.weatherSubLineTemperature,
).forEach {
it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor)
}
// Icons shadow
listOf(
Pair(bindingView.subLineIcon, bindingView.subLineIconShadow),
).forEach {
if ((if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) == 0) {
it.second.isVisible = false
} else {
it.second.isVisible = it.first.isVisible
it.second.scaleX = it.first.scaleX
it.second.scaleY = it.first.scaleY
it.second.applyShadow(it.first)
}
}
listOf(
Pair(bindingView.actionNext, bindingView.actionNextShadow),
).forEach {
if ((if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) == 0) {
it.second.isVisible = false
} else {
it.second.isVisible = it.first.isVisible
it.second.scaleX = it.first.scaleX
it.second.scaleY = it.first.scaleY
it.second.applyShadow(it.first, 0.6f)
}
}
// 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")
}
listOf<TextView>(
bindingView.date,
bindingView.weatherDateLineTemperature,
bindingView.nextEvent,
bindingView.nextEventDifferenceTime,
bindingView.subLineText,
bindingView.weatherSubLineDivider,
bindingView.weatherSubLineTemperature,
).forEach {
it.typeface = googleSans
}
} else if (Preferences.customFont == Constants.CUSTOM_FONT_DOWNLOADED && typeface != null) {
listOf<TextView>(
bindingView.date,
bindingView.weatherDateLineTemperature,
bindingView.nextEvent,
bindingView.nextEventDifferenceTime,
bindingView.subLineText,
bindingView.weatherSubLineDivider,
bindingView.weatherSubLineTemperature,
).forEach {
it.typeface = typeface
}
}
// 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
}
}
eventRepository.close()
return bindingView
}
}

View File

@ -1,47 +1,20 @@
package com.tommasoberlose.anotherwidget.ui.widgets package com.tommasoberlose.anotherwidget.ui.widgets
import android.Manifest
import android.app.PendingIntent
import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider import android.appwidget.AppWidgetProvider
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Color
import android.graphics.Typeface import android.graphics.Typeface
import android.os.Bundle import android.os.Bundle
import android.text.format.DateUtils
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.RemoteViews import android.widget.RemoteViews
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.viewbinding.ViewBinding
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.LeftAlignedWidgetBinding
import com.tommasoberlose.anotherwidget.databinding.TheWidgetBinding
import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.global.Actions
import com.tommasoberlose.anotherwidget.global.Constants import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.* import com.tommasoberlose.anotherwidget.helpers.*
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toIntValue
import com.tommasoberlose.anotherwidget.helpers.ImageHelper.applyShadow
import com.tommasoberlose.anotherwidget.receivers.* import com.tommasoberlose.anotherwidget.receivers.*
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 com.tommasoberlose.anotherwidget.utils.toPixel
import java.text.DateFormat import java.lang.Exception
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToInt
class MainWidget : AppWidgetProvider() { class MainWidget : AppWidgetProvider() {
@ -90,25 +63,34 @@ class MainWidget : AppWidgetProvider() {
internal fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager, internal fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager,
appWidgetId: Int) { appWidgetId: Int) {
val displayMetrics = Resources.getSystem().displayMetrics
val width = displayMetrics.widthPixels
val height = displayMetrics.heightPixels
val dimensions = WidgetHelper.WidgetSizeProvider(context, appWidgetManager).getWidgetsSize(appWidgetId) val dimensions = WidgetHelper.WidgetSizeProvider(context, appWidgetManager).getWidgetsSize(appWidgetId)
WidgetHelper.runWithCustomTypeface(context) { WidgetHelper.runWithCustomTypeface(context) {
val views = when (Preferences.widgetAlign) { val views = when (Preferences.widgetAlign) {
Constants.WidgetAlign.LEFT.rawValue -> LeftAlignedWidget(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)
else -> StandardWidget(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, dimensions.first, it)
else -> StandardWidget(context).generateWidget(appWidgetId, dimensions.first, it)
}
try {
if (views != null) appWidgetManager.updateAppWidget(appWidgetId, views)
} catch (ex: Exception) {
ex.printStackTrace()
} }
appWidgetManager.updateAppWidget(appWidgetId, views)
} }
} }
fun getWidgetView(context: Context, typeface: Typeface?): ViewBinding { fun getWidgetView(context: Context, width: Int, typeface: Typeface?): RemoteViews? {
return when (Preferences.widgetAlign) { return when (Preferences.widgetAlign) {
Constants.WidgetAlign.LEFT.rawValue -> LeftAlignedWidget(context).generateWidgetView(typeface) Constants.WidgetAlign.LEFT.rawValue -> AlignedWidget(context).generateWidget(
else -> StandardWidget(context).generateWidgetView(typeface) 0,
width,
typeface
)
Constants.WidgetAlign.RIGHT.rawValue -> AlignedWidget(
context,
rightAligned = true
).generateWidget(0, width, typeface)
else -> StandardWidget(context).generateWidget(0, width, typeface)
} }
} }
} }

View File

@ -24,18 +24,20 @@ import android.util.TypedValue
import android.view.animation.AlphaAnimation import android.view.animation.AlphaAnimation
import android.widget.RelativeLayout import android.widget.RelativeLayout
import androidx.annotation.UiThread import androidx.annotation.UiThread
import androidx.appcompat.app.AppCompatDelegate
import androidx.browser.customtabs.CustomTabColorSchemeParams import androidx.browser.customtabs.CustomTabColorSchemeParams
import androidx.core.animation.addListener import androidx.core.animation.addListener
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.OnSingleClickListener import com.tommasoberlose.anotherwidget.components.OnSingleClickListener
import com.tommasoberlose.anotherwidget.global.Preferences
import java.util.* import java.util.*
fun PackageManager.missingSystemFeature(name: String): Boolean = !hasSystemFeature(name) fun PackageManager.missingSystemFeature(name: String): Boolean = !hasSystemFeature(name)
fun Context.toast(message: String, long: Boolean = false) { 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.setGravity(Gravity.CENTER, 0, 0)
toast.show() toast.show()
} }
@ -192,7 +194,7 @@ fun String.isValidEmail(): Boolean
Patterns.EMAIL_ADDRESS.matcher(this).matches() Patterns.EMAIL_ADDRESS.matcher(this).matches()
fun Context.isDarkTheme(): Boolean { 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) fun Activity.isNotificationAccessGranted(): Boolean = Settings.Secure.getString(this.contentResolver,"enabled_notification_listeners").contains(this.packageName)
@ -265,3 +267,11 @@ fun View.setOnSingleClickListener(l: View.OnClickListener) {
fun View.setOnSingleClickListener(l: (View) -> Unit) { fun View.setOnSingleClickListener(l: (View) -> Unit) {
setOnClickListener(OnSingleClickListener(l)) setOnClickListener(OnSingleClickListener(l))
} }
fun ignoreExceptions(function: () -> Unit) = run {
try {
function.invoke()
} catch (ex: Exception) {
ex.printStackTrace()
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 674 B

After

Width:  |  Height:  |  Size: 750 B

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: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 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: 224 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 381 B

After

Width:  |  Height:  |  Size: 563 B

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