Compare commits
139 Commits
v2.0.6
...
v2.0.14-be
Author | SHA1 | Date | |
---|---|---|---|
4ab12e33c7 | |||
364198ef08 | |||
2c81c7cfd2 | |||
3b723e5a1b | |||
6a912ee003 | |||
1644fb7682 | |||
bd4869540b | |||
31103303be | |||
e9c0cdd61c | |||
59e42029e8 | |||
b2ef2adb2a | |||
bab92f9169 | |||
cb480ed4ee | |||
6f83a45865 | |||
9e528e1f6f | |||
20d3ae0e32 | |||
ecea1265e7 | |||
c38b7a335c | |||
6ab8e40d45 | |||
d14dfee980 | |||
0a454f0b5f | |||
b7bc93e174 | |||
1dee4cc8e5 | |||
02b521c0b8 | |||
c04040e242 | |||
a208aa97b2 | |||
e3fda9457e | |||
b84631a2b2 | |||
bad06c5762 | |||
74d8966f0a | |||
d8a76936a6 | |||
e72ca4fc6f | |||
dd62212b06 | |||
8dfc12e412 | |||
2762f12151 | |||
a873c71918 | |||
e8f3c110a8 | |||
72bf3b04a7 | |||
ca6fb75dfd | |||
59dc5de21a | |||
ec40a277d7 | |||
507b7f2318 | |||
1a709a9406 | |||
770ba0cd13 | |||
6a92225edf | |||
ce801d4a7d | |||
25da807bd5 | |||
63212d13b1 | |||
79ad9b2500 | |||
9c0aa0a9ef | |||
b9197ddf2e | |||
d1362f7d81 | |||
a8c7d115ae | |||
a5a753e198 | |||
39ede20518 | |||
5527e6e405 | |||
565ed11f53 | |||
4bd8653d32 | |||
d121119ca9 | |||
e82a34bcc7 | |||
8867a1fbbd | |||
7265883d6c | |||
7a4fc6ff58 | |||
9f61215caa | |||
7ccf7eb8f6 | |||
704448a848 | |||
fa366b3d45 | |||
e58fef66aa | |||
94825808f4 | |||
c120bad9c6 | |||
0d2287dbdf | |||
9e40586456 | |||
4d75f4ca0c | |||
0859632803 | |||
47562b35ca | |||
0f4f02ea28 | |||
31cf950eee | |||
f230d300ee | |||
c610857056 | |||
770040ad93 | |||
f784817296 | |||
ec1c25cb4c | |||
e1d2f5a782 | |||
6e8c6cf055 | |||
68b5997e8d | |||
56b21be946 | |||
fdc02b2cef | |||
863b8f79d8 | |||
857a8009b6 | |||
9199f28ad9 | |||
ddb1b7494a | |||
fd2b1ba976 | |||
90f0d7de00 | |||
66ad5e0839 | |||
2ac7e072e5 | |||
a3392dabcf | |||
c2c54a04d2 | |||
2c237058b3 | |||
aa2b4b0510 | |||
4c27d1dd9b | |||
eba5575ee2 | |||
aff7e407ca | |||
f7fc31d968 | |||
90b588603d | |||
5d9bd11abb | |||
01005ec443 | |||
eaf6400e8b | |||
30339c7375 | |||
87da284be4 | |||
573c6d03e5 | |||
79e87b0648 | |||
9e9a91690e | |||
5f699af509 | |||
aae40b9dd3 | |||
cebd679856 | |||
06443ddddb | |||
233761a169 | |||
b81461f725 | |||
26b1948b70 | |||
af64818dff | |||
b04b103634 | |||
1dc050e77f | |||
ff171d4022 | |||
d91471d1ee | |||
ac381c8542 | |||
ba5a860e75 | |||
09bc9df22f | |||
385806413e | |||
7d2ea5a4d8 | |||
ce9f5a6e45 | |||
8f0f6bc868 | |||
34d8fa4b59 | |||
9401b89036 | |||
842c0d764f | |||
1754b4045b | |||
f28596c194 | |||
7f41a92a91 | |||
9a63b9bde2 | |||
40c9253159 |
4
.gitignore
vendored
@ -1,11 +1,11 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
/.idea/*
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
/tasksintegration/build
|
||||
/app/google-services.json
|
||||
apikey.properties
|
BIN
.idea/caches/build_file_checksums.ser
generated
17
.idea/codeStyles/Project.xml
generated
@ -1,6 +1,23 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value>
|
||||
<package name="java.util" alias="false" withSubpackages="false" />
|
||||
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
|
||||
<package name="io.ktor" alias="false" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="PACKAGES_IMPORT_LAYOUT">
|
||||
<value>
|
||||
<package name="" alias="false" withSubpackages="true" />
|
||||
<package name="java" alias="false" withSubpackages="true" />
|
||||
<package name="javax" alias="false" withSubpackages="true" />
|
||||
<package name="kotlin" alias="false" withSubpackages="true" />
|
||||
<package name="" alias="true" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="ALLOW_TRAILING_COMMA" value="true" />
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
|
2
.idea/gradle.xml
generated
@ -11,8 +11,6 @@
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
<option value="$PROJECT_DIR$/googlefit" />
|
||||
<option value="$PROJECT_DIR$/tasksintegration" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveModulePerSourceSet" value="false" />
|
||||
|
4
.idea/modules.xml
generated
@ -2,10 +2,8 @@
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/Another Widget.iml" filepath="$PROJECT_DIR$/Another Widget.iml" group="Another Widget" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/Another_Widget.iml" filepath="$PROJECT_DIR$/.idea/modules/Another_Widget.iml" group="Another_Widget" />
|
||||
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" group="Another Widget/app" />
|
||||
<module fileurl="file://$PROJECT_DIR$/googlefit/googlefit.iml" filepath="$PROJECT_DIR$/googlefit/googlefit.iml" group="Another Widget/googlefit" />
|
||||
<module fileurl="file://$PROJECT_DIR$/tasksintegration/tasksintegration.iml" filepath="$PROJECT_DIR$/tasksintegration/tasksintegration.iml" group="Another Widget/tasksintegration" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -11,7 +11,14 @@ While respecting the design of the application, there is a great opportunity to
|
||||
Also, as much as possible, there are always updates and new features in the short run.
|
||||
|
||||
Help me developing with feedback and support me on how you can.
|
||||
<div style="text-align:center"><a href="https://play.google.com/store/apps/details?id=com.tommasoberlose.anotherwidget" target="_blank"><img src="google-play-badge.png" height="100" /></a></div>
|
||||
<div style="text-align:center"><a href='https://play.google.com/store/apps/details?id=com.tommasoberlose.anotherwidget&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height='100px' src='https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png'/></a></div>
|
||||
|
||||
|
||||
Help with translations
|
||||
-------
|
||||
|
||||
Hey! You could view the file strings.xml ([here](https://github.com/tommasoberlose/another-widget/blob/master/app/src/main/res/values/strings.xml)) that contains the English version of the app strings.
|
||||
You have to copy the file, create a copy of it inside the folder values-[LANGUAGE-SUFFIX] with the translated strings and create a pull request to submit your changes.
|
||||
|
||||
License
|
||||
-------
|
||||
|
@ -10,7 +10,12 @@ apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
apply plugin: 'realm-android'
|
||||
|
||||
def apikeyPropertiesFile = rootProject.file("apikey.properties")
|
||||
def apikeyProperties = new Properties()
|
||||
apikeyProperties.load(new FileInputStream(apikeyPropertiesFile))
|
||||
|
||||
android {
|
||||
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "29.0.3"
|
||||
|
||||
@ -18,10 +23,11 @@ android {
|
||||
applicationId "com.tommasoberlose.anotherwidget"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 29
|
||||
versionCode 81
|
||||
versionName "2.0.6"
|
||||
versionCode 106
|
||||
versionName "2.0.14"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
buildConfigField("String", "GOOGLE_API_KEY", apikeyProperties['GOOGLE_API_KEY'])
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@ -52,10 +58,6 @@ android {
|
||||
}
|
||||
|
||||
viewBinding.enabled = true
|
||||
|
||||
dynamicFeatures = [":tasksintegration", ":googlefit"]
|
||||
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@ -63,13 +65,13 @@ dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
testImplementation 'junit:junit:4.13'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||
|
||||
// UI
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'com.google.android.material:material:1.2.0-alpha06'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
|
||||
implementation 'com.google.android.material:material:1.3.0-alpha03'
|
||||
implementation 'androidx.browser:browser:1.2.0'
|
||||
implementation 'net.idik:slimadapter:2.1.2'
|
||||
implementation 'com.google.android:flexbox:2.0.1'
|
||||
@ -79,18 +81,18 @@ dependencies {
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||
|
||||
implementation "androidx.work:work-runtime-ktx:2.3.4"
|
||||
implementation "androidx.work:work-runtime-ktx:2.4.0"
|
||||
|
||||
// EventBus
|
||||
implementation 'org.greenrobot:eventbus:3.1.1'
|
||||
|
||||
// Navigation
|
||||
implementation 'androidx.navigation:navigation-fragment:2.3.0-alpha05'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
|
||||
implementation 'androidx.navigation:navigation-fragment:2.3.0'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'
|
||||
|
||||
// Other
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation 'joda-time:joda-time:2.9.9'
|
||||
implementation 'joda-time:joda-time:2.10.3'
|
||||
implementation 'me.everything:providers-android:1.0.1'
|
||||
implementation 'com.github.warkiz.widget:indicatorseekbar:2.1.2'
|
||||
|
||||
@ -98,32 +100,45 @@ dependencies {
|
||||
implementation 'com.github.bumptech.glide:glide:4.11.0'
|
||||
kapt 'com.github.bumptech.glide:compiler:4.11.0'
|
||||
|
||||
// Fitness
|
||||
implementation 'com.google.android.gms:play-services-fitness:18.0.0'
|
||||
implementation 'com.google.android.gms:play-services-auth:18.1.0'
|
||||
|
||||
//Weather
|
||||
implementation 'com.github.KwabenBerko:OpenWeatherMap-Android-Library:2.0.2'
|
||||
implementation 'com.google.android.gms:play-services-location:17.0.0'
|
||||
implementation 'com.google.android.gms:play-services-location:17.1.0'
|
||||
|
||||
// Billing
|
||||
implementation 'com.android.billingclient:billing:2.2.0'
|
||||
implementation 'com.android.billingclient:billing-ktx:2.2.0'
|
||||
implementation 'com.android.billingclient:billing:3.0.1'
|
||||
implementation 'com.android.billingclient:billing-ktx:3.0.1'
|
||||
|
||||
// KTX
|
||||
implementation "androidx.core:core-ktx:1.2.0"
|
||||
implementation "androidx.core:core-ktx:1.3.2"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
|
||||
implementation "androidx.palette:palette-ktx:1.0.0"
|
||||
implementation 'androidx.core:core-ktx:1.2.0'
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
|
||||
//Retrofit
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0'
|
||||
implementation "com.github.haroldadmin:NetworkResponseAdapter:4.0.1"
|
||||
|
||||
//Coroutines
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5'
|
||||
|
||||
// Add the Firebase SDK for Crashlytics.
|
||||
implementation 'com.google.firebase:firebase-crashlytics:17.0.0'
|
||||
implementation 'com.google.firebase:firebase-crashlytics:17.2.2'
|
||||
|
||||
// Preferences
|
||||
implementation 'com.chibatching.kotpref:kotpref:2.10.0'
|
||||
implementation 'com.chibatching.kotpref:kotpref:2.11.0'
|
||||
implementation 'com.chibatching.kotpref:livedata-support:2.10.0'
|
||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||
|
||||
// Permissions
|
||||
implementation 'com.karumi:dexter:6.1.0'
|
||||
|
||||
// Billing
|
||||
implementation 'com.android.billingclient:billing:2.2.0'
|
||||
implementation 'com.android.billingclient:billing-ktx:2.2.0'
|
||||
// Fonts
|
||||
implementation 'com.github.firatkarababa:downloadable-font-list-library:1.0.2'
|
||||
}
|
||||
|
@ -1,40 +0,0 @@
|
||||
{
|
||||
"project_info": {
|
||||
"project_number": "791844924473",
|
||||
"firebase_url": "https://anotherwidget-182008.firebaseio.com",
|
||||
"project_id": "anotherwidget-182008",
|
||||
"storage_bucket": "anotherwidget-182008.appspot.com"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:791844924473:android:0ad4f6e3890f1ad320b1e8",
|
||||
"android_client_info": {
|
||||
"package_name": "com.tommasoberlose.anotherwidget"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "791844924473-73dh46rorjq8vm97dgbn6can2dcpqlf0.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyAeJRXstqnzebibxmm3FRM98nbwE_kC8tA"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": [
|
||||
{
|
||||
"client_id": "791844924473-73dh46rorjq8vm97dgbn6can2dcpqlf0.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
@ -10,15 +10,19 @@
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="com.android.vending.BILLING" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
|
||||
<uses-permission android:name="android.gms.permission.ACTIVITY_RECOGNITION"/>
|
||||
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
|
||||
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:fullBackupContent="@xml/my_backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:name=".AWApplication"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:theme="@style/AppTheme"
|
||||
tools:ignore="LockedOrientationActivity">
|
||||
<activity android:name=".ui.activities.MainActivity" android:launchMode="singleInstance" android:theme="@style/AppTheme.Main" android:screenOrientation="portrait">
|
||||
@ -29,11 +33,13 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".ui.activities.ChooseApplicationActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
|
||||
<activity android:name=".ui.activities.CustomFontActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
|
||||
<activity android:name=".ui.activities.CustomLocationActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
|
||||
<activity android:name=".ui.activities.WeatherProviderActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
|
||||
<activity android:name=".ui.activities.SupportDevActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
|
||||
<activity android:name=".ui.activities.CustomDateActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
|
||||
<activity android:name=".ui.activities.IntegrationsActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
|
||||
<activity android:name=".ui.activities.MusicPlayersFilterActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
|
||||
|
||||
|
||||
<receiver android:name=".ui.widgets.MainWidget">
|
||||
@ -82,44 +88,16 @@
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="com.tommasoberlose.anotherwidget.action.ACTION_WEATHER_UPDATE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.TIME_SET" />
|
||||
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
|
||||
<action android:name="android.intent.action.LOCALE_CHANGED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".receivers.PlayerReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
tools:ignore="ExportedReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.music.metachanged" />
|
||||
<action android:name="com.android.music.playstatechanged" />
|
||||
<action android:name="com.android.music.playbackcomplete" />
|
||||
<action android:name="com.android.music.queuechanged" />
|
||||
|
||||
<action android:name="com.htc.music.metachanged" />
|
||||
<action android:name="fm.last.android.metachanged" />
|
||||
<action android:name="com.sec.android.app.music.metachanged" />
|
||||
<action android:name="com.nullsoft.winamp.metachanged" />
|
||||
<action android:name="com.amazon.mp3.metachanged" />
|
||||
<action android:name="com.miui.player.metachanged" />
|
||||
<action android:name="com.real.IMP.metachanged" />
|
||||
<action android:name="com.sonyericsson.music.metachanged" />
|
||||
<action android:name="com.rdio.android.metachanged" />
|
||||
<action android:name="com.samsung.sec.android.MusicPlayer.metachanged" />
|
||||
<action android:name="com.andrew.apollo.metachanged" />
|
||||
<action android:name="com.spotify.music.playbackstatechanged"/>
|
||||
<action android:name="com.spotify.music.metadatachanged"/>
|
||||
<action android:name="com.spotify.music.queuechanged"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".receivers.WidgetClickListenerReceiver"
|
||||
android:enabled="true"
|
||||
@ -140,12 +118,41 @@
|
||||
|
||||
<service android:name=".services.EventListenerJob" android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
|
||||
<service android:name=".services.BatteryListenerJob" android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
|
||||
<service android:name=".receivers.MusicNotificationListener"
|
||||
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.notification.NotificationListenerService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver android:name=".receivers.BatteryLevelReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
|
||||
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
|
||||
<action android:name="android.intent.action.BATTERY_LOW"/>
|
||||
<action android:name="android.intent.action.BATTERY_OKAY"/>
|
||||
<action android:name="android.intent.action.BATTERY_CHANGED"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".receivers.ActivityDetectionReceiver"
|
||||
android:exported="false"
|
||||
android:permission="com.google.android.gms.permission.ACTIVITY_RECOGNITION">
|
||||
<intent-filter>
|
||||
<action android:name="com.mypackage.ACTION_PROCESS_ACTIVITY_TRANSITIONS" />
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name=".services.UpdateCalendarJob"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:exported="true"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
BIN
app/src/main/assets/fonts/google_sans_black.ttf
Normal file
BIN
app/src/main/assets/fonts/google_sans_black_italic.ttf
Normal file
BIN
app/src/main/assets/fonts/google_sans_bold.ttf
Normal file
BIN
app/src/main/assets/fonts/google_sans_bold_italic.ttf
Normal file
BIN
app/src/main/assets/fonts/google_sans_italic.ttf
Normal file
BIN
app/src/main/assets/fonts/google_sans_light.ttf
Normal file
BIN
app/src/main/assets/fonts/google_sans_light_italic.ttf
Normal file
BIN
app/src/main/assets/fonts/google_sans_medium.ttf
Normal file
BIN
app/src/main/assets/fonts/google_sans_medium_italic.ttf
Normal file
BIN
app/src/main/assets/fonts/google_sans_regular.ttf
Normal file
BIN
app/src/main/assets/fonts/google_sans_thin.ttf
Normal file
BIN
app/src/main/assets/fonts/google_sans_thin_italic.ttf
Normal file
@ -1,6 +1,7 @@
|
||||
package com.tommasoberlose.anotherwidget
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import com.chibatching.kotpref.Kotpref
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
|
@ -2,6 +2,7 @@ package com.tommasoberlose.anotherwidget.components
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
@ -9,6 +10,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import kotlinx.android.synthetic.main.bottom_sheet_menu.view.*
|
||||
import kotlinx.android.synthetic.main.bottom_sheet_menu_item.view.*
|
||||
import org.w3c.dom.Text
|
||||
|
||||
/**
|
||||
* [BottomSheetDialogFragment] that uses a custom
|
||||
@ -32,8 +34,8 @@ open class BottomSheetMenu<T>(context: Context, private val header: String? = nu
|
||||
return this
|
||||
}
|
||||
|
||||
fun addItem(title: String, value: T? = null): BottomSheetMenu<T> {
|
||||
items.add(MenuItem(title, value))
|
||||
fun addItem(title: String, value: T? = null, renderCallback: ((view: TextView) -> Unit)? = null): BottomSheetMenu<T> {
|
||||
items.add(MenuItem(title, value, renderCallback))
|
||||
return this
|
||||
}
|
||||
|
||||
@ -94,7 +96,10 @@ open class BottomSheetMenu<T>(context: Context, private val header: String? = nu
|
||||
) else ContextCompat.getColor(context, R.color.colorSecondaryText)
|
||||
)
|
||||
}
|
||||
|
||||
item.renderCallback?.invoke(itemView.label)
|
||||
}
|
||||
|
||||
view.menu.addView(itemView)
|
||||
} else {
|
||||
val itemView = View.inflate(context, R.layout.bottom_sheet_menu_divider, null)
|
||||
@ -106,6 +111,6 @@ open class BottomSheetMenu<T>(context: Context, private val header: String? = nu
|
||||
super.show()
|
||||
}
|
||||
|
||||
class MenuItem<T>(val title: String, val value: T? = null)
|
||||
class MenuItem<T>(val title: String, val value: T? = null, val renderCallback: ((view: TextView) -> Unit)? = null)
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.tommasoberlose.anotherwidget.components
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.global.Constants
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
||||
import com.tommasoberlose.anotherwidget.utils.openURI
|
||||
import kotlinx.android.synthetic.main.weather_provider_settings_layout.view.*
|
||||
|
||||
class BottomSheetWeatherProviderSettings(context: Context, callback: () -> Unit) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
|
||||
|
||||
init {
|
||||
val view = View.inflate(context, R.layout.weather_provider_settings_layout, null)
|
||||
view.api_key_container.isVisible = WeatherHelper.isKeyRequired()
|
||||
view.action_save_key.isVisible = WeatherHelper.isKeyRequired()
|
||||
|
||||
WeatherHelper.getProviderInfoTitle(context).let { title ->
|
||||
view.info_title.text = title
|
||||
view.info_title.isVisible = title != ""
|
||||
}
|
||||
|
||||
WeatherHelper.getProviderInfoSubtitle(context).let { subtitle ->
|
||||
view.info_subtitle.text = subtitle
|
||||
view.info_subtitle.isVisible = subtitle != ""
|
||||
}
|
||||
|
||||
view.info_provider.text = WeatherHelper.getProviderName(context)
|
||||
|
||||
view.api_key.editText?.setText(when (Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) {
|
||||
Constants.WeatherProvider.OPEN_WEATHER -> Preferences.weatherProviderApiOpen
|
||||
Constants.WeatherProvider.WEATHER_BIT -> Preferences.weatherProviderApiWeatherBit
|
||||
Constants.WeatherProvider.WEATHER_API -> Preferences.weatherProviderApiWeatherApi
|
||||
Constants.WeatherProvider.HERE -> Preferences.weatherProviderApiHere
|
||||
Constants.WeatherProvider.ACCUWEATHER -> Preferences.weatherProviderApiAccuweather
|
||||
Constants.WeatherProvider.WEATHER_GOV,
|
||||
Constants.WeatherProvider.YR,
|
||||
null -> ""
|
||||
})
|
||||
|
||||
view.action_open_provider.setOnClickListener {
|
||||
context.openURI(WeatherHelper.getProviderLink())
|
||||
}
|
||||
|
||||
view.action_save_key.setOnClickListener {
|
||||
val key = view.api_key.editText?.text.toString()
|
||||
when (Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) {
|
||||
Constants.WeatherProvider.OPEN_WEATHER -> Preferences.weatherProviderApiOpen = key
|
||||
Constants.WeatherProvider.WEATHER_BIT -> Preferences.weatherProviderApiWeatherBit = key
|
||||
Constants.WeatherProvider.WEATHER_API -> Preferences.weatherProviderApiWeatherApi = key
|
||||
Constants.WeatherProvider.HERE -> Preferences.weatherProviderApiHere = key
|
||||
Constants.WeatherProvider.ACCUWEATHER -> Preferences.weatherProviderApiAccuweather = key
|
||||
else -> {}
|
||||
}
|
||||
callback.invoke()
|
||||
dismiss()
|
||||
}
|
||||
|
||||
setContentView(view)
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.tommasoberlose.anotherwidget.components
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import kotlinx.android.synthetic.main.custom_notes_dialog_layout.view.*
|
||||
|
||||
class CustomNotesDialog(context: Context) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
|
||||
|
||||
init {
|
||||
val view = View.inflate(context, R.layout.custom_notes_dialog_layout, null)
|
||||
view.notes.setText(Preferences.customNotes)
|
||||
|
||||
view.action_positive.setOnClickListener {
|
||||
Preferences.customNotes = view.notes.text.toString()
|
||||
this.dismiss()
|
||||
}
|
||||
|
||||
view.notes.requestFocus()
|
||||
|
||||
setContentView(view)
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.tommasoberlose.anotherwidget.components
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.ScrollView
|
||||
|
||||
|
||||
class FixedFocusScrollView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyle: Int = 0
|
||||
) : ScrollView(context, attrs, defStyle) {
|
||||
|
||||
var isScrollable = true
|
||||
|
||||
override fun scrollTo(x: Int, y: Int) {
|
||||
if (isScrollable) {
|
||||
super.scrollTo(x, y)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package com.tommasoberlose.anotherwidget.components
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
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.global.Constants
|
||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark
|
||||
import com.tommasoberlose.anotherwidget.helpers.GlanceProviderHelper
|
||||
import com.tommasoberlose.anotherwidget.models.GlanceProvider
|
||||
import kotlinx.android.synthetic.main.glance_provider_sort_bottom_menu.view.*
|
||||
import kotlinx.coroutines.*
|
||||
import net.idik.lib.slimadapter.SlimAdapter
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class GlanceProviderSortMenu(
|
||||
context: Context
|
||||
) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
|
||||
|
||||
private lateinit var adapter: SlimAdapter
|
||||
|
||||
override fun show() {
|
||||
val view = View.inflate(context, R.layout.glance_provider_sort_bottom_menu, null)
|
||||
|
||||
// Header
|
||||
view.header_text.text = context.getString(R.string.settings_sort_glance_providers_title)
|
||||
|
||||
// List
|
||||
adapter = SlimAdapter.create()
|
||||
|
||||
view.menu.setHasFixedSize(true)
|
||||
val mLayoutManager = LinearLayoutManager(context)
|
||||
view.menu.layoutManager = mLayoutManager
|
||||
|
||||
adapter = SlimAdapter.create()
|
||||
adapter
|
||||
.register<GlanceProvider>(R.layout.glance_provider_item) { item, injector ->
|
||||
injector
|
||||
.text(R.id.title, item.title)
|
||||
.with<ImageView>(R.id.icon) {
|
||||
it.setImageDrawable(ContextCompat.getDrawable(context, item.icon))
|
||||
}
|
||||
}
|
||||
.attachTo(view.menu)
|
||||
|
||||
val mIth = ItemTouchHelper(
|
||||
object : ItemTouchHelper.SimpleCallback(
|
||||
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
|
||||
ItemTouchHelper.LEFT
|
||||
) {
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder
|
||||
): Boolean {
|
||||
val fromPos = viewHolder.adapterPosition
|
||||
val toPos = target.adapterPosition
|
||||
// move item in `fromPos` to `toPos` in adapter.
|
||||
adapter.notifyItemMoved(fromPos, toPos)
|
||||
|
||||
val list = GlanceProviderHelper.getGlanceProviders(context)
|
||||
Collections.swap(list, fromPos, toPos)
|
||||
GlanceProviderHelper.saveGlanceProviderOrder(list)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSwiped(
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
direction: Int
|
||||
) {
|
||||
// remove from adapter
|
||||
}
|
||||
})
|
||||
|
||||
mIth.attachToRecyclerView(view.menu)
|
||||
|
||||
adapter.updateData(
|
||||
GlanceProviderHelper.getGlanceProviders(context)
|
||||
.mapNotNull { GlanceProviderHelper.getGlanceProviderById(context, it) }
|
||||
)
|
||||
|
||||
setContentView(view)
|
||||
super.show()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,57 @@
|
||||
package com.tommasoberlose.anotherwidget.components
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.global.Constants
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
||||
import kotlinx.android.synthetic.main.bottom_sheet_menu.view.*
|
||||
import kotlinx.android.synthetic.main.bottom_sheet_menu.view.header
|
||||
import kotlinx.android.synthetic.main.fragment_weather_settings.*
|
||||
import kotlinx.android.synthetic.main.icon_pack_menu_item.view.*
|
||||
|
||||
class IconPackSelector(context: Context, private val header: String? = null) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
|
||||
|
||||
override fun show() {
|
||||
val view = View.inflate(context, R.layout.bottom_sheet_menu, null)
|
||||
|
||||
// Header
|
||||
view.header.isVisible = header != null
|
||||
view.header_text.text = header ?: ""
|
||||
|
||||
view.warning_text.isVisible = false
|
||||
|
||||
// Menu
|
||||
for (item in Constants.WeatherIconPack.values()) {
|
||||
val itemView = View.inflate(context, R.layout.icon_pack_menu_item, null)
|
||||
itemView.label.text = context.getString(R.string.settings_weather_icon_pack_default).format(item.value + 1)
|
||||
itemView.isSelected = item.value == Preferences.weatherIconPack
|
||||
|
||||
itemView.icon_1.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "01d", item.value)))
|
||||
itemView.icon_2.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "01n", item.value)))
|
||||
itemView.icon_3.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "10d", item.value)))
|
||||
itemView.icon_4.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "09n", item.value)))
|
||||
|
||||
listOf<ImageView>(itemView.icon_1, itemView.icon_2, itemView.icon_3, itemView.icon_4).forEach {
|
||||
if (item == Constants.WeatherIconPack.MINIMAL) {
|
||||
it.setColorFilter(ContextCompat.getColor(context, R.color.colorPrimaryText))
|
||||
} else {
|
||||
it.setColorFilter(ContextCompat.getColor(context, android.R.color.transparent))
|
||||
}
|
||||
}
|
||||
|
||||
itemView.setOnClickListener {
|
||||
Preferences.weatherIconPack = item.value
|
||||
this.dismiss()
|
||||
}
|
||||
view.menu.addView(itemView)
|
||||
}
|
||||
setContentView(view)
|
||||
super.show()
|
||||
}
|
||||
}
|
@ -11,8 +11,8 @@ typealias DialogCallback = () -> Unit
|
||||
|
||||
class MaterialBottomSheetDialog(
|
||||
context: Context,
|
||||
private val title: String? = "",
|
||||
private val message: String? = ""
|
||||
private val title: String? = null,
|
||||
private val message: String? = null
|
||||
) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
|
||||
|
||||
private var positiveButtonLabel: String? = null
|
||||
@ -36,7 +36,7 @@ class MaterialBottomSheetDialog(
|
||||
val view = View.inflate(context, R.layout.bottom_sheet_dialog, null)
|
||||
|
||||
// Header
|
||||
view.message.isVisible = title != null
|
||||
view.title.isVisible = title != null
|
||||
view.title.text = title ?: ""
|
||||
|
||||
view.message.isVisible = message != null
|
||||
|
@ -0,0 +1,21 @@
|
||||
package com.tommasoberlose.anotherwidget.components
|
||||
|
||||
import android.view.View
|
||||
import android.widget.CompoundButton
|
||||
|
||||
class MenuItem (
|
||||
val icon: Int,
|
||||
val getIcon: (() -> Int)? = null,
|
||||
val title: String,
|
||||
val label: String = "",
|
||||
val getLabel: (() -> String)? = null,
|
||||
val isEnabled: (() -> Boolean) = fun (): Boolean { return true },
|
||||
val onClick: View.OnClickListener? = null,
|
||||
val onLongClick: View.OnLongClickListener? = null,
|
||||
val showToggle: Boolean = false,
|
||||
val toggleValue: (() -> Boolean) = fun (): Boolean { return false },
|
||||
val onToggle: CompoundButton.OnCheckedChangeListener? = null,
|
||||
val showPermission: (() -> Boolean) = fun (): Boolean { return false },
|
||||
val onPermissionClickListener: View.OnClickListener? = null,
|
||||
val render: ((view: View) -> Unit)? = null
|
||||
)
|
@ -1,32 +1,37 @@
|
||||
package com.tommasoberlose.anotherwidget.db
|
||||
|
||||
import android.content.Context
|
||||
import android.provider.CalendarContract
|
||||
import android.util.Log
|
||||
import com.chibatching.kotpref.bulk
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.applyFilters
|
||||
import com.tommasoberlose.anotherwidget.models.Event
|
||||
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmResults
|
||||
import java.util.*
|
||||
import kotlin.Comparator
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class EventRepository(val context: Context) {
|
||||
private val realm by lazy { Realm.getDefaultInstance() }
|
||||
|
||||
fun saveEvents(eventList: ArrayList<Event>) {
|
||||
realm.executeTransactionAsync { realm ->
|
||||
realm.executeTransaction { realm ->
|
||||
realm.where(Event::class.java).findAll().deleteAllFromRealm()
|
||||
realm.copyToRealm(eventList)
|
||||
}
|
||||
}
|
||||
|
||||
fun resetNextEventData() {
|
||||
realm.executeTransactionAsync {
|
||||
it.where(Event::class.java).findAll().deleteAllFromRealm()
|
||||
fun clearEvents() {
|
||||
realm.executeTransaction { realm ->
|
||||
realm.where(Event::class.java).findAll().deleteAllFromRealm()
|
||||
}
|
||||
}
|
||||
|
||||
fun resetNextEventData() {
|
||||
Preferences.bulk {
|
||||
remove(Preferences::nextEventId)
|
||||
remove(Preferences::nextEventName)
|
||||
@ -39,36 +44,63 @@ class EventRepository(val context: Context) {
|
||||
}
|
||||
|
||||
fun saveNextEventData(event: Event) {
|
||||
Preferences.nextEventId = event.id
|
||||
Preferences.nextEventId = event.eventID
|
||||
}
|
||||
|
||||
fun getNextEvent(): Event? {
|
||||
val nextEvent = getEventByEventId(Preferences.nextEventId)
|
||||
return if (nextEvent != null && nextEvent.endDate > Calendar.getInstance().timeInMillis) {
|
||||
val now = Calendar.getInstance().timeInMillis
|
||||
val limit = Calendar.getInstance().apply {
|
||||
timeInMillis = now
|
||||
when (Preferences.showUntil) {
|
||||
0 -> add(Calendar.HOUR, 3)
|
||||
1 -> add(Calendar.HOUR, 6)
|
||||
2 -> add(Calendar.HOUR, 12)
|
||||
3 -> add(Calendar.DAY_OF_MONTH, 1)
|
||||
4 -> add(Calendar.DAY_OF_MONTH, 3)
|
||||
5 -> add(Calendar.DAY_OF_MONTH, 7)
|
||||
6 -> add(Calendar.MINUTE, 30)
|
||||
7 -> add(Calendar.HOUR, 1)
|
||||
else -> add(Calendar.HOUR, 6)
|
||||
}
|
||||
}
|
||||
val event = if (nextEvent != null && nextEvent.endDate > now && nextEvent.startDate < limit.timeInMillis) {
|
||||
nextEvent
|
||||
} else {
|
||||
val events = getEvents()
|
||||
if (events.isNotEmpty()) {
|
||||
val newNextEvent = events.first()
|
||||
Preferences.nextEventId = newNextEvent!!.eventID
|
||||
Preferences.nextEventId = newNextEvent.eventID
|
||||
newNextEvent
|
||||
} else {
|
||||
resetNextEventData()
|
||||
null
|
||||
}
|
||||
}
|
||||
return try {
|
||||
realm.copyFromRealm(event!!)
|
||||
} catch (ex: Exception) {
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
fun getEventByEventId(id: Long): Event? = realm.where(Event::class.java).equalTo("eventID", id).findFirst()
|
||||
fun getEventByEventId(id: Long): Event? {
|
||||
val event = realm.where(Event::class.java).equalTo("eventID", id).findFirst()
|
||||
return try {
|
||||
realm.copyFromRealm(event!!)
|
||||
} catch (ex: Exception) {
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
fun goToNextEvent() {
|
||||
val eventList = getEvents()
|
||||
if (eventList.isNotEmpty()) {
|
||||
val index = eventList.indexOfFirst { it.id == Preferences.nextEventId }
|
||||
val index = eventList.indexOfFirst { it.eventID == Preferences.nextEventId }
|
||||
if (index > -1 && index < eventList.size - 1) {
|
||||
Preferences.nextEventId = eventList[index + 1]!!.id
|
||||
Preferences.nextEventId = eventList[index + 1].eventID
|
||||
} else {
|
||||
Preferences.nextEventId = eventList.first()!!.id
|
||||
Preferences.nextEventId = eventList.first().eventID
|
||||
}
|
||||
} else {
|
||||
resetNextEventData()
|
||||
@ -80,11 +112,11 @@ class EventRepository(val context: Context) {
|
||||
fun goToPreviousEvent() {
|
||||
val eventList = getEvents()
|
||||
if (eventList.isNotEmpty()) {
|
||||
val index = eventList.indexOfFirst { it.id == Preferences.nextEventId }
|
||||
val index = eventList.indexOfFirst { it.eventID == Preferences.nextEventId }
|
||||
if (index > 0) {
|
||||
Preferences.nextEventId = eventList[index - 1]!!.id
|
||||
Preferences.nextEventId = eventList[index - 1].eventID
|
||||
} else {
|
||||
Preferences.nextEventId = eventList.last()!!.id
|
||||
Preferences.nextEventId = eventList.last().eventID
|
||||
}
|
||||
} else {
|
||||
resetNextEventData()
|
||||
@ -93,14 +125,44 @@ class EventRepository(val context: Context) {
|
||||
MainWidget.updateWidget(context)
|
||||
}
|
||||
|
||||
fun getEvents(): RealmResults<Event> {
|
||||
fun getFutureEvents(): List<Event> {
|
||||
val now = Calendar.getInstance().timeInMillis
|
||||
val list = realm.where(Event::class.java).greaterThan("endDate", now).findAll()
|
||||
realm.executeTransactionAsync {
|
||||
it.where(Event::class.java).lessThanOrEqualTo("endDate", now).findAll().deleteAllFromRealm()
|
||||
realm.refresh()
|
||||
return realm
|
||||
.where(Event::class.java)
|
||||
.greaterThan("endDate", now)
|
||||
.findAll()
|
||||
.applyFilters()
|
||||
}
|
||||
|
||||
private fun getEvents(): List<Event> {
|
||||
val now = Calendar.getInstance().timeInMillis
|
||||
val limit = Calendar.getInstance().apply {
|
||||
timeInMillis = now
|
||||
when (Preferences.showUntil) {
|
||||
0 -> add(Calendar.HOUR, 3)
|
||||
1 -> add(Calendar.HOUR, 6)
|
||||
2 -> add(Calendar.HOUR, 12)
|
||||
3 -> add(Calendar.DAY_OF_MONTH, 1)
|
||||
4 -> add(Calendar.DAY_OF_MONTH, 3)
|
||||
5 -> add(Calendar.DAY_OF_MONTH, 7)
|
||||
6 -> add(Calendar.MINUTE, 30)
|
||||
7 -> add(Calendar.HOUR, 1)
|
||||
else -> add(Calendar.HOUR, 6)
|
||||
}
|
||||
}
|
||||
return list
|
||||
realm.refresh()
|
||||
return realm
|
||||
.where(Event::class.java)
|
||||
.greaterThan("endDate", now)
|
||||
.lessThanOrEqualTo("startDate", limit.timeInMillis)
|
||||
.findAll()
|
||||
.applyFilters()
|
||||
}
|
||||
|
||||
fun getEventsCount(): Int = getEvents().size
|
||||
|
||||
fun close() {
|
||||
realm.close()
|
||||
}
|
||||
}
|
@ -7,7 +7,10 @@ object Constants {
|
||||
const val RESULT_APP_NAME = "RESULT_APP_NAME"
|
||||
const val RESULT_APP_PACKAGE = "RESULT_APP_PACKAGE"
|
||||
|
||||
const val CUSTOM_FONT_PRODUCT_SANS = 1
|
||||
const val CUSTOM_FONT_GOOGLE_SANS = 1
|
||||
const val CUSTOM_FONT_DOWNLOADED = 2
|
||||
const val CUSTOM_FONT_DOWNLOAD_NEW = 3
|
||||
|
||||
enum class ClockBottomMargin(val value: Int) {
|
||||
NONE(0),
|
||||
SMALL(1),
|
||||
@ -15,11 +18,46 @@ object Constants {
|
||||
LARGE(3)
|
||||
}
|
||||
|
||||
enum class SecondRowTopMargin(val value: Int) {
|
||||
NONE(0),
|
||||
SMALL(1),
|
||||
MEDIUM(2),
|
||||
LARGE(3)
|
||||
}
|
||||
|
||||
enum class GlanceProviderId(val id: String) {
|
||||
PLAYING_SONG("PLAYING_SONG"),
|
||||
NEXT_CLOCK_ALARM("NEXT_CLOCK_ALARM"),
|
||||
// BATTERY_LEVEL_LOW("BATTERY_LEVEL_LOW"),
|
||||
// CUSTOM_INFO("CUSTOM_INFO"),
|
||||
// GOOGLE_FIT_STEPS("GOOGLE_FIT_STEPS")
|
||||
BATTERY_LEVEL_LOW("BATTERY_LEVEL_LOW"),
|
||||
CUSTOM_INFO("CUSTOM_INFO"),
|
||||
GOOGLE_FIT_STEPS("GOOGLE_FIT_STEPS")
|
||||
}
|
||||
|
||||
enum class WidgetUpdateFrequency(val value: Int) {
|
||||
LOW(0),
|
||||
DEFAULT(1),
|
||||
HIGH(2)
|
||||
}
|
||||
|
||||
enum class WeatherProvider(val value: Int) {
|
||||
OPEN_WEATHER(0),
|
||||
WEATHER_BIT(1),
|
||||
WEATHER_API(2),
|
||||
HERE(3),
|
||||
ACCUWEATHER(4),
|
||||
WEATHER_GOV(5),
|
||||
YR(6);
|
||||
|
||||
companion object {
|
||||
private val map = WeatherProvider.values().associateBy(WeatherProvider::value)
|
||||
fun fromInt(type: Int) = map[type]
|
||||
}
|
||||
}
|
||||
|
||||
enum class WeatherIconPack(val value: Int) {
|
||||
DEFAULT(0),
|
||||
MINIMAL(1),
|
||||
COOL(2),
|
||||
GOOGLE_NEWS(3)
|
||||
}
|
||||
}
|
@ -30,39 +30,78 @@ object Preferences : KotprefModel() {
|
||||
var customLocationLon by stringPref(key = "PREF_CUSTOM_LOCATION_LON", default = "")
|
||||
var customLocationAdd by stringPref(key = "PREF_CUSTOM_LOCATION_ADD", default = "")
|
||||
var dateFormat by stringPref(default = "")
|
||||
var isDateCapitalize by booleanPref(default = true)
|
||||
var isDateUppercase by booleanPref(default = false)
|
||||
var weatherRefreshPeriod by intPref(key = "PREF_WEATHER_REFRESH_PERIOD", default = 1)
|
||||
var showUntil by intPref(key = "PREF_SHOW_UNTIL", default = 1)
|
||||
var calendarAppName by stringPref(key = "PREF_CALENDAR_APP_NAME", default = "")
|
||||
var calendarAppPackage by stringPref(key = "PREF_CALENDAR_APP_PACKAGE", default = "")
|
||||
var weatherAppName by stringPref(key = "PREF_WEATHER_APP_NAME", default = "")
|
||||
var weatherAppPackage by stringPref(key = "PREF_WEATHER_APP_PACKAGE", default = "")
|
||||
var weatherProviderApi by stringPref(key = "PREF_WEATHER_PROVIDER_API_KEY", default = "")
|
||||
var weatherProviderApiOpen by stringPref(key = "PREF_WEATHER_PROVIDER_API_KEY", default = "")
|
||||
var weatherProviderApiHere by stringPref(default = "")
|
||||
var weatherProviderApiWeatherApi by stringPref(default = "")
|
||||
var weatherProviderApiWeatherBit by stringPref(default = "")
|
||||
var weatherProviderApiAccuweather by stringPref(default = "")
|
||||
var weatherProvider by intPref(default = Constants.WeatherProvider.OPEN_WEATHER.value)
|
||||
var weatherProviderError by stringPref(default = "")
|
||||
var weatherProviderLocationError by stringPref(default = "")
|
||||
var eventAppName by stringPref(key = "PREF_EVENT_APP_NAME", default = "")
|
||||
var eventAppPackage by stringPref(key = "PREF_EVENT_APP_PACKAGE", default = "")
|
||||
var openEventDetails by booleanPref(default = true)
|
||||
|
||||
var widgetUpdateFrequency by intPref(default = Constants.WidgetUpdateFrequency.DEFAULT.value)
|
||||
|
||||
var textGlobalColor by stringPref(key = "PREF_TEXT_COLOR", default = "#FFFFFF")
|
||||
var textGlobalAlpha by stringPref(default = "FF")
|
||||
|
||||
var textSecondaryColor by stringPref(default = "#FFFFFF")
|
||||
var textSecondaryAlpha by stringPref(default = "FF")
|
||||
|
||||
var backgroundCardColor by stringPref(default = "#000000")
|
||||
var backgroundCardAlpha by stringPref(default = "00")
|
||||
|
||||
var clockTextColor by stringPref(default = "#FFFFFF")
|
||||
var clockTextAlpha by stringPref(default = "FF")
|
||||
|
||||
var textGlobalColorDark by stringPref(default = "#FFFFFF")
|
||||
var textGlobalAlphaDark by stringPref(default = "FF")
|
||||
|
||||
var textSecondaryColorDark by stringPref(default = "#FFFFFF")
|
||||
var textSecondaryAlphaDark by stringPref(default = "FF")
|
||||
|
||||
var backgroundCardColorDark by stringPref(default = "#000000")
|
||||
var backgroundCardAlphaDark by stringPref(default = "00")
|
||||
|
||||
var clockTextColorDark by stringPref(default = "#FFFFFF")
|
||||
var clockTextAlphaDark by stringPref(default = "FF")
|
||||
|
||||
|
||||
var showAMPMIndicator by booleanPref(default = true)
|
||||
|
||||
var weatherIconPack by intPref(default = Constants.WeatherIconPack.DEFAULT.value)
|
||||
|
||||
// Global
|
||||
var textMainSize by floatPref(key = "PREF_TEXT_MAIN_SIZE", default = 26f)
|
||||
var textSecondSize by floatPref(key = "PREF_TEXT_SECOND_SIZE", default = 18f)
|
||||
var clockTextSize by floatPref(key = "PREF_TEXT_CLOCK_SIZE", default = 90f)
|
||||
var clockBottomMargin by intPref(default = Constants.ClockBottomMargin.MEDIUM.value)
|
||||
var secondRowTopMargin by intPref(default = Constants.SecondRowTopMargin.NONE.value)
|
||||
var showClock by booleanPref(key = "PREF_SHOW_CLOCK", default = false)
|
||||
var clockAppName by stringPref(key = "PREF_CLOCK_APP_NAME", default = "")
|
||||
var clockAppPackage by stringPref(key = "PREF_CLOCK_APP_PACKAGE", default = "")
|
||||
var textShadow by intPref(key = "PREF_TEXT_SHADOW", default = 1)
|
||||
var textShadowDark by intPref(default = 1)
|
||||
var showDiffTime by booleanPref(key = "PREF_SHOW_DIFF_TIME", default = true)
|
||||
var showDeclinedEvents by booleanPref(key = "PREF_SHOW_DECLINED_EVENTS", default = false)
|
||||
var showInvitedEvents by booleanPref(default = false)
|
||||
var showAcceptedEvents by booleanPref(default = true)
|
||||
var showOnlyBusyEvents by booleanPref(default = false)
|
||||
var secondRowInformation by intPref(key = "PREF_SECOND_ROW_INFORMATION", default = 0)
|
||||
var customFont by intPref(key = "PREF_CUSTOM_FONT", default = Constants.CUSTOM_FONT_PRODUCT_SANS)
|
||||
var customFontFile by stringPref(key = "PREF_CUSTOM_FONT_FILE")
|
||||
var customFont by intPref(key = "PREF_CUSTOM_FONT", default = Constants.CUSTOM_FONT_GOOGLE_SANS)
|
||||
var customFontFile by stringPref(default = "")
|
||||
var customFontName by stringPref(default = "")
|
||||
var customFontVariant by stringPref(default = "regular")
|
||||
var showNextEvent by booleanPref(key = "PREF_SHOW_NEXT_EVENT", default = true)
|
||||
|
||||
var showDividers by booleanPref(default = true)
|
||||
@ -77,10 +116,13 @@ object Preferences : KotprefModel() {
|
||||
// Glance
|
||||
var showGlance by booleanPref(default = true)
|
||||
var enabledGlanceProviderOrder by stringPref(default = "")
|
||||
var customInfo by stringPref(default = "")
|
||||
var showNextAlarm by booleanPref(default = false)
|
||||
var customNotes by stringPref(default = "")
|
||||
var showNextAlarm by booleanPref(default = true)
|
||||
var showBatteryCharging by booleanPref(default = false)
|
||||
var isBatteryLevelLow by booleanPref(default = false)
|
||||
var isCharging by booleanPref(default = false)
|
||||
var googleFitSteps by longPref(default = -1)
|
||||
var showDailySteps by booleanPref(default = false)
|
||||
|
||||
var showMusic by booleanPref(default = false)
|
||||
var mediaInfoFormat by stringPref(default = "")
|
||||
@ -88,6 +130,7 @@ object Preferences : KotprefModel() {
|
||||
var mediaPlayerAlbum by stringPref(default = "")
|
||||
var mediaPlayerArtist by stringPref(default = "")
|
||||
var mediaPlayerPackage by stringPref(default = "")
|
||||
var musicPlayersFilter by stringPref(default = "")
|
||||
|
||||
// Integrations
|
||||
var installedIntegrations by intPref(default = 0)
|
||||
|
@ -12,8 +12,7 @@ object AlarmHelper {
|
||||
val alarm = nextAlarmClock
|
||||
return if (
|
||||
alarm != null
|
||||
&& alarm.triggerTime - Calendar.getInstance().timeInMillis > 10 * 60 * 1000
|
||||
&& alarm.triggerTime - Calendar.getInstance().timeInMillis < 12 * 60 * 60 * 1000
|
||||
&& alarm.triggerTime - Calendar.getInstance().timeInMillis > 5 * 60 * 1000
|
||||
) {
|
||||
"%s %s".format(
|
||||
SimpleDateFormat("EEE", Locale.getDefault()).format(alarm.triggerTime),
|
||||
|
@ -0,0 +1,23 @@
|
||||
package com.tommasoberlose.anotherwidget.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Context.BATTERY_SERVICE
|
||||
import android.os.BatteryManager
|
||||
import androidx.core.content.ContextCompat.getSystemService
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
|
||||
|
||||
object BatteryHelper {
|
||||
fun updateBatteryInfo(context: Context) {
|
||||
with(context.getSystemService(BATTERY_SERVICE) as BatteryManager) {
|
||||
Preferences.isBatteryLevelLow = getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) <= 15
|
||||
Preferences.isCharging = isCharging
|
||||
}
|
||||
}
|
||||
|
||||
fun getBatteryLevel(context: Context): Int {
|
||||
with(context.getSystemService(BATTERY_SERVICE) as BatteryManager) {
|
||||
return getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
|
||||
}
|
||||
}
|
||||
}
|
@ -104,13 +104,12 @@ object BitmapHelper {
|
||||
}
|
||||
|
||||
fun drawableToBitmap(drawable: Drawable): Bitmap? {
|
||||
var bitmap: Bitmap? = null
|
||||
if (drawable is BitmapDrawable) {
|
||||
if (drawable.bitmap != null) {
|
||||
return drawable.bitmap
|
||||
}
|
||||
}
|
||||
bitmap = if (drawable.intrinsicWidth <= 0 || drawable.intrinsicHeight <= 0) {
|
||||
val bitmap: Bitmap = if (drawable.intrinsicWidth <= 0 || drawable.intrinsicHeight <= 0) {
|
||||
Bitmap.createBitmap(
|
||||
1,
|
||||
1,
|
||||
|
@ -1,14 +1,22 @@
|
||||
package com.tommasoberlose.anotherwidget.helpers
|
||||
|
||||
import android.Manifest
|
||||
import android.app.job.JobInfo
|
||||
import android.app.job.JobParameters
|
||||
import android.app.job.JobScheduler
|
||||
import android.app.job.JobService
|
||||
import android.content.ComponentName
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.provider.CalendarContract
|
||||
import android.util.Log
|
||||
import com.tommasoberlose.anotherwidget.services.EventListenerJob
|
||||
import com.tommasoberlose.anotherwidget.db.EventRepository
|
||||
import com.tommasoberlose.anotherwidget.models.Event
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
|
||||
import com.tommasoberlose.anotherwidget.services.UpdateCalendarJob
|
||||
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
@ -23,110 +31,8 @@ import kotlin.collections.ArrayList
|
||||
*/
|
||||
|
||||
object CalendarHelper {
|
||||
|
||||
fun updateEventList(context: Context) {
|
||||
val eventRepository = EventRepository(context)
|
||||
if (Preferences.showEvents) {
|
||||
val eventList = ArrayList<Event>()
|
||||
|
||||
val now = Calendar.getInstance()
|
||||
val limit = Calendar.getInstance()
|
||||
when (Preferences.showUntil) {
|
||||
0 -> limit.add(Calendar.HOUR, 3)
|
||||
1 -> limit.add(Calendar.HOUR, 6)
|
||||
2 -> limit.add(Calendar.HOUR, 12)
|
||||
3 -> limit.add(Calendar.DAY_OF_MONTH, 1)
|
||||
4 -> limit.add(Calendar.DAY_OF_MONTH, 3)
|
||||
5 -> limit.add(Calendar.DAY_OF_MONTH, 7)
|
||||
6 -> limit.add(Calendar.MINUTE, 30)
|
||||
7 -> limit.add(Calendar.HOUR, 1)
|
||||
else -> limit.add(Calendar.HOUR, 6)
|
||||
}
|
||||
|
||||
val builder = CalendarContract.Instances.CONTENT_URI.buildUpon()
|
||||
ContentUris.appendId(builder, now.timeInMillis)
|
||||
ContentUris.appendId(builder, limit.timeInMillis)
|
||||
|
||||
if (!context.checkGrantedPermission(
|
||||
Manifest.permission.READ_CALENDAR
|
||||
)
|
||||
) {
|
||||
eventRepository.resetNextEventData()
|
||||
} else {
|
||||
try {
|
||||
val provider = CalendarProvider(context)
|
||||
val data = provider.getInstances(now.timeInMillis, limit.timeInMillis)
|
||||
if (data != null) {
|
||||
val instances = data.list
|
||||
for (instance in instances) {
|
||||
try {
|
||||
val e = provider.getEvent(instance.eventId)
|
||||
if (e != null && !e.deleted && instance.begin <= limit.timeInMillis && (Preferences.calendarAllDay || !e.allDay) && !getFilteredCalendarIdList().contains(
|
||||
e.calendarId
|
||||
) && (Preferences.showDeclinedEvents || e.selfAttendeeStatus.toInt() != CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED)
|
||||
) {
|
||||
if (e.allDay) {
|
||||
val start = Calendar.getInstance()
|
||||
start.timeInMillis = instance.begin
|
||||
val end = Calendar.getInstance()
|
||||
end.timeInMillis = instance.end
|
||||
instance.begin =
|
||||
start.timeInMillis - start.timeZone.getOffset(start.timeInMillis)
|
||||
instance.end =
|
||||
end.timeInMillis - end.timeZone.getOffset(end.timeInMillis)
|
||||
}
|
||||
eventList.add(
|
||||
Event(
|
||||
instance.id,
|
||||
e.id,
|
||||
e.title ?: "",
|
||||
instance.begin,
|
||||
instance.end,
|
||||
e.calendarId.toInt(),
|
||||
e.allDay,
|
||||
e.eventLocation ?: ""
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (eventList.isEmpty()) {
|
||||
eventRepository.resetNextEventData()
|
||||
} else {
|
||||
eventList.sortWith(Comparator { event: Event, event1: Event ->
|
||||
if (event.allDay && event1.allDay) {
|
||||
event.startDate.compareTo(event1.startDate)
|
||||
} else if (event.allDay) {
|
||||
1
|
||||
} else if (event1.allDay) {
|
||||
-1
|
||||
} else {
|
||||
event1.startDate.compareTo(event.startDate)
|
||||
}
|
||||
})
|
||||
eventList.reverse()
|
||||
eventRepository.saveEvents(
|
||||
eventList
|
||||
)
|
||||
eventRepository.saveNextEventData(
|
||||
eventList[0]
|
||||
)
|
||||
}
|
||||
} catch (ignored: java.lang.Exception) {
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eventRepository.resetNextEventData()
|
||||
}
|
||||
|
||||
UpdatesReceiver.setUpdates(context)
|
||||
MainWidget.updateWidget(context)
|
||||
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
UpdateCalendarJob.enqueueWork(context, Intent())
|
||||
}
|
||||
|
||||
fun getCalendarList(context: Context): List<me.everything.providers.android.calendar.Calendar> {
|
||||
@ -148,7 +54,8 @@ object CalendarHelper {
|
||||
}
|
||||
|
||||
fun getFilteredCalendarIdList(): List<Long> {
|
||||
return Preferences.calendarFilter.split(",").map { it.replace(" ", "") }.filter { it != "" }.map { it.toLong() }
|
||||
return Preferences.calendarFilter.split(",").map { it.replace(" ", "") }
|
||||
.filter { it != "" }.map { it.toLong() }
|
||||
}
|
||||
|
||||
fun filterCalendar(list: List<Long>) {
|
||||
@ -162,4 +69,15 @@ object CalendarHelper {
|
||||
fun removeEventUpdatesAndroidN(context: Context) {
|
||||
EventListenerJob.remove(context)
|
||||
}
|
||||
|
||||
fun List<Event>.applyFilters() : List<Event> {
|
||||
return this
|
||||
.asSequence()
|
||||
.filter { (Preferences.showDeclinedEvents || it.selfAttendeeStatus != CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED) }
|
||||
.filter { (Preferences.showAcceptedEvents || it.selfAttendeeStatus != CalendarContract.Attendees.ATTENDEE_STATUS_ACCEPTED) }
|
||||
.filter { (Preferences.showInvitedEvents || it.selfAttendeeStatus != CalendarContract.Attendees.ATTENDEE_STATUS_INVITED) }
|
||||
.filter { (Preferences.calendarAllDay || !it.allDay) }
|
||||
.filter { (!Preferences.showOnlyBusyEvents || it.availability != CalendarContract.EventsEntity.AVAILABILITY_FREE) }
|
||||
.toList()
|
||||
}
|
||||
}
|
@ -7,72 +7,97 @@ import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
object ColorHelper {
|
||||
fun getFontColor(): Int {
|
||||
fun getFontColor(isDark: Boolean): Int {
|
||||
return try {
|
||||
Color.parseColor("#%s%s".format(Preferences.textGlobalAlpha, Preferences.textGlobalColor.replace("#", "")))
|
||||
Color.parseColor("#%s%s".format(if (!isDark) Preferences.textGlobalAlpha else Preferences.textGlobalAlphaDark, (if (!isDark) Preferences.textGlobalColor else Preferences.textGlobalColorDark).replace("#", "")))
|
||||
} catch (e: Exception) {
|
||||
Color.parseColor("#FFFFFFFF")
|
||||
}
|
||||
}
|
||||
|
||||
fun getFontColorAlpha(): Int {
|
||||
fun getFontColorAlpha(isDark: Boolean): Int {
|
||||
return try {
|
||||
Preferences.textGlobalAlpha.toIntValue().toDouble() * 255 / 100
|
||||
(if (!isDark) Preferences.textGlobalAlpha else Preferences.textGlobalAlphaDark).toIntValue().toDouble() * 255 / 100
|
||||
} catch (e: Exception) {
|
||||
"FF".toIntValue().toDouble() * 255 / 100
|
||||
}.roundToInt()
|
||||
}
|
||||
|
||||
fun getFontColorRgb(): Int {
|
||||
fun getFontColorRgb(isDark: Boolean): Int {
|
||||
return try {
|
||||
Color.parseColor(Preferences.textGlobalColor)
|
||||
Color.parseColor((if (!isDark) Preferences.textGlobalColor else Preferences.textGlobalColorDark))
|
||||
} catch (e: Exception) {
|
||||
Color.parseColor("#000000")
|
||||
}
|
||||
}
|
||||
fun getClockFontColor(): Int {
|
||||
|
||||
fun getSecondaryFontColor(isDark: Boolean): Int {
|
||||
return try {
|
||||
Color.parseColor("#%s%s".format(Preferences.clockTextAlpha, Preferences.clockTextColor.replace("#", "")))
|
||||
Color.parseColor("#%s%s".format((if (!isDark) Preferences.textSecondaryAlpha else Preferences.textSecondaryAlphaDark), (if (!isDark) Preferences.textSecondaryColor else Preferences.textSecondaryColorDark).replace("#", "")))
|
||||
} catch (e: Exception) {
|
||||
Color.parseColor("#FFFFFFFF")
|
||||
}
|
||||
}
|
||||
|
||||
fun getClockFontColorAlpha(): Int {
|
||||
fun getSecondaryFontColorAlpha(isDark: Boolean): Int {
|
||||
return try {
|
||||
Preferences.clockTextAlpha.toIntValue().toDouble() * 255 / 100
|
||||
(if (!isDark) Preferences.textSecondaryAlpha else Preferences.textSecondaryAlphaDark).toIntValue().toDouble() * 255 / 100
|
||||
} catch (e: Exception) {
|
||||
"FF".toIntValue().toDouble() * 255 / 100
|
||||
}.roundToInt()
|
||||
}
|
||||
|
||||
fun getClockFontColorRgb(): Int {
|
||||
fun getSecondaryFontColorRgb(isDark: Boolean): Int {
|
||||
return try {
|
||||
Color.parseColor(Preferences.clockTextColor)
|
||||
Color.parseColor((if (!isDark) Preferences.textSecondaryColor else Preferences.textSecondaryColorDark))
|
||||
} catch (e: Exception) {
|
||||
Color.parseColor("#000000")
|
||||
}
|
||||
}
|
||||
|
||||
fun getBackgroundColor(): Int {
|
||||
fun getClockFontColor(isDark: Boolean): Int {
|
||||
return try {
|
||||
Color.parseColor("#%s%s".format(Preferences.backgroundCardAlpha, Preferences.backgroundCardColor.replace("#", "")))
|
||||
Color.parseColor("#%s%s".format((if (!isDark) Preferences.clockTextAlpha else Preferences.clockTextAlphaDark), (if (!isDark) Preferences.clockTextColor else Preferences.clockTextColorDark).replace("#", "")))
|
||||
} catch (e: Exception) {
|
||||
Color.parseColor("#FFFFFFFF")
|
||||
}
|
||||
}
|
||||
|
||||
fun getClockFontColorAlpha(isDark: Boolean): Int {
|
||||
return try {
|
||||
(if (!isDark) Preferences.clockTextAlpha else Preferences.clockTextAlphaDark).toIntValue().toDouble() * 255 / 100
|
||||
} catch (e: Exception) {
|
||||
"FF".toIntValue().toDouble() * 255 / 100
|
||||
}.roundToInt()
|
||||
}
|
||||
|
||||
fun getClockFontColorRgb(isDark: Boolean): Int {
|
||||
return try {
|
||||
Color.parseColor((if (!isDark) Preferences.clockTextColor else Preferences.clockTextColorDark))
|
||||
} catch (e: Exception) {
|
||||
Color.parseColor("#000000")
|
||||
}
|
||||
}
|
||||
|
||||
fun getBackgroundColor(isDark: Boolean): Int {
|
||||
return try {
|
||||
Color.parseColor("#%s%s".format((if (!isDark) Preferences.backgroundCardAlpha else Preferences.backgroundCardAlphaDark), (if (!isDark) Preferences.backgroundCardColor else Preferences.backgroundCardColorDark).replace("#", "")))
|
||||
} catch (e: Exception) {
|
||||
Color.parseColor("#00000000")
|
||||
}
|
||||
}
|
||||
|
||||
fun getBackgroundAlpha(): Int {
|
||||
fun getBackgroundAlpha(isDark: Boolean): Int {
|
||||
return try {
|
||||
Preferences.backgroundCardAlpha.toIntValue().toDouble() * 255 / 100
|
||||
(if (!isDark) Preferences.backgroundCardAlpha else Preferences.backgroundCardAlphaDark).toIntValue().toDouble() * 255 / 100
|
||||
} catch (e: Exception) {
|
||||
"00".toIntValue().toDouble() * 255 / 100
|
||||
}.roundToInt()
|
||||
}
|
||||
|
||||
fun getBackgroundColorRgb(): Int {
|
||||
fun getBackgroundColorRgb(isDark: Boolean): Int {
|
||||
return try {
|
||||
Color.parseColor(Preferences.backgroundCardColor)
|
||||
Color.parseColor((if (!isDark) Preferences.backgroundCardColor else Preferences.backgroundCardColorDark))
|
||||
} catch (e: Exception) {
|
||||
Color.parseColor("#000000")
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package com.tommasoberlose.anotherwidget.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.text.format.DateUtils
|
||||
import android.util.Log
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.utils.getCapWordString
|
||||
import java.lang.Exception
|
||||
@ -12,18 +11,28 @@ import java.util.*
|
||||
object DateHelper {
|
||||
fun getDateText(context: Context, date: Calendar): String {
|
||||
return if (Preferences.dateFormat != "") {
|
||||
try {
|
||||
val text = try {
|
||||
SimpleDateFormat(Preferences.dateFormat, Locale.getDefault()).format(date.time)
|
||||
} catch (e: Exception) {
|
||||
getDefaultDateText(context, date)
|
||||
}
|
||||
when {
|
||||
Preferences.isDateUppercase -> text.toUpperCase(Locale.getDefault())
|
||||
Preferences.isDateCapitalize -> text.getCapWordString()
|
||||
else -> text
|
||||
}
|
||||
} else {
|
||||
val flags: Int =
|
||||
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_ABBREV_MONTH
|
||||
"%s, %s".format(
|
||||
val text = "%s, %s".format(
|
||||
SimpleDateFormat("EEEE", Locale.getDefault()).format(date.time),
|
||||
DateUtils.formatDateTime(context, date.timeInMillis, flags)
|
||||
).getCapWordString()
|
||||
)
|
||||
when {
|
||||
Preferences.isDateUppercase -> text.toUpperCase(Locale.getDefault())
|
||||
Preferences.isDateCapitalize -> text.getCapWordString()
|
||||
else -> text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,6 +42,6 @@ object DateHelper {
|
||||
return "%s, %s".format(
|
||||
SimpleDateFormat("EEEE", Locale.getDefault()).format(date.time),
|
||||
DateUtils.formatDateTime(context, date.timeInMillis, flags)
|
||||
).getCapWordString()
|
||||
)
|
||||
}
|
||||
}
|
@ -1,16 +1,23 @@
|
||||
package com.tommasoberlose.anotherwidget.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.db.EventRepository
|
||||
import com.tommasoberlose.anotherwidget.global.Constants
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.models.GlanceProvider
|
||||
import com.tommasoberlose.anotherwidget.utils.checkIfFitInstalled
|
||||
import java.util.ArrayList
|
||||
|
||||
object GlanceProviderHelper {
|
||||
fun getGlanceProviders(): ArrayList<Constants.GlanceProviderId> {
|
||||
fun getGlanceProviders(context: Context): ArrayList<Constants.GlanceProviderId> {
|
||||
val enabledProviders = Preferences.enabledGlanceProviderOrder.split(",").filter { it != "" }
|
||||
|
||||
val providers = Constants.GlanceProviderId.values()
|
||||
.filter {
|
||||
context.checkIfFitInstalled() || it != Constants.GlanceProviderId.GOOGLE_FIT_STEPS
|
||||
}.toTypedArray()
|
||||
|
||||
providers.sortWith(Comparator { p1, p2 ->
|
||||
when {
|
||||
@ -37,40 +44,52 @@ object GlanceProviderHelper {
|
||||
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
|
||||
GlanceProvider(providerId.id,
|
||||
context.getString(R.string.settings_show_next_alarm_title),
|
||||
R.drawable.round_alarm,
|
||||
context.getString(R.string.settings_show_next_alarm_subtitle)
|
||||
R.drawable.round_alarm
|
||||
)
|
||||
}
|
||||
Constants.GlanceProviderId.PLAYING_SONG -> {
|
||||
GlanceProvider(providerId.id,
|
||||
context.getString(R.string.settings_show_music_title),
|
||||
R.drawable.round_music_note,
|
||||
context.getString(R.string.settings_show_music_enabled_subtitle)
|
||||
R.drawable.round_music_note
|
||||
)
|
||||
}
|
||||
Constants.GlanceProviderId.CUSTOM_INFO -> {
|
||||
GlanceProvider(providerId.id,
|
||||
context.getString(R.string.settings_custom_notes_title),
|
||||
R.drawable.round_notes
|
||||
)
|
||||
}
|
||||
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
|
||||
GlanceProvider(providerId.id,
|
||||
context.getString(R.string.settings_low_battery_level_title),
|
||||
R.drawable.round_battery_charging_full
|
||||
)
|
||||
}
|
||||
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
|
||||
GlanceProvider(providerId.id,
|
||||
context.getString(R.string.settings_daily_steps_title),
|
||||
R.drawable.round_directions_walk
|
||||
)
|
||||
}
|
||||
// Constants.GlanceProviderId.CUSTOM_INFO -> {
|
||||
// GlanceProvider(providerId.id,
|
||||
// context.getString(R.string.settings_show_next_alarm_title),
|
||||
// R.drawable.round_event_note
|
||||
// )
|
||||
// }
|
||||
// Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
|
||||
// GlanceProvider(providerId.id,
|
||||
// context.getString(R.string.settings_show_next_alarm_title),
|
||||
// R.drawable.round_battery_charging_full
|
||||
// )
|
||||
// }
|
||||
// Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
|
||||
// GlanceProvider(providerId.id,
|
||||
// context.getString(R.string.settings_show_next_alarm_title),
|
||||
// R.drawable.round_directions_walk
|
||||
// )
|
||||
// }
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun saveGlanceProviderOrder(list: ArrayList<Constants.GlanceProviderId>) {
|
||||
Preferences.enabledGlanceProviderOrder = list.joinToString(separator = ",")
|
||||
}
|
||||
|
||||
fun showGlanceProviders(context: Context): Boolean {
|
||||
val eventRepository = EventRepository(context)
|
||||
BatteryHelper.updateBatteryInfo(context)
|
||||
|
||||
val showGlance = Preferences.showGlance && (eventRepository.getEventsCount() == 0 || !Preferences.showEvents) && (
|
||||
(Preferences.showNextAlarm && AlarmHelper.getNextAlarm(context) != "") ||
|
||||
(MediaPlayerHelper.isSomeonePlaying(context)) ||
|
||||
(Preferences.showBatteryCharging && Preferences.isCharging || Preferences.isBatteryLevelLow) ||
|
||||
(Preferences.customNotes.isNotEmpty()) ||
|
||||
(Preferences.showDailySteps && Preferences.googleFitSteps > 0)
|
||||
)
|
||||
eventRepository.close()
|
||||
return showGlance
|
||||
}
|
||||
}
|
@ -11,9 +11,11 @@ import android.provider.AlarmClock
|
||||
import android.provider.CalendarContract
|
||||
import android.provider.CalendarContract.Events
|
||||
import android.util.Log
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.models.Event
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import com.tommasoberlose.anotherwidget.utils.toast
|
||||
import java.util.*
|
||||
|
||||
|
||||
@ -64,12 +66,8 @@ object IntentHelper {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Intent(Intent.ACTION_VIEW).apply {
|
||||
addCategory(Intent.CATEGORY_DEFAULT)
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
data = Uri.parse("dynact://velour/weather/ProxyActivity")
|
||||
component = ComponentName("com.google.android.googlequicksearchbox", "com.google.android.apps.gsa.velour.DynamicActivityTrampoline")
|
||||
}
|
||||
context.toast(context.getString(R.string.error_opening_app))
|
||||
Intent()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -98,10 +96,8 @@ object IntentHelper {
|
||||
data = calendarUri
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Intent(Intent.ACTION_VIEW).apply {
|
||||
data = calendarUri
|
||||
}
|
||||
context.toast(context.getString(R.string.error_opening_app))
|
||||
Intent()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -114,19 +110,51 @@ object IntentHelper {
|
||||
if (Preferences.calendarAppPackage == "") {
|
||||
Intent(Intent.ACTION_VIEW).apply {
|
||||
data = uri
|
||||
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate)
|
||||
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate)
|
||||
// putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, if (e.allDay) 1 else 0)
|
||||
// type = "vnd.android.cursor.item/event"
|
||||
flags = (Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED or Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
if (!e.allDay) {
|
||||
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate)
|
||||
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate)
|
||||
} else {
|
||||
val start = Calendar.getInstance().apply {
|
||||
timeInMillis = e.startDate
|
||||
}
|
||||
val end = Calendar.getInstance().apply {
|
||||
timeInMillis = e.endDate
|
||||
}
|
||||
|
||||
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate + start.timeZone.getOffset(start.timeInMillis))
|
||||
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate + end.timeZone.getOffset(end.timeInMillis))
|
||||
putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, 1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
getCalendarIntent(context).apply {
|
||||
action = Intent.ACTION_VIEW
|
||||
data = uri
|
||||
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate)
|
||||
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate)
|
||||
// putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, if (e.allDay) 1 else 0)
|
||||
// type = "vnd.android.cursor.item/event"
|
||||
val calendarIntent = getCalendarIntent(context)
|
||||
if (calendarIntent.action == Intent.ACTION_VIEW) {
|
||||
calendarIntent.apply {
|
||||
data = uri
|
||||
if (!e.allDay) {
|
||||
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate)
|
||||
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate)
|
||||
} else {
|
||||
val start = Calendar.getInstance().apply {
|
||||
timeInMillis = e.startDate
|
||||
}
|
||||
val end = Calendar.getInstance().apply {
|
||||
timeInMillis = e.endDate
|
||||
}
|
||||
putExtra(
|
||||
CalendarContract.EXTRA_EVENT_BEGIN_TIME,
|
||||
start.timeInMillis + start.timeZone.getOffset(start.timeInMillis)
|
||||
)
|
||||
putExtra(
|
||||
CalendarContract.EXTRA_EVENT_END_TIME,
|
||||
end.timeInMillis + end.timeZone.getOffset(end.timeInMillis)
|
||||
)
|
||||
putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, 1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Intent()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -153,14 +181,17 @@ object IntentHelper {
|
||||
addCategory(Intent.CATEGORY_LAUNCHER)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Intent(AlarmClock.ACTION_SHOW_ALARMS).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
}
|
||||
context.toast(context.getString(R.string.error_opening_app))
|
||||
Intent()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getBatteryIntent(): Intent {
|
||||
return Intent(Intent.ACTION_POWER_USAGE_SUMMARY)
|
||||
}
|
||||
|
||||
fun getMusicIntent(context: Context): Intent {
|
||||
return when (Preferences.mediaPlayerPackage) {
|
||||
"" -> {
|
||||
@ -173,9 +204,22 @@ object IntentHelper {
|
||||
addCategory(Intent.CATEGORY_LAUNCHER)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
context.toast(context.getString(R.string.error_opening_app))
|
||||
Intent()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getFitIntent(context: Context): Intent {
|
||||
val pm: PackageManager = context.packageManager
|
||||
return try {
|
||||
pm.getLaunchIntentForPackage("com.google.android.apps.fitness")!!.apply {
|
||||
addCategory(Intent.CATEGORY_LAUNCHER)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
context.toast(context.getString(R.string.error_opening_app))
|
||||
Intent()
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,8 @@ import android.media.session.MediaSessionManager
|
||||
import android.media.session.PlaybackState
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import com.chibatching.kotpref.Kotpref
|
||||
import com.chibatching.kotpref.blockingBulk
|
||||
import com.chibatching.kotpref.bulk
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.receivers.MusicNotificationListener
|
||||
@ -28,6 +30,7 @@ object MediaPlayerHelper {
|
||||
}
|
||||
|
||||
fun updatePlayingMediaInfo(context: Context) {
|
||||
Kotpref.init(context)
|
||||
if (NotificationManagerCompat.getEnabledListenerPackages(context).contains(context.packageName)) {
|
||||
val list = try {
|
||||
(context.getSystemService(Context.MEDIA_SESSION_SERVICE) as MediaSessionManager).getActiveSessions(
|
||||
@ -35,6 +38,8 @@ object MediaPlayerHelper {
|
||||
)
|
||||
} catch (ex: Exception) {
|
||||
emptyList<MediaController>()
|
||||
}.filter {
|
||||
Preferences.musicPlayersFilter == "" || isMusicPlayerAccepted(it.packageName)
|
||||
}
|
||||
|
||||
if (list.isNotEmpty()) {
|
||||
@ -65,24 +70,35 @@ object MediaPlayerHelper {
|
||||
}
|
||||
|
||||
if (!isSomeonePlaying) {
|
||||
removeMediaInfo()
|
||||
removeMediaInfo(context)
|
||||
}
|
||||
|
||||
} else {
|
||||
removeMediaInfo()
|
||||
removeMediaInfo(context)
|
||||
}
|
||||
} else {
|
||||
removeMediaInfo()
|
||||
removeMediaInfo(context)
|
||||
}
|
||||
MainWidget.updateWidget(context)
|
||||
}
|
||||
|
||||
private fun removeMediaInfo() {
|
||||
Preferences.bulk {
|
||||
private fun removeMediaInfo(context: Context) {
|
||||
Kotpref.init(context)
|
||||
Preferences.blockingBulk {
|
||||
remove(Preferences::mediaPlayerTitle)
|
||||
remove(Preferences::mediaPlayerArtist)
|
||||
remove(Preferences::mediaPlayerAlbum)
|
||||
remove(Preferences::mediaPlayerPackage)
|
||||
}
|
||||
}
|
||||
|
||||
fun isMusicPlayerAccepted(appPkg: String): Boolean = Preferences.musicPlayersFilter.contains(appPkg)
|
||||
|
||||
fun toggleMusicPlayerFilter(appPkg: String) {
|
||||
if (Preferences.musicPlayersFilter == "" || !Preferences.musicPlayersFilter.contains(appPkg)) {
|
||||
Preferences.musicPlayersFilter = Preferences.musicPlayersFilter.split(",").union(listOf(appPkg)).joinToString(separator = ",")
|
||||
} else {
|
||||
Preferences.musicPlayersFilter = Preferences.musicPlayersFilter.split(",").filter { it != appPkg }.joinToString(separator = ",")
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,8 @@ package com.tommasoberlose.anotherwidget.helpers
|
||||
import android.content.Context
|
||||
import android.text.format.DateUtils
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.global.Constants
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import org.joda.time.DateTime
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@ -52,14 +54,27 @@ object SettingsStringHelper {
|
||||
}
|
||||
}
|
||||
|
||||
fun getCustomFontLabel(shadow: Int): Int {
|
||||
return when (shadow) {
|
||||
0 -> R.string.custom_font_subtitle_0
|
||||
1 -> R.string.custom_font_subtitle_1
|
||||
else -> R.string.custom_font_subtitle_1
|
||||
fun getCustomFontLabel(context: Context, font: Int): String {
|
||||
return when (font) {
|
||||
Constants.CUSTOM_FONT_GOOGLE_SANS -> context.getString(R.string.custom_font_subtitle_1) + " - ${getVariantLabel(context, Preferences.customFontVariant)}"
|
||||
Constants.CUSTOM_FONT_DOWNLOADED -> Preferences.customFontName + " - ${getVariantLabel(context, Preferences.customFontVariant)}"
|
||||
else -> context.getString(R.string.custom_font_subtitle_0)
|
||||
}
|
||||
}
|
||||
|
||||
fun getVariantLabel(context: Context, variant: String): String = when {
|
||||
variant.contains("100") -> context.getString(R.string.font_100)
|
||||
variant.contains("200") -> context.getString(R.string.font_200)
|
||||
variant.contains("300") -> context.getString(R.string.font_300)
|
||||
variant.contains("regular") || variant.contains("400") -> context.getString(R.string.font_400)
|
||||
variant.contains("500") -> context.getString(R.string.font_500)
|
||||
variant.contains("600") -> context.getString(R.string.font_600)
|
||||
variant.contains("700") -> context.getString(R.string.font_700)
|
||||
variant.contains("800") -> context.getString(R.string.font_800)
|
||||
variant.contains("900") -> context.getString(R.string.font_900)
|
||||
else -> context.getString(R.string.font_400)
|
||||
}
|
||||
|
||||
fun getDifferenceText(context: Context, now: Long, start: Long): String {
|
||||
val nowDate = DateTime(now)
|
||||
val eventDate = DateTime(start)
|
||||
@ -68,11 +83,38 @@ object SettingsStringHelper {
|
||||
difference += 60 * 1000 - (difference % (60 * 1000))
|
||||
|
||||
when {
|
||||
difference <= 0 || TimeUnit.MILLISECONDS.toHours(difference) < 1 -> {
|
||||
difference <= 0 -> {
|
||||
return ""
|
||||
}
|
||||
TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.HIGH.value && TimeUnit.MILLISECONDS.toMinutes(difference) > 5 -> {
|
||||
return DateUtils.getRelativeTimeSpanString(start, start - 1000 * 60 * (TimeUnit.MILLISECONDS.toMinutes(difference) - 1 - (TimeUnit.MILLISECONDS.toMinutes(difference) - 1) % 5), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString()
|
||||
}
|
||||
TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.DEFAULT.value && TimeUnit.MILLISECONDS.toMinutes(difference) > 5 -> {
|
||||
return DateUtils.getRelativeTimeSpanString(start, start - 1000 * 60 * (TimeUnit.MILLISECONDS.toMinutes(difference) - 1 - (TimeUnit.MILLISECONDS.toMinutes(difference) - 1) % 15), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString()
|
||||
}
|
||||
TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.LOW.value -> {
|
||||
return context.getString(R.string.soon)
|
||||
}
|
||||
TimeUnit.MILLISECONDS.toHours(difference) < 1 -> {
|
||||
return context.getString(R.string.now)
|
||||
}
|
||||
TimeUnit.MILLISECONDS.toHours(difference) < 12 -> {
|
||||
return DateUtils.getRelativeTimeSpanString(start, now, DateUtils.HOUR_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString()
|
||||
val minutes = TimeUnit.MILLISECONDS.toMinutes(difference) - 60 * TimeUnit.MILLISECONDS.toHours(difference)
|
||||
return if (minutes < 1 || minutes > 30) {
|
||||
DateUtils.getRelativeTimeSpanString(
|
||||
start,
|
||||
now - 1000 * 60 * 40,
|
||||
DateUtils.HOUR_IN_MILLIS,
|
||||
DateUtils.FORMAT_ABBREV_RELATIVE
|
||||
).toString()
|
||||
} else {
|
||||
DateUtils.getRelativeTimeSpanString(
|
||||
start,
|
||||
now,
|
||||
DateUtils.HOUR_IN_MILLIS,
|
||||
DateUtils.FORMAT_ABBREV_RELATIVE
|
||||
).toString()
|
||||
}
|
||||
}
|
||||
eventDate.dayOfYear == nowDate.plusDays(1).dayOfYear -> {
|
||||
return String.format("%s", context.getString(R.string.tomorrow))
|
||||
|
@ -3,14 +3,19 @@ package com.tommasoberlose.anotherwidget.helpers
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import com.google.android.gms.location.LocationServices
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.db.EventRepository
|
||||
import com.tommasoberlose.anotherwidget.global.Constants
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
|
||||
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
|
||||
@ -20,7 +25,7 @@ import org.greenrobot.eventbus.EventBus
|
||||
|
||||
object WeatherHelper {
|
||||
|
||||
fun updateWeather(context: Context) {
|
||||
suspend fun updateWeather(context: Context) {
|
||||
val networkApi = WeatherNetworkApi(context)
|
||||
if (Preferences.customLocationAdd != "") {
|
||||
networkApi.updateWeather()
|
||||
@ -31,10 +36,17 @@ object WeatherHelper {
|
||||
if (location != null) {
|
||||
Preferences.customLocationLat = location.latitude.toString()
|
||||
Preferences.customLocationLon = location.longitude.toString()
|
||||
|
||||
networkApi.updateWeather()
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
networkApi.updateWeather()
|
||||
}
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
} else {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
networkApi.updateWeather()
|
||||
}
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -43,89 +55,455 @@ object WeatherHelper {
|
||||
fun removeWeather(context: Context) {
|
||||
Preferences.remove(Preferences::weatherTemp)
|
||||
Preferences.remove(Preferences::weatherRealTempUnit)
|
||||
Preferences.remove(Preferences::weatherIcon)
|
||||
MainWidget.updateWidget(context)
|
||||
}
|
||||
|
||||
fun getWeatherIconResource(icon: String): Int {
|
||||
when (icon) {
|
||||
fun getProviderName(context: Context, provider: Constants.WeatherProvider = Constants.WeatherProvider.fromInt(Preferences.weatherProvider)!!): String {
|
||||
return context.getString(when(provider) {
|
||||
Constants.WeatherProvider.OPEN_WEATHER -> R.string.settings_weather_provider_open_weather
|
||||
Constants.WeatherProvider.WEATHER_BIT -> R.string.settings_weather_provider_weatherbit
|
||||
Constants.WeatherProvider.WEATHER_API -> R.string.settings_weather_provider_weather_api
|
||||
Constants.WeatherProvider.HERE -> R.string.settings_weather_provider_here
|
||||
Constants.WeatherProvider.ACCUWEATHER -> R.string.settings_weather_provider_accuweather
|
||||
Constants.WeatherProvider.WEATHER_GOV -> R.string.settings_weather_provider_weather_gov
|
||||
Constants.WeatherProvider.YR -> R.string.settings_weather_provider_yr
|
||||
})
|
||||
}
|
||||
|
||||
fun getProviderInfoTitle(context: Context, provider: Constants.WeatherProvider? = Constants.WeatherProvider.fromInt(Preferences.weatherProvider)!!): String {
|
||||
return context.getString(when(provider) {
|
||||
Constants.WeatherProvider.OPEN_WEATHER -> R.string.weather_provider_info_open_weather_title
|
||||
Constants.WeatherProvider.WEATHER_BIT -> R.string.weather_provider_info_weatherbit_title
|
||||
Constants.WeatherProvider.WEATHER_API -> R.string.weather_provider_info_weatherapi_title
|
||||
Constants.WeatherProvider.HERE -> R.string.weather_provider_info_here_title
|
||||
Constants.WeatherProvider.ACCUWEATHER -> R.string.weather_provider_info_accuweather_title
|
||||
Constants.WeatherProvider.WEATHER_GOV -> R.string.weather_provider_info_weather_gov_title
|
||||
Constants.WeatherProvider.YR -> R.string.weather_provider_info_yr_title
|
||||
else -> R.string.nothing
|
||||
})
|
||||
}
|
||||
|
||||
fun getProviderInfoSubtitle(context: Context, provider: Constants.WeatherProvider? = Constants.WeatherProvider.fromInt(Preferences.weatherProvider)!!): String {
|
||||
return context.getString(when(provider) {
|
||||
Constants.WeatherProvider.OPEN_WEATHER -> R.string.weather_provider_info_open_weather_subtitle
|
||||
Constants.WeatherProvider.WEATHER_BIT -> R.string.weather_provider_info_weatherbit_subtitle
|
||||
Constants.WeatherProvider.WEATHER_API -> R.string.weather_provider_info_weatherapi_subtitle
|
||||
Constants.WeatherProvider.HERE -> R.string.weather_provider_info_here_subtitle
|
||||
Constants.WeatherProvider.ACCUWEATHER -> R.string.weather_provider_info_accuweather_subtitle
|
||||
Constants.WeatherProvider.WEATHER_GOV -> R.string.weather_provider_info_weather_gov_subtitle
|
||||
Constants.WeatherProvider.YR -> R.string.weather_provider_info_yr_subtitle
|
||||
else -> R.string.nothing
|
||||
})
|
||||
}
|
||||
|
||||
fun getProviderLink(provider: Constants.WeatherProvider? = Constants.WeatherProvider.fromInt(Preferences.weatherProvider)!!): String {
|
||||
return when(provider) {
|
||||
Constants.WeatherProvider.OPEN_WEATHER -> "https://home.openweathermap.org/users/sign_in"
|
||||
Constants.WeatherProvider.WEATHER_BIT -> "https://www.weatherbit.io/account/login"
|
||||
Constants.WeatherProvider.WEATHER_API -> "https://www.weatherapi.com/login.aspx"
|
||||
Constants.WeatherProvider.HERE -> "https://developer.here.com/login"
|
||||
Constants.WeatherProvider.ACCUWEATHER -> "https://developer.accuweather.com/user/login"
|
||||
Constants.WeatherProvider.WEATHER_GOV -> "http://www.weather.gov/"
|
||||
Constants.WeatherProvider.YR -> "https://www.yr.no/"
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
fun isKeyRequired(provider: Constants.WeatherProvider? = Constants.WeatherProvider.fromInt(Preferences.weatherProvider)!!): Boolean = when (provider) {
|
||||
Constants.WeatherProvider.OPEN_WEATHER,
|
||||
Constants.WeatherProvider.WEATHER_BIT,
|
||||
Constants.WeatherProvider.WEATHER_API,
|
||||
Constants.WeatherProvider.HERE,
|
||||
Constants.WeatherProvider.ACCUWEATHER -> true
|
||||
|
||||
Constants.WeatherProvider.WEATHER_GOV,
|
||||
Constants.WeatherProvider.YR -> false
|
||||
else -> true
|
||||
}
|
||||
|
||||
fun getApiKey(provider: Constants.WeatherProvider? = Constants.WeatherProvider.fromInt(Preferences.weatherProvider)!!): String = when (provider) {
|
||||
Constants.WeatherProvider.OPEN_WEATHER -> Preferences.weatherProviderApiOpen
|
||||
Constants.WeatherProvider.WEATHER_BIT -> Preferences.weatherProviderApiWeatherBit
|
||||
Constants.WeatherProvider.WEATHER_API -> Preferences.weatherProviderApiWeatherApi
|
||||
Constants.WeatherProvider.HERE -> Preferences.weatherProviderApiHere
|
||||
Constants.WeatherProvider.ACCUWEATHER -> Preferences.weatherProviderApiAccuweather
|
||||
Constants.WeatherProvider.WEATHER_GOV -> ""
|
||||
Constants.WeatherProvider.YR -> ""
|
||||
else -> ""
|
||||
}
|
||||
|
||||
fun getWeatherIconResource(context: Context, icon: String, style: Int = Preferences.weatherIconPack): Int {
|
||||
return when (icon) {
|
||||
"01d" -> {
|
||||
return R.drawable.clear_day
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.clear_day_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.clear_day_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.clear_day_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.clear_day_5 else R.drawable.clear_day_5_light
|
||||
}
|
||||
}
|
||||
"02d" -> {
|
||||
return R.drawable.partly_cloudy
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.partly_cloudy_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.partly_cloudy_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.partly_cloudy_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.partly_cloudy_5 else R.drawable.partly_cloudy_5_light
|
||||
}
|
||||
}
|
||||
"03d" -> {
|
||||
return R.drawable.mostly_cloudy
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.mostly_cloudy_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.mostly_cloudy_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.mostly_cloudy_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.mostly_cloudy_5 else R.drawable.mostly_cloudy_5_light
|
||||
}
|
||||
}
|
||||
"04d" -> {
|
||||
return R.drawable.cloudy_weather
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.cloudy_weather_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.cloudy_weather_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.cloudy_weather_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.cloudy_weather_5 else R.drawable.cloudy_weather_5_light
|
||||
}
|
||||
}
|
||||
"09d" -> {
|
||||
return R.drawable.storm_weather_day
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.storm_weather_day_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.storm_weather_day_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.storm_weather_day_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.storm_weather_day_5 else R.drawable.storm_weather_day_5_light
|
||||
}
|
||||
}
|
||||
"10d" -> {
|
||||
return R.drawable.rainy_day
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.rainy_day_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.rainy_day_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.rainy_day_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.rainy_day_5 else R.drawable.rainy_day_5_light
|
||||
}
|
||||
}
|
||||
"11d" -> {
|
||||
return R.drawable.thunder_day
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.thunder_day_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.thunder_day_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.thunder_day_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.thunder_day_5 else R.drawable.thunder_day_5_light
|
||||
}
|
||||
}
|
||||
"13d" -> {
|
||||
return R.drawable.snow_day
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.snow_day_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.snow_day_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.snow_day_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.snow_day_5 else R.drawable.snow_day_5_light
|
||||
}
|
||||
}
|
||||
"50d" -> {
|
||||
return R.drawable.haze_day
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.haze_day_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.haze_day_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.haze_day_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.haze_day_5 else R.drawable.haze_day_5_light
|
||||
}
|
||||
}
|
||||
"80d" -> {
|
||||
return R.drawable.windy_day
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.windy_day_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.windy_day_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.windy_day_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.windy_day_5 else R.drawable.windy_day_5_light
|
||||
}
|
||||
}
|
||||
"81d" -> {
|
||||
return R.drawable.rain_snow_day
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.rain_snow_day_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.rain_snow_day_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.rain_snow_day_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.rain_snow_day_5 else R.drawable.rain_snow_day_5_light
|
||||
}
|
||||
}
|
||||
"82d" -> {
|
||||
return R.drawable.haze_weather
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.haze_weather_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.haze_weather_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.haze_weather_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.haze_weather_5 else R.drawable.haze_weather_5_light
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
"01n" -> {
|
||||
return R.drawable.clear_night
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.clear_night_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.clear_night_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.clear_night_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.clear_night_5 else R.drawable.clear_night_5_light
|
||||
}
|
||||
}
|
||||
"02n" -> {
|
||||
return R.drawable.partly_cloudy_night
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.partly_cloudy_night_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.partly_cloudy_night_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.partly_cloudy_night_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.partly_cloudy_night_5 else R.drawable.partly_cloudy_night_5_light
|
||||
}
|
||||
}
|
||||
"03n" -> {
|
||||
return R.drawable.mostly_cloudy_night
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.mostly_cloudy_night_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.mostly_cloudy_night_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.mostly_cloudy_night_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.mostly_cloudy_night_5 else R.drawable.mostly_cloudy_night_5_light
|
||||
}
|
||||
}
|
||||
"04n" -> {
|
||||
return R.drawable.cloudy_weather
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.cloudy_weather_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.cloudy_weather_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.cloudy_weather_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.cloudy_weather_5 else R.drawable.cloudy_weather_5_light
|
||||
}
|
||||
}
|
||||
"09n" -> {
|
||||
return R.drawable.storm_weather_night
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.storm_weather_night_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.storm_weather_night_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.storm_weather_night_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.storm_weather_night_5 else R.drawable.storm_weather_night_5_light
|
||||
}
|
||||
}
|
||||
"10n" -> {
|
||||
return R.drawable.rainy_night
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.rainy_night_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.rainy_night_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.rainy_night_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.rainy_night_5 else R.drawable.rainy_night_5_light
|
||||
}
|
||||
}
|
||||
"11n" -> {
|
||||
return R.drawable.thunder_night
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.thunder_night_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.thunder_night_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.thunder_night_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.thunder_night_5 else R.drawable.thunder_night_5_light
|
||||
}
|
||||
}
|
||||
"13n" -> {
|
||||
return R.drawable.snow_night
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.snow_night_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.snow_night_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.snow_night_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.snow_night_5 else R.drawable.snow_night_5_light
|
||||
}
|
||||
}
|
||||
"50n" -> {
|
||||
return R.drawable.haze_night
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.haze_night_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.haze_night_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.haze_night_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.haze_night_5 else R.drawable.haze_night_5_light
|
||||
}
|
||||
}
|
||||
"80n" -> {
|
||||
return R.drawable.windy_night
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.windy_night_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.windy_night_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.windy_night_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.windy_night_5 else R.drawable.windy_night_5_light
|
||||
}
|
||||
}
|
||||
"81n" -> {
|
||||
return R.drawable.rain_snow_night
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.rain_snow_night_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.rain_snow_night_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.rain_snow_night_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.rain_snow_night_5 else R.drawable.rain_snow_night_5_light
|
||||
}
|
||||
}
|
||||
"82n" -> {
|
||||
return R.drawable.haze_weather
|
||||
when (style) {
|
||||
Constants.WeatherIconPack.COOL.value -> R.drawable.haze_weather_3
|
||||
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.haze_weather_2
|
||||
Constants.WeatherIconPack.GOOGLE_NEWS.value -> R.drawable.haze_weather_4
|
||||
else -> if (context.isDarkTheme()) R.drawable.haze_weather_5 else R.drawable.haze_weather_5_light
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
return R.drawable.unknown
|
||||
return if (context.isDarkTheme()) R.drawable.unknown_dark else R.drawable.unknown_light
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getWeatherGovIcon(iconString: String, isDaytime: Boolean): String = when {
|
||||
iconString.contains("skc") -> "01"
|
||||
iconString.contains("few") -> "02"
|
||||
iconString.contains("sct") -> "03"
|
||||
iconString.contains("bkn") -> "04"
|
||||
iconString.contains("ovc") -> "04"
|
||||
iconString.contains("wind_skc") -> "01"
|
||||
iconString.contains("wind_few") -> "02"
|
||||
iconString.contains("wind_sct") -> "03"
|
||||
iconString.contains("wind_bkn") -> "04"
|
||||
iconString.contains("wind_ovc") -> "04"
|
||||
iconString.contains("snow") -> "13"
|
||||
iconString.contains("rain_snow") -> "81"
|
||||
iconString.contains("rain_sleet") -> "81"
|
||||
iconString.contains("snow_sleet") -> "81"
|
||||
iconString.contains("fzra") -> "81"
|
||||
iconString.contains("rain_fzra") -> "81"
|
||||
iconString.contains("snow_fzra") -> "81"
|
||||
iconString.contains("sleet") -> "81"
|
||||
iconString.contains("rain") -> "10"
|
||||
iconString.contains("rain_showers") -> "10"
|
||||
iconString.contains("rain_showers_hi") -> "10"
|
||||
iconString.contains("tsra") -> "82"
|
||||
iconString.contains("tsra_sct") -> "82"
|
||||
iconString.contains("tsra_hi") -> "82"
|
||||
iconString.contains("tornado") -> "80"
|
||||
iconString.contains("hurricane") -> "80"
|
||||
iconString.contains("tropical_storm") -> "09"
|
||||
iconString.contains("dust") -> "Dust"
|
||||
iconString.contains("smoke") -> "Smoke"
|
||||
iconString.contains("haze") -> "50"
|
||||
iconString.contains("hot") -> "01"
|
||||
iconString.contains("cold") -> "13"
|
||||
iconString.contains("blizzard") -> "80"
|
||||
iconString.contains("fog") -> "82"
|
||||
else -> ""
|
||||
} + if (isDaytime) "d" else "n"
|
||||
|
||||
fun getWeatherBitIcon(iconString: String): String = when {
|
||||
iconString.contains("t01") -> "11"
|
||||
iconString.contains("t02") -> "09"
|
||||
iconString.contains("t03") -> "09"
|
||||
iconString.contains("t04") -> "09"
|
||||
iconString.contains("t05") -> "09"
|
||||
iconString.contains("d01") -> "10"
|
||||
iconString.contains("d02") -> "10"
|
||||
iconString.contains("d03") -> "10"
|
||||
iconString.contains("r01") -> "10"
|
||||
iconString.contains("r02") -> "10"
|
||||
iconString.contains("r03") -> "10"
|
||||
iconString.contains("f01") -> "10"
|
||||
iconString.contains("r04") -> "10"
|
||||
iconString.contains("r05") -> "10"
|
||||
iconString.contains("r06") -> "10"
|
||||
iconString.contains("s01") -> "13"
|
||||
iconString.contains("s02") -> "13"
|
||||
iconString.contains("s03") -> "13"
|
||||
iconString.contains("s04") -> "81"
|
||||
iconString.contains("s05") -> "90"
|
||||
iconString.contains("s06") -> "13"
|
||||
iconString.contains("a01") -> "82"
|
||||
iconString.contains("a02") -> "82"
|
||||
iconString.contains("a03") -> "82"
|
||||
iconString.contains("a04") -> "82"
|
||||
iconString.contains("a05") -> "82"
|
||||
iconString.contains("a06") -> "82"
|
||||
iconString.contains("c01") -> "01"
|
||||
iconString.contains("c02") -> "02"
|
||||
iconString.contains("c03") -> "04"
|
||||
iconString.contains("c04") -> "04"
|
||||
else -> ""
|
||||
} + if (iconString.contains("d")) "d" else "n"
|
||||
|
||||
fun getWeatherApiIcon(icon: Int, isDaytime: Boolean): String = when(icon) {
|
||||
1000 -> "01"
|
||||
1003 -> "02"
|
||||
1006 -> "03"
|
||||
1009 -> "04"
|
||||
1030 -> "82"
|
||||
1063 -> "10"
|
||||
1066 -> "10"
|
||||
1069 -> "10"
|
||||
1072 -> "81"
|
||||
1087 -> "11"
|
||||
1114 -> "13"
|
||||
1117 -> "09"
|
||||
1135 -> "82"
|
||||
1147 -> "82"
|
||||
1150 -> "10"
|
||||
1153 -> "10"
|
||||
1168 -> "10"
|
||||
1171 -> "10"
|
||||
1180 -> "10"
|
||||
1183 -> "10"
|
||||
1186 -> "10"
|
||||
1189 -> "10"
|
||||
1192 -> "10"
|
||||
1195 -> "10"
|
||||
1198 -> "81"
|
||||
1201 -> "81"
|
||||
1204 -> "13"
|
||||
1207 -> "13"
|
||||
1210 -> "13"
|
||||
1213 -> "13"
|
||||
1216 -> "13"
|
||||
1219 -> "13"
|
||||
1222 -> "13"
|
||||
1225 -> "13"
|
||||
1237 -> "13"
|
||||
1240 -> "10"
|
||||
1243 -> "10"
|
||||
1246 -> "10"
|
||||
1249 -> "13"
|
||||
1252 -> "13"
|
||||
1255 -> "13"
|
||||
1258 -> "13"
|
||||
1261 -> "13"
|
||||
1264 -> "13"
|
||||
1273 -> "09"
|
||||
1276 -> "09"
|
||||
1279 -> "13"
|
||||
1282 -> "13"
|
||||
else -> ""
|
||||
} + if (isDaytime) "d" else "n"
|
||||
|
||||
fun getYRIcon(iconCode: String, isDaytime: Boolean): String = when {
|
||||
iconCode.contains("clearsky") -> "01"
|
||||
iconCode.contains("cloudy") -> "04"
|
||||
iconCode.contains("fair") -> "02"
|
||||
iconCode.contains("fog") -> "82"
|
||||
iconCode.contains("heavyrain") -> "10"
|
||||
iconCode.contains("heavyrainandthunder") -> "11"
|
||||
iconCode.contains("heavyrainshowers") -> "10"
|
||||
iconCode.contains("heavyrainshowersandthunder") -> "11"
|
||||
iconCode.contains("heavysleet") -> "10"
|
||||
iconCode.contains("heavysleetandthunder") -> "11"
|
||||
iconCode.contains("heavysleetshowers") -> "10"
|
||||
iconCode.contains("heavysleetshowersandthunder") -> "11"
|
||||
iconCode.contains("heavysnow") -> "13"
|
||||
iconCode.contains("heavysnowandthunder") -> "13"
|
||||
iconCode.contains("heavysnowshowers") -> "13"
|
||||
iconCode.contains("heavysnowshowersandthunder") -> "13"
|
||||
iconCode.contains("lightrain") -> "10"
|
||||
iconCode.contains("lightrainandthunder") -> "11"
|
||||
iconCode.contains("lightrainshowers") -> "10"
|
||||
iconCode.contains("lightrainshowersandthunder") -> "11"
|
||||
iconCode.contains("lightsleet") -> "10"
|
||||
iconCode.contains("lightsleetandthunder") -> "11"
|
||||
iconCode.contains("lightsleetshowers") -> "10"
|
||||
iconCode.contains("lightsnow") -> "13"
|
||||
iconCode.contains("lightsnowandthunder") -> "13"
|
||||
iconCode.contains("lightsnowshowers") -> "13"
|
||||
iconCode.contains("lightssleetshowersandthunder") -> "81"
|
||||
iconCode.contains("lightssnowshowersandthunder") -> "81"
|
||||
iconCode.contains("partlycloudy") -> "03"
|
||||
iconCode.contains("rain") -> "10"
|
||||
iconCode.contains("rainandthunder") -> "11"
|
||||
iconCode.contains("rainshowers") -> "10"
|
||||
iconCode.contains("rainshowersandthunder") -> "11"
|
||||
iconCode.contains("sleet") -> "10"
|
||||
iconCode.contains("sleetandthunder") -> "11"
|
||||
iconCode.contains("sleetshowers") -> "10"
|
||||
iconCode.contains("sleetshowersandthunder") -> "11"
|
||||
iconCode.contains("snow") -> "13"
|
||||
iconCode.contains("snowandthunder") -> "13"
|
||||
iconCode.contains("snowshowers") -> "13"
|
||||
iconCode.contains("snowshowersandthunder") -> "13"
|
||||
else -> ""
|
||||
} + if (isDaytime) "d" else "n"
|
||||
|
||||
}
|
@ -3,9 +3,20 @@ package com.tommasoberlose.anotherwidget.helpers
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration.ORIENTATION_PORTRAIT
|
||||
import android.graphics.Typeface
|
||||
import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import androidx.core.provider.FontRequest
|
||||
import androidx.core.provider.FontsContractCompat
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.db.EventRepository
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import com.tommasoberlose.anotherwidget.utils.toPixel
|
||||
import kotlin.math.min
|
||||
|
||||
object WidgetHelper {
|
||||
class WidgetSizeProvider(
|
||||
@ -24,19 +35,9 @@ object WidgetHelper {
|
||||
return widthInPx to heightInPx
|
||||
}
|
||||
|
||||
private fun getWidgetWidth(isPortrait: Boolean, widgetId: Int): Int =
|
||||
if (isPortrait) {
|
||||
getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
|
||||
} else {
|
||||
getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
|
||||
}
|
||||
private fun getWidgetWidth(isPortrait: Boolean, widgetId: Int): Int = getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
|
||||
|
||||
private fun getWidgetHeight(isPortrait: Boolean, widgetId: Int): Int =
|
||||
if (isPortrait) {
|
||||
getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)
|
||||
} else {
|
||||
getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
|
||||
}
|
||||
private fun getWidgetHeight(isPortrait: Boolean, widgetId: Int): Int = getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)
|
||||
|
||||
private fun getWidgetSizeInDp(widgetId: Int, key: String): Int =
|
||||
appWidgetManager.getAppWidgetOptions(widgetId).getInt(key, 0)
|
||||
@ -54,10 +55,36 @@ object WidgetHelper {
|
||||
}
|
||||
}
|
||||
|
||||
fun showSpecialWeather(context: Context): Boolean {
|
||||
return EventRepository(context).getEventsCount() == 0 && (
|
||||
(Preferences.showNextAlarm && AlarmHelper.getNextAlarm(context) != "") ||
|
||||
(MediaPlayerHelper.isSomeonePlaying(context))
|
||||
fun runWithCustomTypeface(context: Context, function: (typeface: Typeface?) -> Unit) {
|
||||
if (Preferences.customFontFile != "") {
|
||||
val request = FontRequest(
|
||||
"com.google.android.gms.fonts",
|
||||
"com.google.android.gms",
|
||||
Preferences.customFontFile,
|
||||
R.array.com_google_android_gms_fonts_certs
|
||||
)
|
||||
|
||||
val callback = object : FontsContractCompat.FontRequestCallback() {
|
||||
override fun onTypefaceRetrieved(typeface: Typeface) {
|
||||
function.invoke(typeface)
|
||||
}
|
||||
|
||||
override fun onTypefaceRequestFailed(reason: Int) {
|
||||
function.invoke(null)
|
||||
}
|
||||
}
|
||||
|
||||
val handlerThread = HandlerThread("generateView")
|
||||
handlerThread.start()
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare()
|
||||
}
|
||||
|
||||
Handler(handlerThread.looper).run {
|
||||
FontsContractCompat.requestFont(context, request, callback, this)
|
||||
}
|
||||
} else {
|
||||
function.invoke(null)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package com.tommasoberlose.anotherwidget.models
|
||||
|
||||
import android.provider.CalendarContract
|
||||
import io.realm.RealmObject
|
||||
import java.util.Date
|
||||
|
||||
@ -7,14 +8,18 @@ import java.util.Date
|
||||
* Created by tommaso on 05/10/17.
|
||||
*/
|
||||
|
||||
open class Event(var id: Long = 0,
|
||||
var eventID: Long = 0,
|
||||
var title: String = "",
|
||||
var startDate: Long = 0,
|
||||
var endDate: Long = 0,
|
||||
var calendarID: Int = 0,
|
||||
var allDay: Boolean = false,
|
||||
var address: String = "") : RealmObject() {
|
||||
open class Event(
|
||||
var id: Long = 0,
|
||||
var eventID: Long = 0,
|
||||
var title: String = "",
|
||||
var startDate: Long = 0,
|
||||
var endDate: Long = 0,
|
||||
var calendarID: Int = 0,
|
||||
var allDay: Boolean = false,
|
||||
var address: String = "",
|
||||
var selfAttendeeStatus: Int = CalendarContract.Attendees.ATTENDEE_STATUS_NONE,
|
||||
var availability: Int = CalendarContract.EventsEntity.AVAILABILITY_BUSY
|
||||
) : RealmObject() {
|
||||
override fun toString(): String {
|
||||
return "Event:\nEVENT ID: " + eventID + "\nTITLE: " + title + "\nSTART DATE: " + Date(startDate) + "\nEND DATE: " + Date(endDate) + "\nCAL ID: " + calendarID + "\nADDRESS: " + address
|
||||
}
|
||||
|
@ -3,10 +3,5 @@ package com.tommasoberlose.anotherwidget.models
|
||||
class GlanceProvider(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val icon: Int,
|
||||
val label: String = "",
|
||||
val enabled: Boolean = false,
|
||||
val isPermissionRequired: Boolean = false,
|
||||
val isPermissionGranted: (() -> Boolean)? = null,
|
||||
val requestPermission: (() -> Unit)? = null
|
||||
val icon: Int
|
||||
)
|
@ -1,20 +1,56 @@
|
||||
package com.tommasoberlose.anotherwidget.network
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.google.gson.internal.LinkedTreeMap
|
||||
import com.haroldadmin.cnradapter.NetworkResponse
|
||||
import com.haroldadmin.cnradapter.executeWithRetry
|
||||
import com.kwabenaberko.openweathermaplib.constants.Units
|
||||
import com.kwabenaberko.openweathermaplib.implementation.OpenWeatherMapHelper
|
||||
import com.kwabenaberko.openweathermaplib.implementation.callbacks.CurrentWeatherCallback
|
||||
import com.kwabenaberko.openweathermaplib.models.currentweather.CurrentWeather
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.global.Constants
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
||||
import com.tommasoberlose.anotherwidget.network.repository.*
|
||||
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.lang.Exception
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class WeatherNetworkApi(val context: Context) {
|
||||
fun updateWeather() {
|
||||
if (Preferences.showWeather && Preferences.weatherProviderApi != "" && Preferences.customLocationLat != "" && Preferences.customLocationLon != "") {
|
||||
val helper = OpenWeatherMapHelper(Preferences.weatherProviderApi)
|
||||
suspend fun updateWeather() {
|
||||
Preferences.weatherProviderError = "-"
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
|
||||
if (Preferences.showWeather && Preferences.customLocationLat != "" && Preferences.customLocationLon != "") {
|
||||
when (Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) {
|
||||
Constants.WeatherProvider.OPEN_WEATHER -> useOpenWeatherMap(context)
|
||||
Constants.WeatherProvider.WEATHER_GOV -> useWeatherGov(context)
|
||||
Constants.WeatherProvider.WEATHER_BIT -> useWeatherBitProvider(context)
|
||||
Constants.WeatherProvider.WEATHER_API -> useWeatherApiProvider(context)
|
||||
Constants.WeatherProvider.HERE -> useHereProvider(context)
|
||||
Constants.WeatherProvider.ACCUWEATHER -> useAccuweatherProvider(context)
|
||||
Constants.WeatherProvider.YR -> useYrProvider(context)
|
||||
}
|
||||
} else {
|
||||
WeatherHelper.removeWeather(
|
||||
context
|
||||
)
|
||||
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
|
||||
private fun useOpenWeatherMap(context: Context) {
|
||||
if (Preferences.weatherProviderApiOpen != "") {
|
||||
val helper = OpenWeatherMapHelper(Preferences.weatherProviderApiOpen)
|
||||
helper.setUnits(if (Preferences.weatherTempUnit == "F") Units.IMPERIAL else Units.METRIC)
|
||||
helper.getCurrentWeatherByGeoCoordinates(Preferences.customLocationLat.toDouble(), Preferences.customLocationLon.toDouble(), object :
|
||||
CurrentWeatherCallback {
|
||||
@ -24,19 +60,383 @@ class WeatherNetworkApi(val context: Context) {
|
||||
Preferences.weatherIcon = currentWeather.weather[0].icon
|
||||
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||
MainWidget.updateWidget(context)
|
||||
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
|
||||
override fun onFailure(throwable: Throwable?) {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
|
||||
})
|
||||
} else {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
|
||||
WeatherHelper.removeWeather(
|
||||
context
|
||||
)
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun useWeatherGov(context: Context) {
|
||||
val repository = WeatherGovRepository()
|
||||
val pointsResponse = executeWithRetry(times = 5) {
|
||||
repository.getGridPoints(
|
||||
Preferences.customLocationLat,
|
||||
Preferences.customLocationLon
|
||||
)
|
||||
}
|
||||
|
||||
when (pointsResponse) {
|
||||
is NetworkResponse.Success -> {
|
||||
try {
|
||||
val pp = pointsResponse.body["properties"] as LinkedTreeMap<*, *>
|
||||
val gridId = pp["gridId"] as String
|
||||
val gridX = pp["gridX"] as Double
|
||||
val gridY = pp["gridY"] as Double
|
||||
|
||||
when (val weatherResponse = repository.getWeather(
|
||||
gridId,
|
||||
gridX,
|
||||
gridY,
|
||||
if (Preferences.weatherTempUnit == "F") "us" else "si"
|
||||
)) {
|
||||
is NetworkResponse.Success -> {
|
||||
try {
|
||||
val props =
|
||||
weatherResponse.body["properties"] as LinkedTreeMap<*, *>
|
||||
val periods = props["periods"] as List<*>
|
||||
val now = periods[0] as LinkedTreeMap<*, *>
|
||||
|
||||
val temp = now["temperature"] as Double
|
||||
val fullIcon = now["icon"] as String
|
||||
val isDaytime = now["isDaytime"] as Boolean
|
||||
|
||||
Preferences.weatherTemp = temp.toFloat()
|
||||
Preferences.weatherIcon = WeatherHelper.getWeatherGovIcon(fullIcon, isDaytime)
|
||||
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
|
||||
MainWidget.updateWidget(context)
|
||||
} catch (ex: Exception) {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
} finally {
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
}
|
||||
} catch(ex: Exception) {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
} finally {
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
is NetworkResponse.ServerError -> {
|
||||
if (pointsResponse.body?.containsKey("status") == true && (pointsResponse.body?.get("status") as Double).toInt() == 404) {
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_wrong_location)
|
||||
} else {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
|
||||
WeatherHelper.removeWeather(
|
||||
context
|
||||
)
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
else -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun useHereProvider(context: Context) {
|
||||
if (Preferences.weatherProviderApiHere != "") {
|
||||
val repository = HereRepository()
|
||||
|
||||
when (val response = repository.getWeather()) {
|
||||
is NetworkResponse.Success -> {
|
||||
try {
|
||||
Log.d("ciao - here", response.body.toString())
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
} catch(ex: Exception) {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
} finally {
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
is NetworkResponse.ServerError -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
WeatherHelper.removeWeather(
|
||||
context
|
||||
)
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
else -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
|
||||
WeatherHelper.removeWeather(
|
||||
context
|
||||
)
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun useWeatherBitProvider(context: Context) {
|
||||
if (Preferences.weatherProviderApiWeatherBit != "") {
|
||||
val repository = WeatherbitRepository()
|
||||
|
||||
when (val response = repository.getWeather()) {
|
||||
is NetworkResponse.Success -> {
|
||||
try {
|
||||
val data = response.body["data"] as List<LinkedTreeMap<String, Any>>?
|
||||
data?.first()?.let {
|
||||
val temp = it["temp"] as Double
|
||||
val weatherInfo = it["weather"] as LinkedTreeMap<String, Any>
|
||||
val iconCode = weatherInfo["icon"] as String
|
||||
|
||||
Preferences.weatherTemp = temp.toFloat()
|
||||
Preferences.weatherIcon = WeatherHelper.getWeatherBitIcon(iconCode)
|
||||
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||
MainWidget.updateWidget(context)
|
||||
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
} catch(ex: Exception) {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
} finally {
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
is NetworkResponse.ServerError -> {
|
||||
when (response.code) {
|
||||
403 -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_invalid_key)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
else -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
}
|
||||
WeatherHelper.removeWeather(
|
||||
context
|
||||
)
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
else -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
|
||||
WeatherHelper.removeWeather(
|
||||
context
|
||||
)
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun useWeatherApiProvider(context: Context) {
|
||||
if (Preferences.weatherProviderApiWeatherApi != "") {
|
||||
val repository = WeatherApiRepository()
|
||||
|
||||
when (val response = repository.getWeather()) {
|
||||
is NetworkResponse.Success -> {
|
||||
try {
|
||||
val current = response.body["current"] as LinkedTreeMap<String, Any>?
|
||||
current?.let {
|
||||
val tempC = current["temp_c"] as Double
|
||||
val tempF = current["temp_f"] as Double
|
||||
val isDay = current["is_day"] as Double
|
||||
val condition = current["condition"] as LinkedTreeMap<String, Any>
|
||||
val iconCode = condition["code"] as Double
|
||||
|
||||
Preferences.weatherTemp = if (Preferences.weatherTempUnit == "F") tempF.toFloat() else tempC.toFloat()
|
||||
Preferences.weatherIcon = WeatherHelper.getWeatherApiIcon(iconCode.toInt(), isDay.toInt() == 1)
|
||||
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||
MainWidget.updateWidget(context)
|
||||
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
} catch(ex: Exception) {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
} finally {
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
is NetworkResponse.ServerError -> {
|
||||
when (response.code) {
|
||||
401 -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_invalid_key)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
403 -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_expired_key)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
else -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
}
|
||||
}
|
||||
|
||||
WeatherHelper.removeWeather(
|
||||
context
|
||||
)
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
else -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
|
||||
WeatherHelper.removeWeather(
|
||||
context
|
||||
)
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun useAccuweatherProvider(context: Context) {
|
||||
if (Preferences.weatherProviderApiAccuweather != "") {
|
||||
val repository = AccuweatherRepository()
|
||||
|
||||
// when (val response = repository.getWeather()) {
|
||||
// is NetworkResponse.Success -> {
|
||||
// try {
|
||||
// Log.d("ciao", response.body.toString())
|
||||
// } catch(ex: Exception) {
|
||||
//
|
||||
// Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
|
||||
// Preferences.weatherProviderLocationError = ""
|
||||
// }
|
||||
// }
|
||||
// is NetworkResponse.ServerError -> {
|
||||
// WeatherHelper.removeWeather(
|
||||
// context
|
||||
// )
|
||||
// }
|
||||
// Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
|
||||
// Preferences.weatherProviderLocationError = ""
|
||||
// EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
// }
|
||||
|
||||
} else {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
|
||||
WeatherHelper.removeWeather(
|
||||
context
|
||||
)
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun useYrProvider(context: Context) {
|
||||
val repository = YrRepository()
|
||||
|
||||
when (val response = repository.getWeather()) {
|
||||
is NetworkResponse.Success -> {
|
||||
try {
|
||||
val pp = response.body["properties"] as LinkedTreeMap<*, *>
|
||||
val data = pp["timeseries"] as List<LinkedTreeMap<String, Any>>?
|
||||
data?.let {
|
||||
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
|
||||
for (item in data) {
|
||||
val time = Calendar.getInstance().apply { time = format.parse(item["time"] as String)!! }
|
||||
val now = Calendar.getInstance()
|
||||
if (time.timeInMillis >= now.timeInMillis) {
|
||||
val dd = item["data"] as LinkedTreeMap<*, *>
|
||||
val instant = dd["instant"] as LinkedTreeMap<*, *>
|
||||
val next = dd["next_1_hours"] as LinkedTreeMap<*, *>
|
||||
|
||||
val details = instant["details"] as LinkedTreeMap<*, *>
|
||||
val temp = details["air_temperature"] as Double
|
||||
|
||||
val summary = next["summary"] as LinkedTreeMap<*, *>
|
||||
val iconCode = summary["symbol_code"] as String
|
||||
|
||||
Preferences.weatherTemp = temp.toFloat()
|
||||
Preferences.weatherIcon = WeatherHelper.getYRIcon(iconCode, now.get(Calendar.HOUR_OF_DAY) >= 22 || now.get(Calendar.HOUR_OF_DAY) <= 8)
|
||||
Preferences.weatherTempUnit = "C"
|
||||
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
|
||||
MainWidget.updateWidget(context)
|
||||
|
||||
Preferences.weatherProviderError = ""
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
} catch(ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
} finally {
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
is NetworkResponse.ServerError -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
WeatherHelper.removeWeather(
|
||||
context
|
||||
)
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
else -> {
|
||||
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
|
||||
Preferences.weatherProviderLocationError = ""
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package com.tommasoberlose.anotherwidget.network.api
|
||||
|
||||
import com.haroldadmin.cnradapter.NetworkResponse
|
||||
import retrofit2.http.*
|
||||
|
||||
object ApiServices {
|
||||
interface WeatherGovApiService {
|
||||
@Headers("User-Agent: (Another Widget, tommaso.berlose@gmail.com)")
|
||||
@GET("points/{latitude},{longitude}")
|
||||
suspend fun getGridPoints(
|
||||
@Path("latitude") latitude: String,
|
||||
@Path("longitude") longitude: String
|
||||
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||
|
||||
@Headers("User-Agent: (Another Widget, tommaso.berlose@gmail.com)")
|
||||
@GET("gridpoints/{gridId}/{gridX},{gridY}/forecast")
|
||||
suspend fun getWeather(
|
||||
@Path("gridId") gridId: String,
|
||||
@Path("gridX") gridX: Int,
|
||||
@Path("gridY") gridY: Int,
|
||||
@Query("units") unit: String
|
||||
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||
}
|
||||
|
||||
interface WeatherBitService {
|
||||
@GET("current")
|
||||
suspend fun getWeather(
|
||||
@Query("key") key: String,
|
||||
@Query("lat") lat: String,
|
||||
@Query("lon") lon: String,
|
||||
@Query("units") units: String,
|
||||
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||
}
|
||||
|
||||
interface WeatherApiService {
|
||||
@Headers("Accept: application/json")
|
||||
@GET("current.json")
|
||||
suspend fun getWeather(
|
||||
@Query("key") key: String,
|
||||
@Query("q") location: String,
|
||||
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||
}
|
||||
|
||||
interface HereService {
|
||||
@GET("report.json")
|
||||
suspend fun getWeather(
|
||||
@Query("apiKey") apiKey: String,
|
||||
@Query("latitude") latitude: String,
|
||||
@Query("longitude") longitude: String,
|
||||
@Query("product") product: String,
|
||||
@Query("oneobservation") oneobservation: Boolean,
|
||||
@Query("metric") metric: Boolean,
|
||||
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||
}
|
||||
|
||||
interface AccuweatherService {
|
||||
@GET("")
|
||||
suspend fun getWeather(
|
||||
@Path("gridId") gridId: String,
|
||||
@Path("gridX") gridX: Int,
|
||||
@Path("gridY") gridY: Int,
|
||||
@Query("units") unit: String
|
||||
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||
}
|
||||
|
||||
interface YrService {
|
||||
@Headers("User-Agent: AnotherWidget")
|
||||
@GET("compact.json")
|
||||
suspend fun getWeather(
|
||||
@Query("lat") lat: String,
|
||||
@Query("lon") lon: String,
|
||||
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.tommasoberlose.anotherwidget.network.repository
|
||||
|
||||
import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory
|
||||
import com.tommasoberlose.anotherwidget.network.api.ApiServices
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
|
||||
class AccuweatherRepository {
|
||||
|
||||
/* ACCUWEATHER */
|
||||
private val apiServiceAccu: ApiServices.AccuweatherService = getRetrofit().create(ApiServices.AccuweatherService::class.java)
|
||||
suspend fun getWeather(): Nothing = TODO()
|
||||
|
||||
companion object {
|
||||
private const val BASE_URL_ACCU = ""
|
||||
|
||||
private fun getRetrofit(): Retrofit {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(BASE_URL_ACCU)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addCallAdapterFactory(NetworkResponseAdapterFactory())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.tommasoberlose.anotherwidget.network.repository
|
||||
|
||||
import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.network.api.ApiServices
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
|
||||
class HereRepository {
|
||||
|
||||
/* HERE */
|
||||
private val apiServiceHere: ApiServices.HereService = getRetrofit().create(ApiServices.HereService::class.java)
|
||||
suspend fun getWeather() = apiServiceHere.getWeather(Preferences.weatherProviderApiHere, Preferences.customLocationLat, Preferences.customLocationLon, "observation", true, Preferences.weatherTempUnit != "F")
|
||||
|
||||
companion object {
|
||||
private const val BASE_URL_HERE = "https://weather.ls.hereapi.com/weather/1.0/"
|
||||
|
||||
private fun getRetrofit(): Retrofit {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(BASE_URL_HERE)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addCallAdapterFactory(NetworkResponseAdapterFactory())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.tommasoberlose.anotherwidget.network.repository
|
||||
|
||||
import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.network.api.ApiServices
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
|
||||
class WeatherApiRepository {
|
||||
|
||||
/* WEATHER API*/
|
||||
private val apiServiceApi: ApiServices.WeatherApiService = getRetrofit().create(ApiServices.WeatherApiService::class.java)
|
||||
suspend fun getWeather() = apiServiceApi.getWeather(Preferences.weatherProviderApiWeatherApi, "${Preferences.customLocationLat},${Preferences.customLocationLon}")
|
||||
|
||||
companion object {
|
||||
private const val BASE_URL_API = "http://api.weatherapi.com/v1/"
|
||||
|
||||
private fun getRetrofit(): Retrofit {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(BASE_URL_API)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addCallAdapterFactory(NetworkResponseAdapterFactory())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.tommasoberlose.anotherwidget.network.repository
|
||||
|
||||
import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory
|
||||
import com.tommasoberlose.anotherwidget.network.api.ApiServices
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
|
||||
class WeatherGovRepository {
|
||||
|
||||
/* WEATHER GOV*/
|
||||
private val apiServiceGov: ApiServices.WeatherGovApiService = getRetrofit().create(ApiServices.WeatherGovApiService::class.java)
|
||||
suspend fun getGridPoints(latitude: String, longitude: String) = apiServiceGov.getGridPoints(latitude, longitude)
|
||||
suspend fun getWeather(gridId: String, gridX: Double, gridY: Double, unit: String) = apiServiceGov.getWeather(gridId, gridX.toInt(), gridY.toInt(), unit)
|
||||
|
||||
companion object {
|
||||
private const val BASE_URL_GOV = "https://api.weather.gov/"
|
||||
|
||||
private fun getRetrofit(): Retrofit {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(BASE_URL_GOV)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addCallAdapterFactory(NetworkResponseAdapterFactory())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.tommasoberlose.anotherwidget.network.repository
|
||||
|
||||
import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.network.api.ApiServices
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
|
||||
class WeatherbitRepository {
|
||||
|
||||
/* BIT */
|
||||
private val apiServiceBit: ApiServices.WeatherBitService = getRetrofit().create(ApiServices.WeatherBitService::class.java)
|
||||
suspend fun getWeather() = apiServiceBit.getWeather(Preferences.weatherProviderApiWeatherBit, Preferences.customLocationLat, Preferences.customLocationLon, if (Preferences.weatherTempUnit == "F") "I" else "M")
|
||||
|
||||
companion object {
|
||||
private const val BASE_URL_BIT = "https://api.weatherbit.io/v2.0/"
|
||||
|
||||
private fun getRetrofit(): Retrofit {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(BASE_URL_BIT)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addCallAdapterFactory(NetworkResponseAdapterFactory())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.tommasoberlose.anotherwidget.network.repository
|
||||
|
||||
import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.network.api.ApiServices
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
|
||||
class YrRepository {
|
||||
|
||||
/* YR */
|
||||
private val apiServiceYr: ApiServices.YrService = getRetrofit().create(ApiServices.YrService::class.java)
|
||||
suspend fun getWeather() = apiServiceYr.getWeather(Preferences.customLocationLat, Preferences.customLocationLon)
|
||||
|
||||
companion object {
|
||||
private const val BASE_URL_YR = "https://api.met.no/weatherapi/locationforecast/2.0/"
|
||||
|
||||
private fun getRetrofit(): Retrofit {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(BASE_URL_YR)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addCallAdapterFactory(NetworkResponseAdapterFactory())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,183 @@
|
||||
package com.tommasoberlose.anotherwidget.receivers
|
||||
|
||||
import android.Manifest
|
||||
import android.app.AlarmManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import com.chibatching.kotpref.Kotpref
|
||||
import com.chibatching.kotpref.blockingBulk
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
|
||||
import com.google.android.gms.fitness.Fitness
|
||||
import com.google.android.gms.fitness.FitnessOptions
|
||||
import com.google.android.gms.fitness.data.DataType
|
||||
import com.google.android.gms.fitness.data.Field.FIELD_STEPS
|
||||
import com.google.android.gms.fitness.request.DataReadRequest
|
||||
import com.google.android.gms.location.*
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
class ActivityDetectionReceiver : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (ActivityTransitionResult.hasResult(intent)) {
|
||||
val result = ActivityTransitionResult.extractResult(intent)!!
|
||||
val lastEvent = result.transitionEvents.last()
|
||||
|
||||
if (lastEvent.activityType == DetectedActivity.WALKING || lastEvent.activityType == DetectedActivity.RUNNING && lastEvent.transitionType == ActivityTransition.ACTIVITY_TRANSITION_EXIT) {
|
||||
requestDailySteps(context)
|
||||
}
|
||||
} else {
|
||||
if (intent.action == Intent.ACTION_BOOT_COMPLETED || intent.action == Intent.ACTION_MY_PACKAGE_REPLACED && Preferences.showDailySteps && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || context.checkGrantedPermission(Manifest.permission.ACTIVITY_RECOGNITION)) {
|
||||
resetDailySteps(context)
|
||||
registerFence(context)
|
||||
} else {
|
||||
resetDailySteps(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetDailySteps(context: Context) {
|
||||
Kotpref.init(context)
|
||||
Preferences.blockingBulk {
|
||||
remove(Preferences::googleFitSteps)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val FITNESS_OPTIONS: FitnessOptions = FitnessOptions.builder()
|
||||
.addDataType(DataType.TYPE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
|
||||
.addDataType(DataType.AGGREGATE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
|
||||
.build()
|
||||
|
||||
fun registerFence(context: Context) {
|
||||
Kotpref.init(context)
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || context.checkGrantedPermission(
|
||||
Manifest.permission.ACTIVITY_RECOGNITION)) {
|
||||
val transitions = mutableListOf<ActivityTransition>()
|
||||
|
||||
transitions +=
|
||||
ActivityTransition.Builder()
|
||||
.setActivityType(DetectedActivity.WALKING)
|
||||
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
|
||||
.build()
|
||||
|
||||
transitions +=
|
||||
ActivityTransition.Builder()
|
||||
.setActivityType(DetectedActivity.RUNNING)
|
||||
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
|
||||
.build()
|
||||
|
||||
val request = ActivityTransitionRequest(transitions)
|
||||
|
||||
// myPendingIntent is the instance of PendingIntent where the app receives callbacks.
|
||||
val task = ActivityRecognition.getClient(context)
|
||||
.requestActivityTransitionUpdates(
|
||||
request,
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
2,
|
||||
Intent(context, ActivityDetectionReceiver::class.java),
|
||||
0
|
||||
)
|
||||
)
|
||||
|
||||
task.addOnFailureListener { e: Exception ->
|
||||
e.printStackTrace()
|
||||
Preferences.showDailySteps = false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun unregisterFence(context: Context) {
|
||||
val task = ActivityRecognition.getClient(context)
|
||||
.removeActivityTransitionUpdates(
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
2,
|
||||
Intent(context, ActivityDetectionReceiver::class.java),
|
||||
0
|
||||
)
|
||||
)
|
||||
|
||||
task.addOnCompleteListener {
|
||||
if (it.isSuccessful) {
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
2,
|
||||
Intent(context, ActivityDetectionReceiver::class.java),
|
||||
0
|
||||
).cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun requestDailySteps(context: Context) {
|
||||
Kotpref.init(context)
|
||||
|
||||
val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(context)
|
||||
if (account != null && GoogleSignIn.hasPermissions(account, FITNESS_OPTIONS)) {
|
||||
|
||||
val cal: Calendar = Calendar.getInstance()
|
||||
cal.set(Calendar.HOUR_OF_DAY, 0)
|
||||
cal.set(Calendar.MINUTE, 0)
|
||||
cal.set(Calendar.SECOND, 0)
|
||||
cal.set(Calendar.MILLISECOND, 0)
|
||||
val startTime: Long = cal.timeInMillis
|
||||
|
||||
cal.add(Calendar.DAY_OF_YEAR, 1)
|
||||
val endTime: Long = cal.timeInMillis
|
||||
|
||||
val readRequest = DataReadRequest.Builder()
|
||||
.aggregate(
|
||||
DataType.TYPE_STEP_COUNT_DELTA,
|
||||
DataType.AGGREGATE_STEP_COUNT_DELTA
|
||||
)
|
||||
.setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
|
||||
.bucketByTime(1, TimeUnit.DAYS)
|
||||
.build()
|
||||
|
||||
Fitness.getHistoryClient(context, account)
|
||||
.readData(readRequest)
|
||||
.addOnSuccessListener { response ->
|
||||
Preferences.googleFitSteps = response.buckets.sumBy {
|
||||
try {
|
||||
it.getDataSet(DataType.AGGREGATE_STEP_COUNT_DELTA)?.dataPoints?.get(
|
||||
0
|
||||
)?.getValue(FIELD_STEPS)?.asInt() ?: 0
|
||||
} catch (ex: Exception) {
|
||||
0
|
||||
}
|
||||
}.toLong()
|
||||
MainWidget.updateWidget(context)
|
||||
setTimeout(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setTimeout(context: Context) {
|
||||
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||
cancel(PendingIntent.getBroadcast(context, 5, Intent(context, ActivityDetectionReceiver::class.java), 0))
|
||||
setExactAndAllowWhileIdle(
|
||||
AlarmManager.RTC,
|
||||
Calendar.getInstance().timeInMillis + 5 * 60 * 1000,
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
5,
|
||||
Intent(context, ActivityDetectionReceiver::class.java),
|
||||
0
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.tommasoberlose.anotherwidget.receivers
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.BatteryManager
|
||||
import android.util.Log
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import com.tommasoberlose.anotherwidget.utils.toast
|
||||
|
||||
class BatteryLevelReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
when(intent.action) {
|
||||
Intent.ACTION_BATTERY_LOW -> Preferences.isBatteryLevelLow = true
|
||||
Intent.ACTION_BATTERY_OKAY -> Preferences.isBatteryLevelLow = false
|
||||
Intent.ACTION_POWER_CONNECTED -> Preferences.isCharging = true
|
||||
Intent.ACTION_POWER_DISCONNECTED -> Preferences.isCharging = false
|
||||
}
|
||||
MainWidget.updateWidget(context)
|
||||
}
|
||||
|
||||
}
|
@ -24,5 +24,6 @@ class NewCalendarEventReceiver : BroadcastReceiver() {
|
||||
eventRepository.goToPreviousEvent()
|
||||
}
|
||||
}
|
||||
eventRepository.close()
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
package com.tommasoberlose.anotherwidget.receivers
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
|
||||
|
||||
class PlayerReceiver : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
Log.d("ciao", "player ok")
|
||||
|
||||
// val cmd = intent.getStringExtra("command")
|
||||
// Log.v("tag ", "$action / $cmd")
|
||||
// val artist = intent.getStringExtra("artist")
|
||||
// val album = intent.getStringExtra("album")
|
||||
// val track = intent.getStringExtra("track")
|
||||
// Log.v("tag", "$artist:$album:$track")
|
||||
|
||||
}
|
||||
}
|
@ -10,6 +10,9 @@ import androidx.core.app.AlarmManagerCompat
|
||||
import androidx.core.content.ContextCompat.getSystemService
|
||||
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.BatteryHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
|
||||
import com.tommasoberlose.anotherwidget.models.Event
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
@ -26,10 +29,12 @@ class UpdatesReceiver : BroadcastReceiver() {
|
||||
Intent.ACTION_TIME_CHANGED,
|
||||
Intent.ACTION_TIMEZONE_CHANGED,
|
||||
Intent.ACTION_LOCALE_CHANGED,
|
||||
Actions.ACTION_CALENDAR_UPDATE -> CalendarHelper.updateEventList(context)
|
||||
Intent.ACTION_DATE_CHANGED,
|
||||
Actions.ACTION_CALENDAR_UPDATE -> {
|
||||
CalendarHelper.updateEventList(context)
|
||||
}
|
||||
|
||||
"com.sec.android.widgetapp.APPWIDGET_RESIZE",
|
||||
Intent.ACTION_DATE_CHANGED,
|
||||
AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED,
|
||||
Actions.ACTION_TIME_UPDATE -> {
|
||||
MainWidget.updateWidget(context)
|
||||
@ -48,7 +53,7 @@ class UpdatesReceiver : BroadcastReceiver() {
|
||||
if (eventId == null) {
|
||||
removeUpdates(context)
|
||||
|
||||
eventRepository.getEvents().forEach { event ->
|
||||
eventRepository.getFutureEvents().forEach { event ->
|
||||
setEventUpdate(context, event)
|
||||
}
|
||||
} else {
|
||||
@ -57,6 +62,7 @@ class UpdatesReceiver : BroadcastReceiver() {
|
||||
setEventUpdate(context, event)
|
||||
}
|
||||
}
|
||||
eventRepository.close()
|
||||
}
|
||||
|
||||
private fun setEventUpdate(context: Context, event: Event) {
|
||||
@ -66,11 +72,84 @@ class UpdatesReceiver : BroadcastReceiver() {
|
||||
set(Calendar.MILLISECOND, 0)
|
||||
}
|
||||
val diff = Period(now.timeInMillis, event.startDate)
|
||||
if (event.startDate > now.timeInMillis) {
|
||||
// Update the widget every hour till the event
|
||||
setExactAndAllowWhileIdle(
|
||||
val limit = when (Preferences.showUntil) {
|
||||
0 -> 1000 * 60 * 60 * 3
|
||||
1 -> 1000 * 60 * 60 * 6
|
||||
2 -> 1000 * 60 * 60 * 12
|
||||
3 -> 1000 * 60 * 60 * 24
|
||||
4 -> 1000 * 60 * 60 * 24 * 3
|
||||
5 -> 1000 * 60 * 60 * 24 * 7
|
||||
6 -> 1000 * 60 * 30
|
||||
7 -> 1000 * 60 * 60
|
||||
else -> 1000 * 60 * 60 * 6
|
||||
}
|
||||
if (event.startDate <= limit) {
|
||||
if (event.startDate > now.timeInMillis) {
|
||||
// Update the widget every hour till the event
|
||||
if (diff.hours == 0) {
|
||||
var minutes = 0
|
||||
when (Preferences.widgetUpdateFrequency) {
|
||||
Constants.WidgetUpdateFrequency.DEFAULT.value -> {
|
||||
minutes = when {
|
||||
diff.minutes > 50 -> 50
|
||||
diff.minutes > 30 -> 30
|
||||
diff.minutes > 15 -> 15
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
Constants.WidgetUpdateFrequency.HIGH.value -> {
|
||||
minutes = diff.minutes - (diff.minutes % 5)
|
||||
}
|
||||
}
|
||||
setExact(
|
||||
AlarmManager.RTC,
|
||||
if (event.startDate - minutes * 1000 * 60 > (now.timeInMillis + 120 * 1000)) event.startDate - 60 * 1000 * minutes else now.timeInMillis + 120000,
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
event.eventID.toInt(),
|
||||
Intent(context, UpdatesReceiver::class.java).apply {
|
||||
action = Actions.ACTION_TIME_UPDATE
|
||||
putExtra(EVENT_ID, event.eventID)
|
||||
},
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
)
|
||||
} else {
|
||||
setExact(
|
||||
AlarmManager.RTC,
|
||||
event.startDate - diff.hours * 1000 * 60 * 60 + if (diff.minutes > 30) (-30) else (+30),
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
event.eventID.toInt(),
|
||||
Intent(context, UpdatesReceiver::class.java).apply {
|
||||
action = Actions.ACTION_TIME_UPDATE
|
||||
putExtra(EVENT_ID, event.eventID)
|
||||
},
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// Update the widget one second after the event is finished
|
||||
val fireTime =
|
||||
if (event.endDate > now.timeInMillis + 120 * 1000) event.endDate else now.timeInMillis + 120000
|
||||
setExact(
|
||||
AlarmManager.RTC,
|
||||
fireTime,
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
event.eventID.toInt(),
|
||||
Intent(context, UpdatesReceiver::class.java).apply {
|
||||
action = Actions.ACTION_TIME_UPDATE
|
||||
},
|
||||
0
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
setExact(
|
||||
AlarmManager.RTC,
|
||||
if (event.startDate - diff.hours * 1000 * 60 * 60 > (now.timeInMillis + 120 * 1000)) event.startDate - diff.hours * 1000 * 60 * 60 else now.timeInMillis + 120000,
|
||||
if (event.startDate - limit > now.timeInMillis + 120 * 1000) event.startDate - limit else now.timeInMillis + 120000,
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
event.eventID.toInt(),
|
||||
@ -78,23 +157,7 @@ class UpdatesReceiver : BroadcastReceiver() {
|
||||
action = Actions.ACTION_TIME_UPDATE
|
||||
putExtra(EVENT_ID, event.eventID)
|
||||
},
|
||||
0
|
||||
)
|
||||
)
|
||||
} else {
|
||||
// Update the widget one second after the event is finished
|
||||
val fireTime =
|
||||
if (event.endDate > now.timeInMillis + 120 * 1000) event.endDate else now.timeInMillis + 120000
|
||||
setExactAndAllowWhileIdle(
|
||||
AlarmManager.RTC,
|
||||
fireTime,
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
event.eventID.toInt(),
|
||||
Intent(context, UpdatesReceiver::class.java).apply {
|
||||
action = Actions.ACTION_TIME_UPDATE
|
||||
},
|
||||
0
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -103,9 +166,11 @@ class UpdatesReceiver : BroadcastReceiver() {
|
||||
|
||||
fun removeUpdates(context: Context) {
|
||||
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||
EventRepository(context).getEvents().forEach {
|
||||
val eventRepository = EventRepository(context)
|
||||
eventRepository.getFutureEvents().forEach {
|
||||
cancel(PendingIntent.getBroadcast(context, it.eventID.toInt(), Intent(context, UpdatesReceiver::class.java), 0))
|
||||
}
|
||||
eventRepository.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,12 @@ import android.app.PendingIntent
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import com.tommasoberlose.anotherwidget.global.Actions
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.*
|
||||
|
||||
|
||||
@ -23,7 +25,9 @@ class WeatherReceiver : BroadcastReceiver() {
|
||||
Intent.ACTION_TIME_CHANGED -> setUpdates(context)
|
||||
|
||||
Actions.ACTION_WEATHER_UPDATE -> {
|
||||
WeatherHelper.updateWeather(context)
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
WeatherHelper.updateWeather(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,7 +37,7 @@ class WeatherReceiver : BroadcastReceiver() {
|
||||
fun setUpdates(context: Context) {
|
||||
removeUpdates(context)
|
||||
|
||||
if (Preferences.showWeather && Preferences.weatherProviderApi != "") {
|
||||
if (Preferences.showWeather) {
|
||||
val interval = MINUTE * when (Preferences.weatherRefreshPeriod) {
|
||||
0 -> 30
|
||||
1 -> 60
|
||||
@ -55,7 +59,7 @@ class WeatherReceiver : BroadcastReceiver() {
|
||||
}
|
||||
|
||||
fun setOneTimeUpdate(context: Context) {
|
||||
if (Preferences.showWeather && Preferences.weatherProviderApi != "") {
|
||||
if (Preferences.showWeather) {
|
||||
listOf(10, 20, 30).forEach {
|
||||
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||
setExactAndAllowWhileIdle(
|
||||
|
@ -0,0 +1,61 @@
|
||||
package com.tommasoberlose.anotherwidget.services
|
||||
|
||||
import android.app.job.JobInfo
|
||||
import android.app.job.JobParameters
|
||||
import android.app.job.JobScheduler
|
||||
import android.app.job.JobService
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.provider.CalendarContract
|
||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
|
||||
class BatteryListenerJob : JobService() {
|
||||
override fun onStartJob(params: JobParameters): Boolean {
|
||||
MainWidget.updateWidget(this)
|
||||
schedule(
|
||||
this
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun onStopJob(params: JobParameters): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val chargingJobId = 1006
|
||||
private const val notChargingJobId = 1007
|
||||
fun schedule(context: Context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
remove(context)
|
||||
val componentName = ComponentName(
|
||||
context,
|
||||
EventListenerJob::class.java
|
||||
)
|
||||
with(context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler) {
|
||||
schedule(
|
||||
JobInfo.Builder(chargingJobId, componentName)
|
||||
.setRequiresCharging(true)
|
||||
.setPersisted(true)
|
||||
.build()
|
||||
)
|
||||
schedule(
|
||||
JobInfo.Builder(notChargingJobId, componentName)
|
||||
.setRequiresCharging(false)
|
||||
.setPersisted(true)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun remove(context: Context) {
|
||||
val js = context.getSystemService(JobScheduler::class.java)
|
||||
js?.cancel(chargingJobId)
|
||||
js?.cancel(notChargingJobId)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
package com.tommasoberlose.anotherwidget.services
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.provider.CalendarContract
|
||||
import android.util.Log
|
||||
import androidx.core.app.JobIntentService
|
||||
import com.tommasoberlose.anotherwidget.db.EventRepository
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.applyFilters
|
||||
import com.tommasoberlose.anotherwidget.models.Event
|
||||
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
|
||||
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import me.everything.providers.android.calendar.CalendarProvider
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.util.*
|
||||
import kotlin.Comparator
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class UpdateCalendarJob : JobIntentService() {
|
||||
|
||||
companion object {
|
||||
private const val jobId = 1200
|
||||
|
||||
fun enqueueWork(context: Context, work: Intent) {
|
||||
enqueueWork(context, UpdateCalendarJob::class.java, jobId, work)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onHandleWork(intent: Intent) {
|
||||
val eventRepository = EventRepository(this)
|
||||
if (Preferences.showEvents) {
|
||||
val eventList = ArrayList<Event>()
|
||||
|
||||
val now = Calendar.getInstance()
|
||||
val begin = Calendar.getInstance().apply {
|
||||
set(Calendar.MILLISECOND, 0)
|
||||
set(Calendar.SECOND, 0)
|
||||
set(Calendar.MINUTE, 0)
|
||||
set(Calendar.HOUR_OF_DAY, 0)
|
||||
}
|
||||
val limit = Calendar.getInstance().apply {
|
||||
timeInMillis = begin.timeInMillis
|
||||
add(Calendar.DAY_OF_YEAR, 2)
|
||||
}
|
||||
|
||||
if (!checkGrantedPermission(
|
||||
Manifest.permission.READ_CALENDAR
|
||||
)
|
||||
) {
|
||||
eventRepository.resetNextEventData()
|
||||
} else {
|
||||
try {
|
||||
val provider = CalendarProvider(this)
|
||||
val data = provider.getInstances(begin.timeInMillis, limit.timeInMillis)
|
||||
if (data != null) {
|
||||
val instances = data.list
|
||||
for (instance in instances) {
|
||||
try {
|
||||
val e = provider.getEvent(instance.eventId)
|
||||
if (e != null && !e.deleted && instance.begin <= limit.timeInMillis && now.timeInMillis < instance.end && !CalendarHelper.getFilteredCalendarIdList()
|
||||
.contains(e.calendarId)
|
||||
) {
|
||||
if (e.allDay) {
|
||||
val start = Calendar.getInstance()
|
||||
start.timeInMillis = instance.begin
|
||||
val end = Calendar.getInstance()
|
||||
end.timeInMillis = instance.end
|
||||
instance.begin =
|
||||
start.timeInMillis - start.timeZone.getOffset(start.timeInMillis)
|
||||
instance.end =
|
||||
end.timeInMillis - end.timeZone.getOffset(end.timeInMillis)
|
||||
}
|
||||
eventList.add(
|
||||
Event(
|
||||
id = instance.id,
|
||||
eventID = e.id,
|
||||
title = e.title ?: "",
|
||||
startDate = instance.begin,
|
||||
endDate = instance.end,
|
||||
calendarID = e.calendarId.toInt(),
|
||||
allDay = e.allDay,
|
||||
address = e.eventLocation ?: "",
|
||||
selfAttendeeStatus = e.selfAttendeeStatus.toInt(),
|
||||
availability = e.availability
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val filteredEventList = eventList
|
||||
.applyFilters()
|
||||
|
||||
if (filteredEventList.isEmpty()) {
|
||||
eventRepository.resetNextEventData()
|
||||
eventRepository.clearEvents()
|
||||
} else {
|
||||
eventList.sortWith(Comparator { event: Event, event1: Event ->
|
||||
val date = Calendar.getInstance().apply { timeInMillis = event.startDate }
|
||||
val date1 = Calendar.getInstance().apply { timeInMillis = event1.startDate }
|
||||
|
||||
if (date.get(Calendar.DAY_OF_YEAR) == date1.get(Calendar.DAY_OF_YEAR) && date.get(Calendar.YEAR) == date1.get(Calendar.YEAR)) {
|
||||
if (event.allDay && event1.allDay) {
|
||||
event.startDate.compareTo(event1.startDate)
|
||||
} else if (event.allDay) {
|
||||
1
|
||||
} else if (event1.allDay) {
|
||||
-1
|
||||
} else {
|
||||
event.startDate.compareTo(event1.startDate)
|
||||
}
|
||||
} else {
|
||||
event.startDate.compareTo(event1.startDate)
|
||||
}
|
||||
})
|
||||
|
||||
eventRepository.saveEvents(
|
||||
eventList
|
||||
)
|
||||
eventRepository.saveNextEventData(filteredEventList.first())
|
||||
}
|
||||
} catch (ignored: java.lang.Exception) {
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eventRepository.resetNextEventData()
|
||||
}
|
||||
|
||||
UpdatesReceiver.setUpdates(this)
|
||||
MainWidget.updateWidget(this)
|
||||
|
||||
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
|
||||
eventRepository.close()
|
||||
}
|
||||
}
|
@ -6,19 +6,23 @@ import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.chibatching.kotpref.blockingBulk
|
||||
import com.chibatching.kotpref.bulk
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.databinding.ActivityCustomDateBinding
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.DateHelper
|
||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.CustomDateViewModel
|
||||
import com.tommasoberlose.anotherwidget.utils.getCapWordString
|
||||
import com.tommasoberlose.anotherwidget.utils.openURI
|
||||
import com.tommasoberlose.anotherwidget.utils.toast
|
||||
import kotlinx.android.synthetic.main.activity_custom_date.*
|
||||
import kotlinx.android.synthetic.main.activity_custom_location.action_back
|
||||
import kotlinx.android.synthetic.main.activity_custom_location.list_view
|
||||
@ -78,7 +82,7 @@ class CustomDateActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
delay(200)
|
||||
val text = if (dateFormat != "") {
|
||||
var text = if (dateFormat != "") {
|
||||
try {
|
||||
SimpleDateFormat(dateFormat, Locale.getDefault()).format(DATE.time)
|
||||
} catch (e: Exception) {
|
||||
@ -88,6 +92,14 @@ class CustomDateActivity : AppCompatActivity() {
|
||||
"__"
|
||||
}
|
||||
|
||||
if (viewModel.isDateCapitalize.value == true) {
|
||||
text = text.getCapWordString()
|
||||
}
|
||||
|
||||
if (viewModel.isDateUppercase.value == true) {
|
||||
text = text.toUpperCase(Locale.getDefault())
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
action_save.isVisible = text != ERROR_STRING
|
||||
loader.visibility = View.INVISIBLE
|
||||
@ -96,6 +108,33 @@ class CustomDateActivity : AppCompatActivity() {
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.isDateCapitalize.observe(this, Observer {
|
||||
viewModel.dateInput.value = viewModel.dateInput.value
|
||||
updateCapitalizeUi()
|
||||
})
|
||||
|
||||
viewModel.isDateUppercase.observe(this, Observer {
|
||||
viewModel.dateInput.value = viewModel.dateInput.value
|
||||
updateCapitalizeUi()
|
||||
})
|
||||
}
|
||||
|
||||
private fun updateCapitalizeUi() {
|
||||
when {
|
||||
viewModel.isDateUppercase.value == true -> {
|
||||
action_capitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.round_publish))
|
||||
action_capitalize.alpha = 1f
|
||||
}
|
||||
viewModel.isDateCapitalize.value == true -> {
|
||||
action_capitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.ic_capitalize))
|
||||
action_capitalize.alpha = 1f
|
||||
}
|
||||
else -> {
|
||||
action_capitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.round_publish))
|
||||
action_capitalize.alpha = 0.3f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupListener() {
|
||||
@ -104,10 +143,36 @@ class CustomDateActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
action_save.setOnClickListener {
|
||||
Preferences.dateFormat = viewModel.dateInput.value ?: ""
|
||||
Preferences.blockingBulk {
|
||||
dateFormat = viewModel.dateInput.value ?: ""
|
||||
isDateCapitalize = viewModel.isDateCapitalize.value ?: true
|
||||
isDateUppercase = viewModel.isDateUppercase.value ?: false
|
||||
}
|
||||
finish()
|
||||
}
|
||||
|
||||
action_capitalize.setOnClickListener {
|
||||
when {
|
||||
viewModel.isDateUppercase.value == true -> {
|
||||
viewModel.isDateCapitalize.value = false
|
||||
viewModel.isDateUppercase.value = false
|
||||
}
|
||||
viewModel.isDateCapitalize.value == true -> {
|
||||
viewModel.isDateCapitalize.value = false
|
||||
viewModel.isDateUppercase.value = true
|
||||
}
|
||||
else -> {
|
||||
viewModel.isDateCapitalize.value = true
|
||||
viewModel.isDateUppercase.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
action_capitalize.setOnLongClickListener {
|
||||
toast(getString(R.string.action_capitalize_the_date))
|
||||
true
|
||||
}
|
||||
|
||||
action_date_format_info.setOnClickListener {
|
||||
openURI("https://developer.android.com/reference/java/text/SimpleDateFormat")
|
||||
}
|
||||
|
@ -0,0 +1,239 @@
|
||||
package com.tommasoberlose.anotherwidget.ui.activities
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.graphics.Typeface
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.provider.FontRequest
|
||||
import androidx.core.provider.FontsContractCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.chibatching.kotpref.blockingBulk
|
||||
import com.koolio.library.Font
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
|
||||
import com.tommasoberlose.anotherwidget.databinding.ActivityCustomFontBinding
|
||||
import com.tommasoberlose.anotherwidget.global.Constants
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.DateHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper
|
||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.CustomFontViewModel
|
||||
import kotlinx.android.synthetic.main.activity_choose_application.*
|
||||
import kotlinx.coroutines.*
|
||||
import net.idik.lib.slimadapter.SlimAdapter
|
||||
import net.idik.lib.slimadapter.diff.DefaultDiffCallback
|
||||
|
||||
|
||||
class CustomFontActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var adapter: SlimAdapter
|
||||
private lateinit var viewModel: CustomFontViewModel
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
viewModel = ViewModelProvider(this).get(CustomFontViewModel::class.java)
|
||||
val binding = DataBindingUtil.setContentView<ActivityCustomFontBinding>(
|
||||
this,
|
||||
R.layout.activity_custom_font
|
||||
)
|
||||
|
||||
list_view.setHasFixedSize(true)
|
||||
val mLayoutManager = LinearLayoutManager(this)
|
||||
list_view.layoutManager = mLayoutManager
|
||||
|
||||
adapter = SlimAdapter.create()
|
||||
adapter.enableDiff(object: DefaultDiffCallback() {
|
||||
override fun areItemsTheSame(oldItem: Any?, newItem: Any?): Boolean {
|
||||
return oldItem is Font && newItem is Font && oldItem.fontFamily == newItem.fontFamily
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: Any?, newItem: Any?): Boolean {
|
||||
return oldItem is Font && newItem is Font && oldItem.fontFamily == newItem.fontFamily
|
||||
}
|
||||
})
|
||||
adapter
|
||||
.register<String>(R.layout.list_item) { item, injector ->
|
||||
injector
|
||||
.text(R.id.text, item)
|
||||
.with<TextView>(R.id.text) {
|
||||
val googleSans: Typeface = when (Preferences.customFontVariant) {
|
||||
"100" -> Typeface.createFromAsset(this.assets, "fonts/google_sans_thin.ttf")
|
||||
"200" -> Typeface.createFromAsset(this.assets, "fonts/google_sans_light.ttf")
|
||||
"500" -> Typeface.createFromAsset(this.assets, "fonts/google_sans_medium.ttf")
|
||||
"700" -> Typeface.createFromAsset(this.assets, "fonts/google_sans_bold.ttf")
|
||||
"800" -> Typeface.createFromAsset(this.assets, "fonts/google_sans_black.ttf")
|
||||
else -> Typeface.createFromAsset(this.assets, "fonts/google_sans_regular.ttf")
|
||||
}
|
||||
it.typeface = googleSans
|
||||
}
|
||||
|
||||
injector.clicked(R.id.text) {
|
||||
val dialog = BottomSheetMenu<String>(this, header = item)
|
||||
listOf("100", "200", "regular", "500", "700", "800").forEachIndexed { index, s ->
|
||||
dialog.addItem(SettingsStringHelper.getVariantLabel(this, s), s)
|
||||
}
|
||||
dialog.addOnSelectItemListener { value ->
|
||||
saveGoogleSansFont(value)
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
.register<Font>(R.layout.list_item) { item, injector ->
|
||||
injector
|
||||
.text(R.id.text, item.fontFamily)
|
||||
.with<TextView>(R.id.text) {
|
||||
val request = FontRequest(
|
||||
"com.google.android.gms.fonts",
|
||||
"com.google.android.gms",
|
||||
item.queryString,
|
||||
R.array.com_google_android_gms_fonts_certs
|
||||
)
|
||||
|
||||
|
||||
val callback = object : FontsContractCompat.FontRequestCallback() {
|
||||
override fun onTypefaceRetrieved(typeface: Typeface) {
|
||||
it.typeface = typeface
|
||||
it.isVisible = true
|
||||
|
||||
it.measure(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
}
|
||||
|
||||
override fun onTypefaceRequestFailed(reason: Int) {
|
||||
it.isVisible = false
|
||||
it.layoutParams = it.layoutParams.apply {
|
||||
height = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val handlerThread = HandlerThread(item.fontFamily)
|
||||
handlerThread.start()
|
||||
val mHandler = Handler(handlerThread.looper)
|
||||
FontsContractCompat.requestFont(this, request, callback, mHandler)
|
||||
}
|
||||
|
||||
injector.clicked(R.id.text) {
|
||||
val dialog = BottomSheetMenu<Int>(this, header = item.fontFamily)
|
||||
if (item.fontVariants.isEmpty()) {
|
||||
dialog.addItem(SettingsStringHelper.getVariantLabel(this, "regular"), -1)
|
||||
} else {
|
||||
item.fontVariants.filter { !it.contains("italic") }
|
||||
.forEachIndexed { index, s ->
|
||||
dialog.addItem(SettingsStringHelper.getVariantLabel(this, s), index)
|
||||
}
|
||||
}
|
||||
dialog.addOnSelectItemListener { value ->
|
||||
saveFont(item, value)
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
.attachTo(list_view)
|
||||
|
||||
setupListener()
|
||||
subscribeUi(binding, viewModel)
|
||||
|
||||
search.requestFocus()
|
||||
}
|
||||
|
||||
private var filterJob: Job? = null
|
||||
|
||||
private fun subscribeUi(binding: ActivityCustomFontBinding, viewModel: CustomFontViewModel) {
|
||||
binding.viewModel = viewModel
|
||||
|
||||
viewModel.fontList.observe(this, Observer {
|
||||
updateList(list = it)
|
||||
loader.visibility = View.INVISIBLE
|
||||
})
|
||||
|
||||
viewModel.searchInput.observe(this, Observer { search ->
|
||||
updateList(search = search)
|
||||
})
|
||||
}
|
||||
|
||||
private fun updateList(
|
||||
list: ArrayList<Font>? = viewModel.fontList.value,
|
||||
search: String? = viewModel.searchInput.value
|
||||
) {
|
||||
loader.visibility = View.VISIBLE
|
||||
filterJob?.cancel()
|
||||
filterJob = lifecycleScope.launch(Dispatchers.IO) {
|
||||
if (list != null && list.isNotEmpty()) {
|
||||
delay(200)
|
||||
val filteredList: List<Any> = if (search == null || search == "") {
|
||||
listOf(getString(R.string.custom_font_subtitle_1)) + list.distinctBy { it.fontFamily }
|
||||
} else {
|
||||
(listOf(getString(R.string.custom_font_subtitle_1)) + list.distinctBy { it.fontFamily }).filter {
|
||||
when (it) {
|
||||
is Font -> {
|
||||
it.fontFamily.contains(search, true)
|
||||
}
|
||||
is String -> {
|
||||
it.contains(search, ignoreCase = true)
|
||||
}
|
||||
else -> {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}.sortedWith { el1, el2 ->
|
||||
if (el1 is Font && el2 is Font) {
|
||||
el1.fontFamily.compareTo(el2.fontFamily)
|
||||
} else if (el1 is Font && el2 is String) {
|
||||
el1.fontFamily.compareTo(el2)
|
||||
} else if (el1 is String && el2 is Font) {
|
||||
el1.compareTo(el2.fontFamily)
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
adapter.updateData(filteredList)
|
||||
loader.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupListener() {
|
||||
action_back.setOnClickListener {
|
||||
onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveFont(font: Font, variantPos: Int? = null) {
|
||||
val resultIntent = Intent()
|
||||
Preferences.blockingBulk {
|
||||
customFont = Constants.CUSTOM_FONT_DOWNLOADED
|
||||
customFontName = font.fontFamily
|
||||
customFontFile = if (variantPos != null && variantPos > -1) font.getQueryString(variantPos) else font.queryString
|
||||
customFontVariant = if (variantPos != null && variantPos > -1) font.fontVariants[variantPos] else "regular"
|
||||
}
|
||||
setResult(Activity.RESULT_OK, resultIntent)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun saveGoogleSansFont(variant: String) {
|
||||
val resultIntent = Intent()
|
||||
Preferences.blockingBulk {
|
||||
customFont = Constants.CUSTOM_FONT_GOOGLE_SANS
|
||||
customFontName = ""
|
||||
customFontFile = ""
|
||||
customFontVariant = variant
|
||||
}
|
||||
setResult(Activity.RESULT_OK, resultIntent)
|
||||
finish()
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@ import com.karumi.dexter.MultiplePermissionsReport
|
||||
import com.karumi.dexter.PermissionToken
|
||||
import com.karumi.dexter.listener.PermissionRequest
|
||||
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
||||
import com.tommasoberlose.anotherwidget.components.MaterialBottomSheetDialog
|
||||
import com.tommasoberlose.anotherwidget.databinding.ActivityChooseApplicationBinding
|
||||
import com.tommasoberlose.anotherwidget.databinding.ActivityCustomLocationBinding
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
@ -62,7 +63,11 @@ class CustomLocationActivity : AppCompatActivity() {
|
||||
injector
|
||||
.text(R.id.text, getString(R.string.custom_location_gps))
|
||||
.clicked(R.id.text) {
|
||||
requirePermission()
|
||||
MaterialBottomSheetDialog(this, message = getString(R.string.background_location_warning))
|
||||
.setPositiveButton(getString(android.R.string.ok)) {
|
||||
requirePermission()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
.register<Address>(R.layout.custom_location_item) { item, injector ->
|
||||
|
@ -26,6 +26,7 @@ import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.Navigation
|
||||
import com.chibatching.kotpref.Kotpref
|
||||
import com.google.android.material.badge.BadgeDrawable
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.karumi.dexter.Dexter
|
||||
@ -75,7 +76,9 @@ class MainActivity : AppCompatActivity() {
|
||||
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
|
||||
|
||||
controlExtras(intent)
|
||||
requirePermission()
|
||||
if (Preferences.showWallpaper) {
|
||||
requirePermission()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
|
@ -0,0 +1,141 @@
|
||||
package com.tommasoberlose.anotherwidget.ui.activities
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import android.content.Intent
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.ResolveInfo
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.bumptech.glide.Glide
|
||||
import com.tommasoberlose.anotherwidget.databinding.ActivityChooseApplicationBinding
|
||||
import com.tommasoberlose.anotherwidget.databinding.ActivityMusicPlayersFilterBinding
|
||||
import com.tommasoberlose.anotherwidget.global.Constants
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
|
||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.ChooseApplicationViewModel
|
||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.MusicPlayersFilterViewModel
|
||||
import kotlinx.android.synthetic.main.activity_choose_application.*
|
||||
import kotlinx.android.synthetic.main.activity_choose_application.list_view
|
||||
import kotlinx.coroutines.*
|
||||
import net.idik.lib.slimadapter.SlimAdapter
|
||||
import kotlin.Comparator as Comparator1
|
||||
|
||||
|
||||
class MusicPlayersFilterActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var adapter: SlimAdapter
|
||||
private lateinit var viewModel: MusicPlayersFilterViewModel
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
viewModel = ViewModelProvider(this).get(MusicPlayersFilterViewModel::class.java)
|
||||
val binding = DataBindingUtil.setContentView<ActivityMusicPlayersFilterBinding>(this, R.layout.activity_music_players_filter)
|
||||
|
||||
list_view.setHasFixedSize(true)
|
||||
val mLayoutManager = LinearLayoutManager(this)
|
||||
list_view.layoutManager = mLayoutManager
|
||||
|
||||
adapter = SlimAdapter.create()
|
||||
adapter
|
||||
.register<ResolveInfo>(R.layout.application_info_layout) { item, injector ->
|
||||
injector
|
||||
.text(R.id.text, item.loadLabel(viewModel.pm))
|
||||
.with<ImageView>(R.id.icon) {
|
||||
Glide
|
||||
.with(this)
|
||||
.load(item.loadIcon(viewModel.pm))
|
||||
.centerCrop()
|
||||
.into(it)
|
||||
}
|
||||
.visible(R.id.checkBox)
|
||||
.clicked(R.id.item) {
|
||||
toggleApp(item)
|
||||
adapter.notifyItemRangeChanged(0, adapter.data.size)
|
||||
}
|
||||
.clicked(R.id.checkBox) {
|
||||
toggleApp(item)
|
||||
adapter.notifyItemRangeChanged(0, adapter.data.size)
|
||||
}
|
||||
.checked(R.id.checkBox, MediaPlayerHelper.isMusicPlayerAccepted(item.activityInfo.packageName))
|
||||
}
|
||||
.attachTo(list_view)
|
||||
|
||||
setupListener()
|
||||
subscribeUi(binding, viewModel)
|
||||
|
||||
search.requestFocus()
|
||||
}
|
||||
|
||||
private var filterJob: Job? = null
|
||||
|
||||
private fun subscribeUi(binding: ActivityMusicPlayersFilterBinding, viewModel: MusicPlayersFilterViewModel) {
|
||||
binding.viewModel = viewModel
|
||||
|
||||
viewModel.appList.observe(this, Observer {
|
||||
updateList(list = it)
|
||||
loader.visibility = View.INVISIBLE
|
||||
})
|
||||
|
||||
viewModel.searchInput.observe(this, Observer { search ->
|
||||
updateList(search = search)
|
||||
})
|
||||
|
||||
viewModel.musicPlayersFilter.observe(this, {
|
||||
updateList()
|
||||
})
|
||||
}
|
||||
|
||||
private fun updateList(list: List<ResolveInfo>? = viewModel.appList.value, search: String? = viewModel.searchInput.value) {
|
||||
loader.visibility = View.VISIBLE
|
||||
filterJob?.cancel()
|
||||
filterJob = lifecycleScope.launch(Dispatchers.IO) {
|
||||
if (list != null && list.isNotEmpty()) {
|
||||
delay(200)
|
||||
val filteredList: List<ResolveInfo> = if (search == null || search == "") {
|
||||
list
|
||||
} else {
|
||||
list.filter {
|
||||
it.loadLabel(viewModel.pm).contains(search, true)
|
||||
}
|
||||
}.sortedWith { app1, app2 ->
|
||||
if (MediaPlayerHelper.isMusicPlayerAccepted(app1.activityInfo.packageName) && MediaPlayerHelper.isMusicPlayerAccepted(app2.activityInfo.packageName)) {
|
||||
app1.loadLabel(viewModel.pm).toString().compareTo(app2.loadLabel(viewModel.pm).toString(), ignoreCase = true)
|
||||
} else if (MediaPlayerHelper.isMusicPlayerAccepted(app1.activityInfo.packageName)) {
|
||||
-1
|
||||
} else if (MediaPlayerHelper.isMusicPlayerAccepted(app2.activityInfo.packageName)) {
|
||||
1
|
||||
} else {
|
||||
app1.loadLabel(viewModel.pm).toString().compareTo(app2.loadLabel(viewModel.pm).toString(), ignoreCase = true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
adapter.updateData(filteredList)
|
||||
loader.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupListener() {
|
||||
action_back.setOnClickListener {
|
||||
onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleApp(app: ResolveInfo) {
|
||||
MediaPlayerHelper.toggleMusicPlayerFilter(app.activityInfo.packageName)
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import android.location.Address
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
@ -45,8 +46,17 @@ class SupportDevActivity : AppCompatActivity(), PurchasesUpdatedListener {
|
||||
adapter = SlimAdapter.create()
|
||||
adapter
|
||||
.register<SkuDetails>(R.layout.inapp_product_layout) { item, injector ->
|
||||
item.sku
|
||||
injector
|
||||
.text(R.id.product_title, item.title.replace("(Another Widget)", ""))
|
||||
.with<TextView>(R.id.product_title) {
|
||||
it.text = when (item.sku) {
|
||||
"donation_coffee" -> getString(R.string.donation_coffee)
|
||||
"donation_donuts" -> getString(R.string.donation_donuts)
|
||||
"donation_breakfast" -> getString(R.string.donation_breakfast)
|
||||
"donation_lunch" -> getString(R.string.donation_lunch)
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
.text(R.id.product_price, item.price)
|
||||
.clicked(R.id.item) {
|
||||
viewModel.purchase(this, item)
|
||||
|
@ -1,42 +1,183 @@
|
||||
package com.tommasoberlose.anotherwidget.ui.activities
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.os.Build
|
||||
import android.content.Intent
|
||||
import android.content.pm.ResolveInfo
|
||||
import android.os.Bundle
|
||||
import android.text.Html
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.bumptech.glide.Glide
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
|
||||
import com.tommasoberlose.anotherwidget.components.BottomSheetWeatherProviderSettings
|
||||
import com.tommasoberlose.anotherwidget.databinding.ActivityChooseApplicationBinding
|
||||
import com.tommasoberlose.anotherwidget.databinding.ActivityWeatherProviderBinding
|
||||
import com.tommasoberlose.anotherwidget.global.Constants
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
||||
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
|
||||
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
|
||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.ChooseApplicationViewModel
|
||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
|
||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.WeatherProviderViewModel
|
||||
import com.tommasoberlose.anotherwidget.utils.collapse
|
||||
import com.tommasoberlose.anotherwidget.utils.expand
|
||||
import com.tommasoberlose.anotherwidget.utils.openURI
|
||||
import com.tommasoberlose.anotherwidget.utils.reveal
|
||||
import kotlinx.android.synthetic.main.activity_weather_provider.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.idik.lib.slimadapter.SlimAdapter
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
class WeatherProviderActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var adapter: SlimAdapter
|
||||
private lateinit var viewModel: WeatherProviderViewModel
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_weather_provider)
|
||||
|
||||
viewModel = ViewModelProvider(this).get(WeatherProviderViewModel::class.java)
|
||||
|
||||
list_view.setHasFixedSize(true)
|
||||
val mLayoutManager = LinearLayoutManager(this)
|
||||
list_view.layoutManager = mLayoutManager
|
||||
|
||||
adapter = SlimAdapter.create()
|
||||
adapter
|
||||
.register<Constants.WeatherProvider>(R.layout.weather_provider_list_item) { provider, injector ->
|
||||
injector
|
||||
.text(R.id.text, WeatherHelper.getProviderName(this, provider))
|
||||
.clicked(R.id.item) {
|
||||
val oldValue = Preferences.weatherProvider
|
||||
Preferences.weatherProvider = provider.value
|
||||
updateListItem(oldValue)
|
||||
updateListItem()
|
||||
loader.isVisible = true
|
||||
|
||||
lifecycleScope.launch {
|
||||
WeatherHelper.updateWeather(this@WeatherProviderActivity)
|
||||
}
|
||||
}
|
||||
.clicked(R.id.radioButton) {
|
||||
val oldValue = Preferences.weatherProvider
|
||||
Preferences.weatherProvider = provider.value
|
||||
updateListItem(oldValue)
|
||||
updateListItem()
|
||||
loader.isVisible = true
|
||||
|
||||
lifecycleScope.launch {
|
||||
WeatherHelper.updateWeather(this@WeatherProviderActivity)
|
||||
}
|
||||
}
|
||||
.checked(R.id.radioButton, provider.value == Preferences.weatherProvider)
|
||||
.with<TextView>(R.id.text2) {
|
||||
if (WeatherHelper.isKeyRequired(provider)) {
|
||||
it.text = getString(R.string.api_key_required_message)
|
||||
}
|
||||
|
||||
if (provider == Constants.WeatherProvider.WEATHER_GOV) {
|
||||
it.text = getString(R.string.us_only_message)
|
||||
}
|
||||
|
||||
if (provider == Constants.WeatherProvider.YR) {
|
||||
it.text = getString(R.string.celsius_only_message)
|
||||
}
|
||||
}
|
||||
.clicked(R.id.action_configure) {
|
||||
BottomSheetWeatherProviderSettings(this) {
|
||||
lifecycleScope.launch {
|
||||
loader.isVisible = true
|
||||
WeatherHelper.updateWeather(this@WeatherProviderActivity)
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
.visibility(R.id.action_configure, if (/*WeatherHelper.isKeyRequired(provider) && */provider.value == Preferences.weatherProvider) View.VISIBLE else View.GONE)
|
||||
.with<TextView>(R.id.provider_error) {
|
||||
if (Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-") {
|
||||
it.text = Preferences.weatherProviderError
|
||||
it.isVisible = provider.value == Preferences.weatherProvider
|
||||
} else if (Preferences.weatherProviderLocationError != "") {
|
||||
it.text = Preferences.weatherProviderLocationError
|
||||
it.isVisible = provider.value == Preferences.weatherProvider
|
||||
} else {
|
||||
it.isVisible = false
|
||||
}
|
||||
}
|
||||
.image(R.id.action_configure, ContextCompat.getDrawable(this, if (WeatherHelper.isKeyRequired(provider)) R.drawable.round_settings else R.drawable.outline_info_white))
|
||||
}.attachTo(list_view)
|
||||
|
||||
adapter.updateData(
|
||||
Constants.WeatherProvider.values().asList()
|
||||
.filter { it != Constants.WeatherProvider.HERE }
|
||||
.filter { it != Constants.WeatherProvider.ACCUWEATHER }
|
||||
)
|
||||
|
||||
setupListener()
|
||||
subscribeUi(viewModel)
|
||||
}
|
||||
|
||||
private fun subscribeUi(viewModel: WeatherProviderViewModel) {
|
||||
viewModel.weatherProviderError.observe(this) {
|
||||
updateListItem()
|
||||
}
|
||||
|
||||
viewModel.weatherProviderLocationError.observe(this) {
|
||||
updateListItem()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateListItem(provider: Int = Preferences.weatherProvider) {
|
||||
(adapter.data).forEachIndexed { index, item ->
|
||||
if (item is Constants.WeatherProvider && item.value == provider) {
|
||||
adapter.notifyItemChanged(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupListener() {
|
||||
action_back.setOnClickListener {
|
||||
onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
action_save.setOnClickListener {
|
||||
Preferences.weatherProviderApi = api_key.editText?.text.toString()
|
||||
setResult(Activity.RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
override fun onBackPressed() {
|
||||
setResult(Activity.RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
|
||||
action_open_provider.setOnClickListener {
|
||||
openURI("https://home.openweathermap.org/users/sign_up")
|
||||
}
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
EventBus.getDefault().register(this)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
last_info.text = Html.fromHtml(getString(R.string.api_key_info_all_set), Html.FROM_HTML_MODE_LEGACY)
|
||||
} else {
|
||||
last_info.text = Html.fromHtml(getString(R.string.api_key_info_all_set))
|
||||
override fun onPause() {
|
||||
EventBus.getDefault().unregister(this)
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onMessageEvent(ignore: MainFragment.UpdateUiMessageEvent?) {
|
||||
loader.isVisible = Preferences.weatherProviderError == "-"
|
||||
if (Preferences.weatherProviderError == "" && Preferences.weatherProviderLocationError == "") {
|
||||
Snackbar.make(list_view, getString(R.string.settings_weather_provider_api_key_subtitle_all_set), Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
api_key.editText?.setText(Preferences.weatherProviderApi)
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.provider.CalendarContract
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@ -31,16 +31,15 @@ import com.tommasoberlose.anotherwidget.ui.activities.ChooseApplicationActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
|
||||
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.DateHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.IntentHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.CustomDateActivity
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import com.tommasoberlose.anotherwidget.utils.isDefaultSet
|
||||
import com.tommasoberlose.anotherwidget.utils.toast
|
||||
import kotlinx.android.synthetic.main.fragment_calendar_settings.*
|
||||
import kotlinx.android.synthetic.main.fragment_calendar_settings.scrollView
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.*
|
||||
import kotlin.Comparator
|
||||
|
||||
class CalendarTabFragment : Fragment() {
|
||||
@ -74,6 +73,11 @@ class CalendarTabFragment : Fragment() {
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
show_all_day_toggle.isChecked = Preferences.calendarAllDay
|
||||
show_only_busy_events_toggle.isChecked = Preferences.showOnlyBusyEvents
|
||||
show_diff_time_toggle.isChecked = Preferences.showDiffTime
|
||||
show_multiple_events_toggle.isChecked = Preferences.showNextEvent
|
||||
|
||||
setupListener()
|
||||
}
|
||||
|
||||
@ -81,6 +85,9 @@ class CalendarTabFragment : Fragment() {
|
||||
binding: FragmentCalendarSettingsBinding,
|
||||
viewModel: MainViewModel
|
||||
) {
|
||||
binding.isCalendarEnabled = Preferences.showEvents
|
||||
binding.isDiffEnabled = Preferences.showDiffTime || !Preferences.showEvents
|
||||
|
||||
viewModel.showEvents.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
binding.isCalendarEnabled = it
|
||||
@ -90,23 +97,17 @@ class CalendarTabFragment : Fragment() {
|
||||
} else {
|
||||
CalendarHelper.removeEventUpdatesAndroidN(requireContext())
|
||||
}
|
||||
binding.isDiffEnabled = Preferences.showDiffTime || !it
|
||||
}
|
||||
checkReadEventsPermission()
|
||||
updateCalendar()
|
||||
})
|
||||
|
||||
viewModel.calendarAllDay.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
all_day_label?.text =
|
||||
if (it) getString(R.string.settings_all_day_subtitle_visible) else getString(R.string.settings_all_day_subtitle_gone)
|
||||
if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
|
||||
}
|
||||
checkReadEventsPermission()
|
||||
})
|
||||
|
||||
viewModel.showDeclinedEvents.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
show_declined_events_label?.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
|
||||
}
|
||||
checkReadEventsPermission()
|
||||
})
|
||||
|
||||
viewModel.secondRowInformation.observe(viewLifecycleOwner, Observer {
|
||||
@ -118,6 +119,18 @@ class CalendarTabFragment : Fragment() {
|
||||
viewModel.showDiffTime.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
show_diff_time_label?.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
|
||||
binding.isDiffEnabled = it || !Preferences.showEvents
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.widgetUpdateFrequency.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
widget_update_frequency_label?.text = when (it) {
|
||||
Constants.WidgetUpdateFrequency.HIGH.value -> getString(R.string.settings_widget_update_frequency_high)
|
||||
Constants.WidgetUpdateFrequency.DEFAULT.value -> getString(R.string.settings_widget_update_frequency_default)
|
||||
Constants.WidgetUpdateFrequency.LOW.value -> getString(R.string.settings_widget_update_frequency_low)
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -125,22 +138,30 @@ class CalendarTabFragment : Fragment() {
|
||||
maintainScrollPosition {
|
||||
show_until_label?.text = getString(SettingsStringHelper.getShowUntilString(it))
|
||||
}
|
||||
checkReadEventsPermission()
|
||||
updateCalendar()
|
||||
})
|
||||
|
||||
viewModel.showNextEvent.observe(viewLifecycleOwner, Observer {
|
||||
show_multiple_events_label?.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
|
||||
})
|
||||
|
||||
viewModel.dateFormat.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
date_format_label?.text = DateHelper.getDateText(requireContext(), Calendar.getInstance())
|
||||
show_multiple_events_label?.text =
|
||||
if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.calendarAppName.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
calendar_app_label?.text = if (it != "") it else getString(R.string.default_calendar_app)
|
||||
calendar_app_label?.text = when {
|
||||
Preferences.clockAppName != "" -> Preferences.clockAppName
|
||||
else -> {
|
||||
if (IntentHelper.getCalendarIntent(requireContext()).isDefaultSet(requireContext())) {
|
||||
getString(
|
||||
R.string.default_calendar_app
|
||||
)
|
||||
} else {
|
||||
getString(R.string.nothing)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -205,14 +226,14 @@ class CalendarTabFragment : Fragment() {
|
||||
}
|
||||
|
||||
dialog.addItem(
|
||||
if (calendarSelectorList[index].name == calendarSelectorList[index].accountName) getString(R.string.account_events) else calendarSelectorList[index].name,
|
||||
if (calendarSelectorList[index].name == calendarSelectorList[index].accountName) getString(R.string.main_calendar) else calendarSelectorList[index].name,
|
||||
calendarSelectorList[index].id
|
||||
)
|
||||
}
|
||||
|
||||
dialog.addOnMultipleSelectItemListener { values ->
|
||||
CalendarHelper.filterCalendar(calendarSelectorList.map { it.id }.filter { !values.contains(it) })
|
||||
checkReadEventsPermission()
|
||||
updateCalendar()
|
||||
}.show()
|
||||
} else {
|
||||
activity?.toast(getString(R.string.calendar_settings_list_error))
|
||||
@ -221,44 +242,100 @@ class CalendarTabFragment : Fragment() {
|
||||
|
||||
action_show_all_day.setOnClickListener {
|
||||
if (Preferences.showEvents) {
|
||||
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_all_day_title)).setSelectedValue(Preferences.calendarAllDay)
|
||||
.addItem(getString(R.string.settings_all_day_subtitle_visible), true)
|
||||
.addItem(getString(R.string.settings_all_day_subtitle_gone), false)
|
||||
.addOnSelectItemListener { value ->
|
||||
Preferences.calendarAllDay = value
|
||||
}.show()
|
||||
show_all_day_toggle.isChecked = !show_all_day_toggle.isChecked
|
||||
}
|
||||
}
|
||||
|
||||
action_show_declined_events.setOnClickListener {
|
||||
show_all_day_toggle.setOnCheckedChangeListener { _, isChecked ->
|
||||
if (Preferences.showEvents) {
|
||||
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_show_declined_events_title)).setSelectedValue(Preferences.showDeclinedEvents)
|
||||
.addItem(getString(R.string.settings_visible), true)
|
||||
.addItem(getString(R.string.settings_not_visible), false)
|
||||
.addOnSelectItemListener { value ->
|
||||
Preferences.showDeclinedEvents = value
|
||||
}.show()
|
||||
Preferences.calendarAllDay = isChecked
|
||||
updateCalendar()
|
||||
}
|
||||
}
|
||||
|
||||
action_change_attendee_filter.setOnClickListener {
|
||||
if (Preferences.showEvents) {
|
||||
val selectedValues = emptyList<Int>().toMutableList()
|
||||
if (Preferences.showDeclinedEvents) {
|
||||
selectedValues.add(CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED)
|
||||
}
|
||||
if (Preferences.showInvitedEvents) {
|
||||
selectedValues.add(CalendarContract.Attendees.ATTENDEE_STATUS_INVITED)
|
||||
}
|
||||
if (Preferences.showAcceptedEvents) {
|
||||
selectedValues.add(CalendarContract.Attendees.ATTENDEE_STATUS_ACCEPTED)
|
||||
}
|
||||
|
||||
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_attendee_status_title), isMultiSelection = true)
|
||||
.setSelectedValues(selectedValues)
|
||||
|
||||
dialog.addItem(
|
||||
getString(R.string.attendee_status_invited),
|
||||
CalendarContract.Attendees.ATTENDEE_STATUS_INVITED
|
||||
)
|
||||
dialog.addItem(
|
||||
getString(R.string.attendee_status_accepted),
|
||||
CalendarContract.Attendees.ATTENDEE_STATUS_ACCEPTED
|
||||
)
|
||||
dialog.addItem(
|
||||
getString(R.string.attendee_status_declined),
|
||||
CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED
|
||||
)
|
||||
|
||||
dialog.addOnMultipleSelectItemListener { values ->
|
||||
Preferences.showDeclinedEvents = values.contains(CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED)
|
||||
Preferences.showAcceptedEvents = values.contains(CalendarContract.Attendees.ATTENDEE_STATUS_ACCEPTED)
|
||||
Preferences.showInvitedEvents = values.contains(CalendarContract.Attendees.ATTENDEE_STATUS_INVITED)
|
||||
updateCalendar()
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
|
||||
action_show_only_busy_events.setOnClickListener {
|
||||
if (Preferences.showEvents) {
|
||||
show_only_busy_events_toggle.isChecked = !show_only_busy_events_toggle.isChecked
|
||||
}
|
||||
}
|
||||
|
||||
show_only_busy_events_toggle.setOnCheckedChangeListener { _, isChecked ->
|
||||
if (Preferences.showEvents) {
|
||||
Preferences.showOnlyBusyEvents = isChecked
|
||||
updateCalendar()
|
||||
}
|
||||
}
|
||||
|
||||
action_show_multiple_events.setOnClickListener {
|
||||
if (Preferences.showEvents) {
|
||||
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_show_multiple_events_title)).setSelectedValue(Preferences.showNextEvent)
|
||||
.addItem(getString(R.string.settings_visible), true)
|
||||
.addItem(getString(R.string.settings_not_visible), false)
|
||||
.addOnSelectItemListener { value ->
|
||||
Preferences.showNextEvent = value
|
||||
}.show()
|
||||
show_multiple_events_toggle.isChecked = !show_multiple_events_toggle.isChecked
|
||||
}
|
||||
}
|
||||
|
||||
show_multiple_events_toggle.setOnCheckedChangeListener { _, isChecked ->
|
||||
if (Preferences.showEvents) {
|
||||
Preferences.showNextEvent = isChecked
|
||||
}
|
||||
}
|
||||
|
||||
action_show_diff_time.setOnClickListener {
|
||||
if (Preferences.showEvents) {
|
||||
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_show_diff_time_title)).setSelectedValue(Preferences.showDiffTime)
|
||||
.addItem(getString(R.string.settings_visible), true)
|
||||
.addItem(getString(R.string.settings_not_visible), false)
|
||||
show_diff_time_toggle.isChecked = !show_diff_time_toggle.isChecked
|
||||
}
|
||||
}
|
||||
|
||||
show_diff_time_toggle.setOnCheckedChangeListener { _, isChecked ->
|
||||
if (Preferences.showEvents) {
|
||||
Preferences.showDiffTime = isChecked
|
||||
}
|
||||
}
|
||||
|
||||
action_widget_update_frequency.setOnClickListener {
|
||||
if (Preferences.showEvents && Preferences.showDiffTime) {
|
||||
BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_widget_update_frequency_title), message = getString(R.string.settings_widget_update_frequency_subtitle)).setSelectedValue(Preferences.widgetUpdateFrequency)
|
||||
.addItem(getString(R.string.settings_widget_update_frequency_high), Constants.WidgetUpdateFrequency.HIGH.value)
|
||||
.addItem(getString(R.string.settings_widget_update_frequency_default), Constants.WidgetUpdateFrequency.DEFAULT.value)
|
||||
.addItem(getString(R.string.settings_widget_update_frequency_low), Constants.WidgetUpdateFrequency.LOW.value)
|
||||
.addOnSelectItemListener { value ->
|
||||
Preferences.showDiffTime = value
|
||||
Preferences.widgetUpdateFrequency = value
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
@ -278,7 +355,7 @@ class CalendarTabFragment : Fragment() {
|
||||
action_show_until.setOnClickListener {
|
||||
if (Preferences.showEvents) {
|
||||
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_show_until_title)).setSelectedValue(Preferences.showUntil)
|
||||
intArrayOf(6,7,0,1,2).forEach {
|
||||
intArrayOf(6,7,0,1,2,3, 4, 5).forEach {
|
||||
dialog.addItem(getString(SettingsStringHelper.getShowUntilString(it)), it)
|
||||
}
|
||||
dialog.addOnSelectItemListener { value ->
|
||||
@ -287,27 +364,6 @@ class CalendarTabFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
action_date_format.setOnClickListener {
|
||||
if (Preferences.showEvents) {
|
||||
val now = Calendar.getInstance()
|
||||
val dialog = BottomSheetMenu<String>(requireContext(), header = getString(R.string.settings_date_format_title)).setSelectedValue(Preferences.dateFormat)
|
||||
|
||||
dialog.addItem(DateHelper.getDefaultDateText(requireContext(), now), "")
|
||||
if (Preferences.dateFormat != "") {
|
||||
dialog.addItem(DateHelper.getDateText(requireContext(), now), Preferences.dateFormat)
|
||||
}
|
||||
dialog.addItem(getString(R.string.custom_date_format), "-")
|
||||
|
||||
dialog.addOnSelectItemListener { value ->
|
||||
if (value == "-") {
|
||||
startActivity(Intent(requireContext(), CustomDateActivity::class.java))
|
||||
} else {
|
||||
Preferences.dateFormat = value
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
|
||||
action_open_event_details.setOnClickListener {
|
||||
if (Preferences.showEvents) {
|
||||
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_event_app_title)).setSelectedValue(Preferences.openEventDetails)
|
||||
@ -328,7 +384,6 @@ class CalendarTabFragment : Fragment() {
|
||||
if (activity?.checkGrantedPermission(Manifest.permission.READ_CALENDAR) == true) {
|
||||
show_events_label?.text = if (showEvents) getString(R.string.show_events_visible) else getString(R.string.show_events_not_visible)
|
||||
read_calendar_permission_alert?.isVisible = false
|
||||
CalendarHelper.updateEventList(requireContext())
|
||||
} else {
|
||||
show_events_label?.text = if (showEvents) getString(R.string.description_permission_calendar) else getString(R.string.show_events_not_visible)
|
||||
read_calendar_permission_alert?.isVisible = showEvents
|
||||
@ -338,6 +393,12 @@ class CalendarTabFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateCalendar() {
|
||||
if (activity?.checkGrantedPermission(Manifest.permission.READ_CALENDAR) == true) {
|
||||
CalendarHelper.updateEventList(requireContext())
|
||||
}
|
||||
}
|
||||
|
||||
private fun requirePermission() {
|
||||
Dexter.withContext(requireContext())
|
||||
.withPermissions(
|
||||
@ -385,11 +446,11 @@ class CalendarTabFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun maintainScrollPosition(callback: () -> Unit) {
|
||||
val scrollPosition = scrollView.scrollY
|
||||
scrollView.isScrollable = false
|
||||
callback.invoke()
|
||||
lifecycleScope.launch {
|
||||
delay(200)
|
||||
scrollView.smoothScrollTo(0, scrollPosition)
|
||||
scrollView.isScrollable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.text.format.DateFormat
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@ -21,6 +23,7 @@ import com.chibatching.kotpref.bulk
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.components.BottomSheetColorPicker
|
||||
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
|
||||
import com.tommasoberlose.anotherwidget.components.FixedFocusScrollView
|
||||
import com.tommasoberlose.anotherwidget.databinding.FragmentClockSettingsBinding
|
||||
import com.tommasoberlose.anotherwidget.global.Constants
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
@ -29,9 +32,12 @@ import com.tommasoberlose.anotherwidget.helpers.AlarmHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toHexValue
|
||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toIntValue
|
||||
import com.tommasoberlose.anotherwidget.helpers.IntentHelper
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.ChooseApplicationActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
|
||||
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
|
||||
import com.tommasoberlose.anotherwidget.utils.isDefaultSet
|
||||
import kotlinx.android.synthetic.main.fragment_clock_settings.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
@ -48,6 +54,7 @@ class ClockTabFragment : Fragment() {
|
||||
|
||||
private lateinit var viewModel: MainViewModel
|
||||
private lateinit var colors: IntArray
|
||||
private lateinit var binding: FragmentClockSettingsBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -59,7 +66,7 @@ class ClockTabFragment : Fragment() {
|
||||
): View {
|
||||
|
||||
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
|
||||
val binding = DataBindingUtil.inflate<FragmentClockSettingsBinding>(inflater, R.layout.fragment_clock_settings, container, false)
|
||||
binding = DataBindingUtil.inflate<FragmentClockSettingsBinding>(inflater, R.layout.fragment_clock_settings, container, false)
|
||||
|
||||
subscribeUi(binding, viewModel)
|
||||
|
||||
@ -72,6 +79,8 @@ class ClockTabFragment : Fragment() {
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
ampm_indicator_toggle.isChecked = Preferences.showAMPMIndicator
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val lazyColors = requireContext().resources.getIntArray(R.array.material_colors)
|
||||
withContext(Dispatchers.Main) {
|
||||
@ -85,6 +94,10 @@ class ClockTabFragment : Fragment() {
|
||||
binding: FragmentClockSettingsBinding,
|
||||
viewModel: MainViewModel
|
||||
) {
|
||||
binding.isClockVisible = Preferences.showClock
|
||||
binding.is24Format = DateFormat.is24HourFormat(requireContext())
|
||||
binding.isDarkModeEnabled = activity?.isDarkTheme() == true
|
||||
|
||||
viewModel.showBigClockWarning.observe(viewLifecycleOwner, Observer {
|
||||
large_clock_warning?.isVisible = it
|
||||
small_clock_warning?.isVisible = !it
|
||||
@ -104,13 +117,30 @@ class ClockTabFragment : Fragment() {
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.showAMPMIndicator.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
ampm_indicator_label?.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.clockTextColor.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
if (Preferences.clockTextAlpha == "00") {
|
||||
clock_text_color_label?.text = getString(R.string.transparent)
|
||||
} else {
|
||||
clock_text_color_label?.text =
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getClockFontColor())).toUpperCase()
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true))).toUpperCase()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.clockTextColorDark.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
if (Preferences.clockTextAlphaDark == "00") {
|
||||
clock_text_color_label?.text = getString(R.string.transparent)
|
||||
} else {
|
||||
clock_text_color_label?.text =
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true))).toUpperCase()
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -121,7 +151,18 @@ class ClockTabFragment : Fragment() {
|
||||
clock_text_color_label?.text = getString(R.string.transparent)
|
||||
} else {
|
||||
clock_text_color_label?.text =
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getClockFontColor())).toUpperCase()
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true))).toUpperCase()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.clockTextAlphaDark.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
if (Preferences.clockTextAlphaDark == "00") {
|
||||
clock_text_color_label?.text = getString(R.string.transparent)
|
||||
} else {
|
||||
clock_text_color_label?.text =
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true))).toUpperCase()
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -139,8 +180,18 @@ class ClockTabFragment : Fragment() {
|
||||
|
||||
viewModel.clockAppName.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
clock_app_label?.text =
|
||||
if (Preferences.clockAppName != "") Preferences.clockAppName else getString(R.string.default_clock_app)
|
||||
clock_app_label?.text = when {
|
||||
Preferences.clockAppName != "" -> Preferences.clockAppName
|
||||
else -> {
|
||||
if (IntentHelper.getClockIntent(requireContext()).isDefaultSet(requireContext())) {
|
||||
getString(
|
||||
R.string.default_clock_app
|
||||
)
|
||||
} else {
|
||||
getString(R.string.nothing)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -159,48 +210,97 @@ class ClockTabFragment : Fragment() {
|
||||
}
|
||||
|
||||
action_clock_text_size.setOnClickListener {
|
||||
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.settings_clock_text_size_title)).setSelectedValue(Preferences.clockTextSize)
|
||||
(46 downTo 12).filter { it % 2 == 0 }.forEach {
|
||||
dialog.addItem("${it}sp", it.toFloat())
|
||||
if (Preferences.showClock) {
|
||||
val dialog = BottomSheetMenu<Float>(
|
||||
requireContext(),
|
||||
header = getString(R.string.settings_clock_text_size_title)
|
||||
).setSelectedValue(Preferences.clockTextSize)
|
||||
(46 downTo 12).filter { it % 2 == 0 }.forEach {
|
||||
dialog.addItem("${it}sp", it.toFloat())
|
||||
}
|
||||
dialog.addOnSelectItemListener { value ->
|
||||
Preferences.clockTextSize = value
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
|
||||
action_ampm_indicator_size.setOnClickListener {
|
||||
if (Preferences.showClock) {
|
||||
ampm_indicator_toggle.isChecked = !ampm_indicator_toggle.isChecked
|
||||
}
|
||||
}
|
||||
|
||||
ampm_indicator_toggle.setOnCheckedChangeListener { _, isChecked ->
|
||||
if (Preferences.showClock) {
|
||||
Preferences.showAMPMIndicator = isChecked
|
||||
}
|
||||
dialog.addOnSelectItemListener { value ->
|
||||
Preferences.clockTextSize = value
|
||||
}.show()
|
||||
}
|
||||
|
||||
action_clock_text_color.setOnClickListener {
|
||||
BottomSheetColorPicker(requireContext(),
|
||||
colors = colors,
|
||||
header = getString(R.string.settings_font_color_title),
|
||||
getSelected = ColorHelper::getClockFontColorRgb,
|
||||
onColorSelected = { color: Int ->
|
||||
val colorString = Integer.toHexString(color)
|
||||
Preferences.clockTextColor = "#" + if (colorString.length > 6) colorString.substring(2) else colorString
|
||||
},
|
||||
showAlphaSelector = true,
|
||||
alpha = Preferences.clockTextAlpha.toIntValue(),
|
||||
onAlphaChangeListener = { alpha ->
|
||||
Preferences.clockTextAlpha = alpha.toHexValue()
|
||||
}
|
||||
).show()
|
||||
if (Preferences.showClock) {
|
||||
BottomSheetColorPicker(requireContext(),
|
||||
colors = colors,
|
||||
header = getString(R.string.settings_font_color_title),
|
||||
getSelected = { ColorHelper.getClockFontColorRgb(activity?.isDarkTheme() == true) },
|
||||
onColorSelected = { color: Int ->
|
||||
val colorString = Integer.toHexString(color)
|
||||
if (activity?.isDarkTheme() == true) {
|
||||
Preferences.clockTextColorDark =
|
||||
"#" + if (colorString.length > 6) colorString.substring(2) else colorString
|
||||
} else {
|
||||
Preferences.clockTextColor =
|
||||
"#" + if (colorString.length > 6) colorString.substring(2) else colorString
|
||||
}
|
||||
},
|
||||
showAlphaSelector = true,
|
||||
alpha = if (activity?.isDarkTheme() == true) Preferences.clockTextAlphaDark.toIntValue() else Preferences.clockTextAlpha.toIntValue(),
|
||||
onAlphaChangeListener = { alpha ->
|
||||
if (activity?.isDarkTheme() == true) {
|
||||
Preferences.clockTextAlphaDark = alpha.toHexValue()
|
||||
} else {
|
||||
Preferences.clockTextAlpha = alpha.toHexValue()
|
||||
}
|
||||
}
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
action_clock_bottom_margin_size.setOnClickListener {
|
||||
BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_clock_bottom_margin_title)).setSelectedValue(Preferences.clockBottomMargin)
|
||||
.addItem(getString(R.string.settings_clock_bottom_margin_subtitle_none), Constants.ClockBottomMargin.NONE.value)
|
||||
.addItem(getString(R.string.settings_clock_bottom_margin_subtitle_small), Constants.ClockBottomMargin.SMALL.value)
|
||||
.addItem(getString(R.string.settings_clock_bottom_margin_subtitle_medium), Constants.ClockBottomMargin.MEDIUM.value)
|
||||
.addItem(getString(R.string.settings_clock_bottom_margin_subtitle_large), Constants.ClockBottomMargin.LARGE.value)
|
||||
.addOnSelectItemListener { value ->
|
||||
Preferences.clockBottomMargin = value
|
||||
}.show()
|
||||
if (Preferences.showClock) {
|
||||
BottomSheetMenu<Int>(
|
||||
requireContext(),
|
||||
header = getString(R.string.settings_clock_bottom_margin_title)
|
||||
).setSelectedValue(Preferences.clockBottomMargin)
|
||||
.addItem(
|
||||
getString(R.string.settings_clock_bottom_margin_subtitle_none),
|
||||
Constants.ClockBottomMargin.NONE.value
|
||||
)
|
||||
.addItem(
|
||||
getString(R.string.settings_clock_bottom_margin_subtitle_small),
|
||||
Constants.ClockBottomMargin.SMALL.value
|
||||
)
|
||||
.addItem(
|
||||
getString(R.string.settings_clock_bottom_margin_subtitle_medium),
|
||||
Constants.ClockBottomMargin.MEDIUM.value
|
||||
)
|
||||
.addItem(
|
||||
getString(R.string.settings_clock_bottom_margin_subtitle_large),
|
||||
Constants.ClockBottomMargin.LARGE.value
|
||||
)
|
||||
.addOnSelectItemListener { value ->
|
||||
Preferences.clockBottomMargin = value
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
|
||||
action_clock_app.setOnClickListener {
|
||||
if (Preferences.showClock) {
|
||||
startActivityForResult(Intent(requireContext(), ChooseApplicationActivity::class.java),
|
||||
RequestCode.CLOCK_APP_REQUEST_CODE.code
|
||||
)
|
||||
if (Preferences.showClock) {
|
||||
startActivityForResult(
|
||||
Intent(requireContext(), ChooseApplicationActivity::class.java),
|
||||
RequestCode.CLOCK_APP_REQUEST_CODE.code
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -215,12 +315,17 @@ class ClockTabFragment : Fragment() {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
binding.is24Format = DateFormat.is24HourFormat(requireContext())
|
||||
super.onResume()
|
||||
}
|
||||
|
||||
private fun maintainScrollPosition(callback: () -> Unit) {
|
||||
val scrollPosition = scrollView.scrollY
|
||||
scrollView.isScrollable = false
|
||||
callback.invoke()
|
||||
lifecycleScope.launch {
|
||||
delay(200)
|
||||
scrollView.smoothScrollTo(0, scrollPosition)
|
||||
scrollView.isScrollable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,32 +3,52 @@ package com.tommasoberlose.anotherwidget.ui.fragments
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.graphics.Typeface
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.core.provider.FontRequest
|
||||
import androidx.core.provider.FontsContractCompat
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.chibatching.kotpref.blockingBulk
|
||||
import com.chibatching.kotpref.bulk
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.components.BottomSheetColorPicker
|
||||
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
|
||||
import com.tommasoberlose.anotherwidget.databinding.FragmentGeneralSettingsBinding
|
||||
import com.tommasoberlose.anotherwidget.global.Constants
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.global.RequestCode
|
||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toHexValue
|
||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toIntValue
|
||||
import com.tommasoberlose.anotherwidget.helpers.DateHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.WidgetHelper
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.ChooseApplicationActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.CustomDateActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.CustomFontActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
|
||||
import kotlinx.android.synthetic.main.fragment_clock_settings.*
|
||||
import kotlinx.android.synthetic.main.fragment_general_settings.*
|
||||
import kotlinx.android.synthetic.main.fragment_general_settings.scrollView
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.*
|
||||
|
||||
|
||||
class GeneralTabFragment : Fragment() {
|
||||
@ -57,6 +77,7 @@ class GeneralTabFragment : Fragment() {
|
||||
|
||||
binding.lifecycleOwner = this
|
||||
binding.viewModel = viewModel
|
||||
binding.isDarkModeEnabled = activity?.isDarkTheme() == true
|
||||
|
||||
return binding.root
|
||||
}
|
||||
@ -64,6 +85,8 @@ class GeneralTabFragment : Fragment() {
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
show_dividers_toggle.isChecked = Preferences.showDividers
|
||||
|
||||
setupListener()
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val lazyColors = requireContext().resources.getIntArray(R.array.material_colors)
|
||||
@ -97,7 +120,18 @@ class GeneralTabFragment : Fragment() {
|
||||
font_color_label?.text = getString(R.string.transparent)
|
||||
} else {
|
||||
font_color_label?.text =
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getFontColor())).toUpperCase()
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getFontColor(activity?.isDarkTheme() == true))).toUpperCase()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.textGlobalColorDark.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
if (Preferences.textGlobalAlphaDark == "00") {
|
||||
font_color_label?.text = getString(R.string.transparent)
|
||||
} else {
|
||||
font_color_label?.text =
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getFontColor(activity?.isDarkTheme() == true))).toUpperCase()
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -108,7 +142,73 @@ class GeneralTabFragment : Fragment() {
|
||||
font_color_label?.text = getString(R.string.transparent)
|
||||
} else {
|
||||
font_color_label?.text =
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getFontColor())).toUpperCase()
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getFontColor(activity?.isDarkTheme() == true))).toUpperCase()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.textGlobalAlphaDark.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
if (Preferences.textGlobalAlphaDark == "00") {
|
||||
font_color_label?.text = getString(R.string.transparent)
|
||||
} else {
|
||||
font_color_label?.text =
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getFontColor(activity?.isDarkTheme() == true))).toUpperCase()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.textSecondaryColor.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
if (Preferences.textSecondaryAlpha == "00") {
|
||||
secondary_font_color_label?.text = getString(R.string.transparent)
|
||||
} else {
|
||||
secondary_font_color_label?.text =
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getSecondaryFontColor(activity?.isDarkTheme() == true))).toUpperCase()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.textSecondaryColorDark.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
if (Preferences.textSecondaryAlphaDark == "00") {
|
||||
secondary_font_color_label?.text = getString(R.string.transparent)
|
||||
} else {
|
||||
secondary_font_color_label?.text =
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getSecondaryFontColor(activity?.isDarkTheme() == true))).toUpperCase()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.textSecondaryAlpha.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
if (Preferences.textSecondaryAlpha == "00") {
|
||||
secondary_font_color_label?.text = getString(R.string.transparent)
|
||||
} else {
|
||||
secondary_font_color_label?.text =
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getSecondaryFontColor(activity?.isDarkTheme() == true))).toUpperCase()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.textSecondaryAlphaDark.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
if (Preferences.textSecondaryAlphaDark == "00") {
|
||||
secondary_font_color_label?.text = getString(R.string.transparent)
|
||||
} else {
|
||||
secondary_font_color_label?.text =
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getSecondaryFontColor(activity?.isDarkTheme() == true))).toUpperCase()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.secondRowTopMargin.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
second_row_top_margin_label?.text = when (it) {
|
||||
Constants.SecondRowTopMargin.NONE.value -> getString(R.string.settings_clock_bottom_margin_subtitle_none)
|
||||
Constants.SecondRowTopMargin.SMALL.value -> getString(R.string.settings_clock_bottom_margin_subtitle_small)
|
||||
Constants.SecondRowTopMargin.LARGE.value -> getString(R.string.settings_clock_bottom_margin_subtitle_large)
|
||||
else -> getString(R.string.settings_clock_bottom_margin_subtitle_medium)
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -119,7 +219,18 @@ class GeneralTabFragment : Fragment() {
|
||||
background_color_label?.text = getString(R.string.transparent)
|
||||
} else {
|
||||
background_color_label?.text =
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getBackgroundColor())).toUpperCase()
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getBackgroundColor(activity?.isDarkTheme() == true))).toUpperCase()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.backgroundCardColorDark.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
if (Preferences.backgroundCardAlphaDark == "00") {
|
||||
background_color_label?.text = getString(R.string.transparent)
|
||||
} else {
|
||||
background_color_label?.text =
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getBackgroundColor(activity?.isDarkTheme() == true))).toUpperCase()
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -130,41 +241,86 @@ class GeneralTabFragment : Fragment() {
|
||||
background_color_label?.text = getString(R.string.transparent)
|
||||
} else {
|
||||
background_color_label?.text =
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getBackgroundColor())).toUpperCase()
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getBackgroundColor(activity?.isDarkTheme() == true))).toUpperCase()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.backgroundCardAlphaDark.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
if (Preferences.backgroundCardAlphaDark == "00") {
|
||||
background_color_label?.text = getString(R.string.transparent)
|
||||
} else {
|
||||
background_color_label?.text =
|
||||
"#%s".format(Integer.toHexString(ColorHelper.getBackgroundColor(activity?.isDarkTheme() == true))).toUpperCase()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.textShadow.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
text_shadow_label?.text = getString(SettingsStringHelper.getTextShadowString(it))
|
||||
if (activity?.isDarkTheme() != true) {
|
||||
text_shadow_label?.text =
|
||||
getString(SettingsStringHelper.getTextShadowString(it))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.textShadowDark.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
if (activity?.isDarkTheme() == true) {
|
||||
text_shadow_label?.text =
|
||||
getString(SettingsStringHelper.getTextShadowString(it))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.dateFormat.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
date_format_label?.text = DateHelper.getDateText(requireContext(), Calendar.getInstance())
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.customFont.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
custom_font_label?.text = getString(SettingsStringHelper.getCustomFontLabel(it))
|
||||
custom_font_label?.text = SettingsStringHelper.getCustomFontLabel(requireContext(), it)
|
||||
MainWidget.updateWidget(requireContext())
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.customFontFile.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
custom_font_label?.text = SettingsStringHelper.getCustomFontLabel(requireContext(), Preferences.customFont)
|
||||
MainWidget.updateWidget(requireContext())
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.customFontName.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
custom_font_label?.text = SettingsStringHelper.getCustomFontLabel(requireContext(), Preferences.customFont)
|
||||
MainWidget.updateWidget(requireContext())
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.customFontVariant.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
custom_font_label?.text = SettingsStringHelper.getCustomFontLabel(requireContext(), Preferences.customFont)
|
||||
MainWidget.updateWidget(requireContext())
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.showDividers.observe(viewLifecycleOwner, Observer {
|
||||
show_dividers_label?.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
|
||||
maintainScrollPosition {
|
||||
show_dividers_label?.text =
|
||||
if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun maintainScrollPosition(callback: () -> Unit) {
|
||||
val scrollPosition = scrollView.scrollY
|
||||
callback.invoke()
|
||||
lifecycleScope.launch {
|
||||
delay(200)
|
||||
scrollView.smoothScrollTo(0, scrollPosition)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupListener() {
|
||||
action_main_text_size.setOnClickListener {
|
||||
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.title_main_text_size)).setSelectedValue(Preferences.textMainSize)
|
||||
(32 downTo 10).filter { it % 2 == 0 }.forEach {
|
||||
(40 downTo 10).filter { it % 2 == 0 }.forEach {
|
||||
dialog.addItem("${it}sp", it.toFloat())
|
||||
}
|
||||
dialog.addOnSelectItemListener { value ->
|
||||
@ -174,7 +330,7 @@ class GeneralTabFragment : Fragment() {
|
||||
|
||||
action_second_text_size.setOnClickListener {
|
||||
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.title_second_text_size)).setSelectedValue(Preferences.textSecondSize)
|
||||
(28 downTo 10).filter { it % 2 == 0 }.forEach {
|
||||
(40 downTo 10).filter { it % 2 == 0 }.forEach {
|
||||
dialog.addItem("${it}sp", it.toFloat())
|
||||
}
|
||||
dialog.addOnSelectItemListener { value ->
|
||||
@ -186,94 +342,194 @@ class GeneralTabFragment : Fragment() {
|
||||
BottomSheetColorPicker(requireContext(),
|
||||
colors = colors,
|
||||
header = getString(R.string.settings_font_color_title),
|
||||
getSelected = ColorHelper::getFontColorRgb,
|
||||
getSelected = { ColorHelper.getFontColorRgb(activity?.isDarkTheme() == true) },
|
||||
onColorSelected = { color: Int ->
|
||||
val colorString = Integer.toHexString(color)
|
||||
Preferences.textGlobalColor = "#" + if (colorString.length > 6) colorString.substring(2) else colorString
|
||||
if (activity?.isDarkTheme() == true) {
|
||||
Preferences.textGlobalColorDark = "#" + if (colorString.length > 6) colorString.substring(2) else colorString
|
||||
} else {
|
||||
Preferences.textGlobalColor = "#" + if (colorString.length > 6) colorString.substring(2) else colorString
|
||||
}
|
||||
},
|
||||
showAlphaSelector = true,
|
||||
alpha = Preferences.textGlobalAlpha.toIntValue(),
|
||||
alpha = if (activity?.isDarkTheme() == true) Preferences.textGlobalAlphaDark.toIntValue() else Preferences.textGlobalAlpha.toIntValue(),
|
||||
onAlphaChangeListener = { alpha ->
|
||||
Preferences.textGlobalAlpha = alpha.toHexValue()
|
||||
if (activity?.isDarkTheme() == true) {
|
||||
Preferences.textGlobalAlphaDark = alpha.toHexValue()
|
||||
} else {
|
||||
Preferences.textGlobalAlpha = alpha.toHexValue()
|
||||
}
|
||||
}
|
||||
).show()
|
||||
}
|
||||
|
||||
action_secondary_font_color.setOnClickListener {
|
||||
BottomSheetColorPicker(requireContext(),
|
||||
colors = colors,
|
||||
header = getString(R.string.settings_secondary_font_color_title),
|
||||
getSelected = { ColorHelper.getSecondaryFontColorRgb(activity?.isDarkTheme() == true) },
|
||||
onColorSelected = { color: Int ->
|
||||
val colorString = Integer.toHexString(color)
|
||||
if (activity?.isDarkTheme() == true) {
|
||||
Preferences.textSecondaryColorDark =
|
||||
"#" + if (colorString.length > 6) colorString.substring(2) else colorString
|
||||
} else {
|
||||
Preferences.textSecondaryColor =
|
||||
"#" + if (colorString.length > 6) colorString.substring(2) else colorString
|
||||
}
|
||||
},
|
||||
showAlphaSelector = true,
|
||||
alpha = if (activity?.isDarkTheme() == true) Preferences.textSecondaryAlphaDark.toIntValue() else Preferences.textSecondaryAlpha.toIntValue(),
|
||||
onAlphaChangeListener = { alpha ->
|
||||
if (activity?.isDarkTheme() == true) {
|
||||
Preferences.textSecondaryAlphaDark = alpha.toHexValue()
|
||||
} else {
|
||||
Preferences.textSecondaryAlpha = alpha.toHexValue()
|
||||
}
|
||||
}
|
||||
).show()
|
||||
}
|
||||
|
||||
action_second_row_top_margin_size.setOnClickListener {
|
||||
BottomSheetMenu<Int>(
|
||||
requireContext(),
|
||||
header = getString(R.string.settings_secondary_row_top_margin_title)
|
||||
).setSelectedValue(Preferences.secondRowTopMargin)
|
||||
.addItem(
|
||||
getString(R.string.settings_clock_bottom_margin_subtitle_none),
|
||||
Constants.SecondRowTopMargin.NONE.value
|
||||
)
|
||||
.addItem(
|
||||
getString(R.string.settings_clock_bottom_margin_subtitle_small),
|
||||
Constants.SecondRowTopMargin.SMALL.value
|
||||
)
|
||||
.addItem(
|
||||
getString(R.string.settings_clock_bottom_margin_subtitle_medium),
|
||||
Constants.SecondRowTopMargin.MEDIUM.value
|
||||
)
|
||||
.addItem(
|
||||
getString(R.string.settings_clock_bottom_margin_subtitle_large),
|
||||
Constants.SecondRowTopMargin.LARGE.value
|
||||
)
|
||||
.addOnSelectItemListener { value ->
|
||||
Preferences.secondRowTopMargin = value
|
||||
}.show()
|
||||
}
|
||||
|
||||
action_date_format.setOnClickListener {
|
||||
val now = Calendar.getInstance()
|
||||
val dialog = BottomSheetMenu<String>(requireContext(), header = getString(R.string.settings_date_format_title)).setSelectedValue(Preferences.dateFormat)
|
||||
|
||||
dialog.addItem(DateHelper.getDefaultDateText(requireContext(), now), "")
|
||||
if (Preferences.dateFormat != "") {
|
||||
dialog.addItem(DateHelper.getDateText(requireContext(), now), Preferences.dateFormat)
|
||||
}
|
||||
dialog.addItem(getString(R.string.custom_date_format), "-")
|
||||
|
||||
dialog.addOnSelectItemListener { value ->
|
||||
when (value) {
|
||||
"-" -> {
|
||||
startActivity(Intent(requireContext(), CustomDateActivity::class.java))
|
||||
}
|
||||
"" -> {
|
||||
Preferences.blockingBulk {
|
||||
isDateCapitalize = false
|
||||
isDateUppercase = false
|
||||
}
|
||||
Preferences.dateFormat = value
|
||||
}
|
||||
else -> {
|
||||
Preferences.dateFormat = value
|
||||
}
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
|
||||
action_background_color.setOnClickListener {
|
||||
BottomSheetColorPicker(requireContext(),
|
||||
colors = colors,
|
||||
header = getString(R.string.settings_background_color_title),
|
||||
getSelected = { ColorHelper.getBackgroundColorRgb() },
|
||||
getSelected = { ColorHelper.getBackgroundColorRgb(activity?.isDarkTheme() == true) },
|
||||
onColorSelected = { color: Int ->
|
||||
val colorString = Integer.toHexString(color)
|
||||
Preferences.backgroundCardColor = "#" + if (colorString.length > 6) colorString.substring(2) else colorString
|
||||
if (activity?.isDarkTheme() == true) {
|
||||
Preferences.backgroundCardColorDark =
|
||||
"#" + if (colorString.length > 6) colorString.substring(2) else colorString
|
||||
} else {
|
||||
Preferences.backgroundCardColor =
|
||||
"#" + if (colorString.length > 6) colorString.substring(2) else colorString
|
||||
}
|
||||
},
|
||||
showAlphaSelector = true,
|
||||
alpha = Preferences.backgroundCardAlpha.toIntValue(),
|
||||
alpha = if (activity?.isDarkTheme() == true) Preferences.backgroundCardAlphaDark.toIntValue() else Preferences.backgroundCardAlpha.toIntValue(),
|
||||
onAlphaChangeListener = { alpha ->
|
||||
Preferences.backgroundCardAlpha = alpha.toHexValue()
|
||||
if (activity?.isDarkTheme() == true) {
|
||||
Preferences.backgroundCardAlphaDark = alpha.toHexValue()
|
||||
} else {
|
||||
Preferences.backgroundCardAlpha = alpha.toHexValue()
|
||||
}
|
||||
}
|
||||
).show()
|
||||
}
|
||||
|
||||
action_text_shadow.setOnClickListener {
|
||||
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.title_text_shadow)).setSelectedValue(Preferences.textShadow)
|
||||
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.title_text_shadow)).setSelectedValue(if (activity?.isDarkTheme() == true) Preferences.textShadowDark else Preferences.textShadow)
|
||||
(2 downTo 0).forEach {
|
||||
dialog.addItem(getString(SettingsStringHelper.getTextShadowString(it)), it)
|
||||
}
|
||||
dialog.addOnSelectItemListener { value ->
|
||||
Preferences.textShadow = value
|
||||
if (activity?.isDarkTheme() == true) {
|
||||
Preferences.textShadowDark = value
|
||||
} else {
|
||||
Preferences.textShadow = value
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
|
||||
action_custom_font.setOnClickListener {
|
||||
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_custom_font_title)).setSelectedValue(Preferences.customFont)
|
||||
(0..1).forEach {
|
||||
dialog.addItem(getString(SettingsStringHelper.getCustomFontLabel(it)), it)
|
||||
dialog.addItem(SettingsStringHelper.getCustomFontLabel(requireContext(), 0), 0)
|
||||
|
||||
if (Preferences.customFont == Constants.CUSTOM_FONT_GOOGLE_SANS) {
|
||||
dialog.addItem(SettingsStringHelper.getCustomFontLabel(requireContext(), Constants.CUSTOM_FONT_GOOGLE_SANS), Constants.CUSTOM_FONT_GOOGLE_SANS)
|
||||
}
|
||||
|
||||
if (Preferences.customFontFile != "") {
|
||||
dialog.addItem(SettingsStringHelper.getCustomFontLabel(requireContext(), Preferences.customFont), Constants.CUSTOM_FONT_DOWNLOADED)
|
||||
}
|
||||
dialog.addItem(getString(R.string.action_custom_font_to_search), Constants.CUSTOM_FONT_DOWNLOAD_NEW)
|
||||
dialog.addOnSelectItemListener { value ->
|
||||
Preferences.customFont = value
|
||||
if (value == Constants.CUSTOM_FONT_DOWNLOAD_NEW) {
|
||||
startActivityForResult(
|
||||
Intent(requireContext(), CustomFontActivity::class.java),
|
||||
RequestCode.CUSTOM_FONT_CHOOSER_REQUEST_CODE.code
|
||||
)
|
||||
} else if (value != Constants.CUSTOM_FONT_DOWNLOADED) {
|
||||
Preferences.bulk {
|
||||
customFont = value
|
||||
customFontFile = ""
|
||||
customFontName = ""
|
||||
customFontVariant = ""
|
||||
}
|
||||
}
|
||||
}.show()
|
||||
|
||||
/*
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
intent.type = "* / *" TO FIX WITHOUT SPACE
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
|
||||
try {
|
||||
startActivityForResult(Intent.createChooser(intent, "Select a File to Upload"), Constants.CUSTOM_FONT_CHOOSER_REQUEST_CODE)
|
||||
} catch (ex: android.content.ActivityNotFoundException) {
|
||||
Toast.makeText(this, "Please install a File Manager.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
action_show_dividers.setOnClickListener {
|
||||
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_show_multiple_events_title)).setSelectedValue(Preferences.showDividers)
|
||||
.addItem(getString(R.string.settings_visible), true)
|
||||
.addItem(getString(R.string.settings_not_visible), false)
|
||||
.addOnSelectItemListener { value ->
|
||||
Preferences.showDividers = value
|
||||
}.show()
|
||||
show_dividers_toggle.isChecked = !show_dividers_toggle.isChecked
|
||||
}
|
||||
|
||||
show_dividers_toggle.setOnCheckedChangeListener { _, isChecked ->
|
||||
Preferences.showDividers = isChecked
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
when (requestCode) {
|
||||
RequestCode.CUSTOM_FONT_CHOOSER_REQUEST_CODE.code -> {
|
||||
/*val uri = data.data
|
||||
Log.d("AW", "File Uri: " + uri.toString())
|
||||
val path = Util.getPath(this, uri)
|
||||
Log.d("AW", "File Path: " + path)
|
||||
SP.edit()
|
||||
.putString(Constants.PREF_CUSTOM_FONT_FILE, path)
|
||||
.commit()
|
||||
sendBroadcast(Intent(Constants.ACTION_TIME_UPDATE))
|
||||
updateSettings()*/
|
||||
}
|
||||
}
|
||||
private fun maintainScrollPosition(callback: () -> Unit) {
|
||||
scrollView.isScrollable = false
|
||||
callback.invoke()
|
||||
lifecycleScope.launch {
|
||||
delay(200)
|
||||
scrollView.isScrollable = true
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +1,59 @@
|
||||
package com.tommasoberlose.anotherwidget.ui.fragments
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.app.AlarmManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
|
||||
import com.google.android.gms.common.api.ApiException
|
||||
import com.karumi.dexter.Dexter
|
||||
import com.karumi.dexter.MultiplePermissionsReport
|
||||
import com.karumi.dexter.PermissionToken
|
||||
import com.karumi.dexter.listener.PermissionRequest
|
||||
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
|
||||
import com.tommasoberlose.anotherwidget.components.CustomNotesDialog
|
||||
import com.tommasoberlose.anotherwidget.components.GlanceProviderSortMenu
|
||||
import com.tommasoberlose.anotherwidget.components.MaterialBottomSheetDialog
|
||||
import com.tommasoberlose.anotherwidget.databinding.FragmentGlanceSettingsBinding
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.AlarmHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.GlanceProviderHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
|
||||
import com.tommasoberlose.anotherwidget.models.GlanceProvider
|
||||
import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver
|
||||
import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver.Companion.FITNESS_OPTIONS
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.MusicPlayersFilterActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import com.tommasoberlose.anotherwidget.utils.checkIfFitInstalled
|
||||
import com.tommasoberlose.anotherwidget.utils.toast
|
||||
import kotlinx.android.synthetic.main.fragment_calendar_settings.*
|
||||
import kotlinx.android.synthetic.main.fragment_glance_settings.*
|
||||
import kotlinx.android.synthetic.main.fragment_glance_settings.scrollView
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import net.idik.lib.slimadapter.SlimAdapter
|
||||
import java.util.*
|
||||
|
||||
|
||||
class GlanceTabFragment : Fragment() {
|
||||
@ -48,7 +63,6 @@ class GlanceTabFragment : Fragment() {
|
||||
}
|
||||
|
||||
private lateinit var viewModel: MainViewModel
|
||||
private lateinit var adapter: SlimAdapter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -73,64 +87,7 @@ class GlanceTabFragment : Fragment() {
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
list.setHasFixedSize(true)
|
||||
val mLayoutManager = LinearLayoutManager(requireContext())
|
||||
list.layoutManager = mLayoutManager
|
||||
|
||||
adapter = SlimAdapter.create()
|
||||
adapter
|
||||
.register<String>(R.layout.glance_provider_item) { item, injector ->
|
||||
injector
|
||||
.text(R.id.title, item)
|
||||
}
|
||||
.register<GlanceProvider>(R.layout.glance_provider_item) { item, injector ->
|
||||
injector
|
||||
.text(R.id.title, item.title)
|
||||
.with<ImageView>(R.id.icon) {
|
||||
it.setImageDrawable(ContextCompat.getDrawable(requireContext(), item.icon))
|
||||
}
|
||||
.with<TextView>(R.id.label) {
|
||||
it.isVisible = item.label != ""
|
||||
it.text = item.label
|
||||
}
|
||||
}
|
||||
.attachTo(list)
|
||||
|
||||
adapter.updateData(
|
||||
GlanceProviderHelper.getGlanceProviders()
|
||||
.mapNotNull { GlanceProviderHelper.getGlanceProviderById(requireContext(), it) }
|
||||
)
|
||||
|
||||
val mIth = ItemTouchHelper(
|
||||
object : ItemTouchHelper.SimpleCallback(
|
||||
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
|
||||
ItemTouchHelper.LEFT
|
||||
) {
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: ViewHolder, target: ViewHolder
|
||||
): Boolean {
|
||||
val fromPos = viewHolder.adapterPosition
|
||||
val toPos = target.adapterPosition
|
||||
// move item in `fromPos` to `toPos` in adapter.
|
||||
adapter.notifyItemMoved(fromPos, toPos)
|
||||
|
||||
val list = GlanceProviderHelper.getGlanceProviders()
|
||||
Collections.swap(list, fromPos, toPos)
|
||||
GlanceProviderHelper.saveGlanceProviderOrder(list)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSwiped(
|
||||
viewHolder: ViewHolder,
|
||||
direction: Int
|
||||
) {
|
||||
// remove from adapter
|
||||
}
|
||||
})
|
||||
|
||||
mIth.attachToRecyclerView(list)
|
||||
|
||||
action_show_steps.isVisible = requireContext().checkIfFitInstalled()
|
||||
|
||||
setupListener()
|
||||
updateNextAlarmWarningUi()
|
||||
@ -140,19 +97,49 @@ class GlanceTabFragment : Fragment() {
|
||||
binding: FragmentGlanceSettingsBinding,
|
||||
viewModel: MainViewModel
|
||||
) {
|
||||
binding.isGlanceVisible = Preferences.showGlance
|
||||
|
||||
viewModel.showGlance.observe(viewLifecycleOwner, Observer {
|
||||
binding.isGlanceVisible = it
|
||||
maintainScrollPosition {
|
||||
binding.isGlanceVisible = it
|
||||
show_glance_label.text = if (it) getString(R.string.description_show_glance_visible) else getString(R.string.description_show_glance_not_visible)
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.showMusic.observe(viewLifecycleOwner, Observer {
|
||||
checkNotificationPermission()
|
||||
maintainScrollPosition {
|
||||
checkNotificationPermission()
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.showNextAlarm.observe(viewLifecycleOwner, Observer {
|
||||
updateNextAlarmWarningUi()
|
||||
maintainScrollPosition {
|
||||
updateNextAlarmWarningUi()
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.showBatteryCharging.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
show_low_battery_level_warning_label?.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.showDailySteps.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
show_steps_label?.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
|
||||
}
|
||||
checkFitnessPermission()
|
||||
})
|
||||
|
||||
viewModel.customInfo.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
show_custom_notes_label?.text = if (it == "") getString(R.string.settings_not_visible) else it
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.musicPlayersFilter.observe(viewLifecycleOwner, Observer {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private fun setupListener() {
|
||||
@ -165,6 +152,11 @@ class GlanceTabFragment : Fragment() {
|
||||
Preferences.showGlance = enabled
|
||||
}
|
||||
|
||||
action_sort_glance_providers.setOnClickListener {
|
||||
GlanceProviderSortMenu(requireContext())
|
||||
.show()
|
||||
}
|
||||
|
||||
action_show_music.setOnClickListener {
|
||||
if (Preferences.showGlance) {
|
||||
BottomSheetMenu<Boolean>(
|
||||
@ -192,6 +184,72 @@ class GlanceTabFragment : Fragment() {
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
|
||||
action_show_next_alarm.setOnLongClickListener {
|
||||
with(requireContext().getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
|
||||
val alarm = nextAlarmClock
|
||||
if (alarm != null && alarm.showIntent != null) {
|
||||
val pm = requireContext().packageManager as PackageManager
|
||||
val appNameOrPackage = try {
|
||||
pm.getApplicationLabel(pm.getApplicationInfo(alarm.showIntent?.creatorPackage ?: "", 0))
|
||||
} catch (e: Exception) {
|
||||
alarm.showIntent?.creatorPackage ?: ""
|
||||
}
|
||||
MaterialBottomSheetDialog(requireContext(), message = getString(R.string.next_alarm_warning).format(appNameOrPackage))
|
||||
.setPositiveButton(getString(android.R.string.ok))
|
||||
.show()
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
action_show_low_battery_level_warning.setOnClickListener {
|
||||
if (Preferences.showGlance) {
|
||||
BottomSheetMenu<Boolean>(
|
||||
requireContext(),
|
||||
header = getString(R.string.settings_low_battery_level_title)
|
||||
).setSelectedValue(Preferences.showBatteryCharging)
|
||||
.addItem(getString(R.string.settings_visible), true)
|
||||
.addItem(getString(R.string.settings_not_visible), false)
|
||||
.addOnSelectItemListener { value ->
|
||||
Preferences.showBatteryCharging = value
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
|
||||
action_show_steps.setOnClickListener {
|
||||
if (Preferences.showGlance) {
|
||||
BottomSheetMenu<Boolean>(
|
||||
requireContext(),
|
||||
header = getString(R.string.settings_daily_steps_title)
|
||||
).setSelectedValue(Preferences.showDailySteps)
|
||||
.addItem(getString(R.string.settings_visible), true)
|
||||
.addItem(getString(R.string.settings_not_visible), false)
|
||||
.addOnSelectItemListener { value ->
|
||||
if (value) {
|
||||
val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(requireContext())
|
||||
if (!GoogleSignIn.hasPermissions(account, FITNESS_OPTIONS)) {
|
||||
val mGoogleSignInClient = GoogleSignIn.getClient(requireActivity(), GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).addExtension(FITNESS_OPTIONS).build())
|
||||
startActivityForResult(mGoogleSignInClient.signInIntent, 2)
|
||||
} else {
|
||||
Preferences.showDailySteps = true
|
||||
}
|
||||
} else {
|
||||
Preferences.showDailySteps = false
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
|
||||
action_show_custom_notes.setOnClickListener {
|
||||
if (Preferences.showGlance) {
|
||||
CustomNotesDialog(requireContext()).show()
|
||||
}
|
||||
}
|
||||
|
||||
action_filter_music_players.setOnClickListener {
|
||||
startActivity(Intent(requireContext(), MusicPlayersFilterActivity::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateNextAlarmWarningUi() {
|
||||
@ -204,15 +262,10 @@ class GlanceTabFragment : Fragment() {
|
||||
} catch (e: Exception) {
|
||||
alarm.showIntent?.creatorPackage ?: ""
|
||||
}
|
||||
maintainScrollPosition {
|
||||
show_next_alarm_warning.text =
|
||||
getString(R.string.next_alarm_warning).format(appNameOrPackage)
|
||||
}
|
||||
show_next_alarm_warning.text = getString(R.string.next_alarm_warning).format(appNameOrPackage)
|
||||
} else {
|
||||
maintainScrollPosition {
|
||||
show_next_alarm_label?.text = if (Preferences.showNextAlarm) getString(R.string.settings_visible) else getString(
|
||||
show_next_alarm_label?.text = if (Preferences.showNextAlarm) getString(R.string.settings_visible) else getString(
|
||||
R.string.settings_not_visible)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -234,28 +287,114 @@ class GlanceTabFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun checkNotificationPermission() {
|
||||
if (NotificationManagerCompat.getEnabledListenerPackages(requireContext()).contains(requireContext().packageName)) {
|
||||
notification_permission_alert?.isVisible = false
|
||||
MediaPlayerHelper.updatePlayingMediaInfo(requireContext())
|
||||
show_music_label?.text = if (Preferences.showMusic) getString(R.string.settings_show_music_enabled_subtitle) else getString(R.string.settings_show_music_disabled_subtitle)
|
||||
} else if (Preferences.showMusic) {
|
||||
notification_permission_alert?.isVisible = true
|
||||
show_music_label?.text = getString(R.string.settings_request_notification_access)
|
||||
notification_permission_alert?.setOnClickListener {
|
||||
activity?.startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"))
|
||||
when {
|
||||
NotificationManagerCompat.getEnabledListenerPackages(requireContext()).contains(requireContext().packageName) -> {
|
||||
notification_permission_alert?.isVisible = false
|
||||
MediaPlayerHelper.updatePlayingMediaInfo(requireContext())
|
||||
show_music_label?.text = if (Preferences.showMusic) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
|
||||
}
|
||||
Preferences.showMusic -> {
|
||||
notification_permission_alert?.isVisible = true
|
||||
show_music_label?.text = getString(R.string.settings_request_notification_access)
|
||||
notification_permission_alert?.setOnClickListener {
|
||||
activity?.startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"))
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
show_music_label?.text = getString(R.string.settings_not_visible)
|
||||
notification_permission_alert?.isVisible = false
|
||||
}
|
||||
} else {
|
||||
show_music_label?.text = getString(R.string.settings_show_music_disabled_subtitle)
|
||||
notification_permission_alert?.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkFitnessPermission() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || activity?.checkGrantedPermission(Manifest.permission.ACTIVITY_RECOGNITION) == true) {
|
||||
fitness_permission_alert?.isVisible = false
|
||||
if (Preferences.showDailySteps) {
|
||||
ActivityDetectionReceiver.registerFence(requireContext())
|
||||
} else {
|
||||
ActivityDetectionReceiver.unregisterFence(requireContext())
|
||||
}
|
||||
show_steps_label?.text = if (Preferences.showDailySteps) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
|
||||
} else if (Preferences.showDailySteps) {
|
||||
ActivityDetectionReceiver.unregisterFence(requireContext())
|
||||
fitness_permission_alert?.isVisible = true
|
||||
show_steps_label?.text = getString(R.string.settings_request_fitness_access)
|
||||
fitness_permission_alert?.setOnClickListener {
|
||||
requireFitnessPermission()
|
||||
}
|
||||
} else {
|
||||
ActivityDetectionReceiver.unregisterFence(requireContext())
|
||||
show_steps_label?.text = getString(R.string.settings_not_visible)
|
||||
fitness_permission_alert?.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(
|
||||
requestCode: Int,
|
||||
resultCode: Int,
|
||||
data: Intent?
|
||||
) {
|
||||
when (requestCode) {
|
||||
1 -> {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
checkFitnessPermission()
|
||||
} else {
|
||||
Preferences.showDailySteps = false
|
||||
}
|
||||
}
|
||||
2-> {
|
||||
try {
|
||||
val account: GoogleSignInAccount? = GoogleSignIn.getSignedInAccountFromIntent(data).getResult(ApiException::class.java)
|
||||
if (!GoogleSignIn.hasPermissions(account, FITNESS_OPTIONS)) {
|
||||
GoogleSignIn.requestPermissions(
|
||||
requireActivity(),
|
||||
1,
|
||||
account,
|
||||
FITNESS_OPTIONS)
|
||||
} else {
|
||||
checkFitnessPermission()
|
||||
}
|
||||
} catch (e: ApiException) {
|
||||
e.printStackTrace()
|
||||
Preferences.showDailySteps = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun requireFitnessPermission() {
|
||||
Dexter.withContext(requireContext())
|
||||
.withPermissions(
|
||||
"com.google.android.gms.permission.ACTIVITY_RECOGNITION",
|
||||
"android.gms.permission.ACTIVITY_RECOGNITION",
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACTIVITY_RECOGNITION else "com.google.android.gms.permission.ACTIVITY_RECOGNITION"
|
||||
).withListener(object: MultiplePermissionsListener {
|
||||
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
|
||||
report?.let {
|
||||
if (report.areAllPermissionsGranted()){
|
||||
checkFitnessPermission()
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun onPermissionRationaleShouldBeShown(
|
||||
permissions: MutableList<PermissionRequest>?,
|
||||
token: PermissionToken?
|
||||
) {
|
||||
// Remember to invoke this method when the custom rationale is closed
|
||||
// or just by default if you don't want to use any custom rationale.
|
||||
token?.continuePermissionRequest()
|
||||
}
|
||||
})
|
||||
.check()
|
||||
}
|
||||
|
||||
private fun maintainScrollPosition(callback: () -> Unit) {
|
||||
val scrollPosition = scrollView.scrollY
|
||||
scrollView.isScrollable = false
|
||||
callback.invoke()
|
||||
lifecycleScope.launch {
|
||||
delay(200)
|
||||
scrollView.smoothScrollTo(0, scrollPosition)
|
||||
scrollView.isScrollable = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@ import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
@ -34,13 +33,13 @@ import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.helpers.BitmapHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark
|
||||
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.WidgetHelper
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.adapters.ViewPagerAdapter
|
||||
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
|
||||
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import com.tommasoberlose.anotherwidget.utils.getCurrentWallpaper
|
||||
import com.tommasoberlose.anotherwidget.utils.toPixel
|
||||
import com.tommasoberlose.anotherwidget.utils.*
|
||||
import kotlinx.android.synthetic.main.fragment_app_main.*
|
||||
import kotlinx.android.synthetic.main.the_widget_sans.*
|
||||
import kotlinx.coroutines.*
|
||||
@ -60,8 +59,8 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
enterTransition = MaterialSharedAxis.create(MaterialSharedAxis.X, true)
|
||||
reenterTransition = MaterialSharedAxis.create(MaterialSharedAxis.X, false)
|
||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
@ -74,6 +73,7 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
subscribeUi(viewModel)
|
||||
|
||||
// Viewpager
|
||||
@ -91,9 +91,9 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
|
||||
}.attach()
|
||||
|
||||
// Init clock
|
||||
time.setTextColor(ColorHelper.getClockFontColor())
|
||||
time.setTextColor(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true))
|
||||
time.setTextSize(TypedValue.COMPLEX_UNIT_SP, Preferences.clockTextSize.toPixel(requireContext()))
|
||||
time_am_pm.setTextColor(ColorHelper.getClockFontColor())
|
||||
time_am_pm.setTextColor(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true))
|
||||
time_am_pm.setTextSize(TypedValue.COMPLEX_UNIT_SP, Preferences.clockTextSize.toPixel(requireContext()) / 5 * 2)
|
||||
time_container.isVisible = Preferences.showClock
|
||||
|
||||
@ -125,141 +125,174 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
|
||||
private fun updateUI() {
|
||||
uiJob?.cancel()
|
||||
|
||||
preview?.clearAnimation()
|
||||
time_container?.clearAnimation()
|
||||
|
||||
if (Preferences.showPreview) {
|
||||
preview.setCardBackgroundColor(
|
||||
preview?.setCardBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
requireContext(),
|
||||
if (ColorHelper.getFontColor()
|
||||
if (ColorHelper.getFontColor(activity?.isDarkTheme() == true)
|
||||
.isColorDark()
|
||||
) android.R.color.white else R.color.colorAccent
|
||||
)
|
||||
)
|
||||
widget_shape_background.setImageDrawable(BitmapHelper.getTintedDrawable(requireContext(), R.drawable.card_background, ColorHelper.getBackgroundColor()))
|
||||
uiJob = viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
|
||||
val generatedView = MainWidget.generateWidgetView(requireContext())
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
generatedView.measure(0, 0)
|
||||
preview.measure(0, 0)
|
||||
}
|
||||
|
||||
val bitmap = BitmapHelper.getBitmapFromView(
|
||||
generatedView,
|
||||
if (preview.width > 0) preview.width else generatedView.measuredWidth,
|
||||
generatedView.measuredHeight
|
||||
widget_shape_background?.setImageDrawable(
|
||||
BitmapHelper.getTintedDrawable(
|
||||
requireContext(),
|
||||
R.drawable.card_background,
|
||||
ColorHelper.getBackgroundColor(activity?.isDarkTheme() == true)
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
// Clock
|
||||
time.setTextColor(ColorHelper.getClockFontColor())
|
||||
time_am_pm.setTextColor(ColorHelper.getClockFontColor())
|
||||
time.setTextSize(
|
||||
TypedValue.COMPLEX_UNIT_SP,
|
||||
Preferences.clockTextSize.toPixel(requireContext())
|
||||
)
|
||||
time_am_pm.setTextSize(
|
||||
TypedValue.COMPLEX_UNIT_SP,
|
||||
Preferences.clockTextSize.toPixel(requireContext()) / 5 * 2
|
||||
)
|
||||
)
|
||||
WidgetHelper.runWithCustomTypeface(requireContext()) { typeface ->
|
||||
uiJob = lifecycleScope.launch(Dispatchers.IO) {
|
||||
val generatedView = MainWidget.generateWidgetView(requireContext(), typeface)
|
||||
|
||||
// Clock bottom margin
|
||||
clock_bottom_margin_none.isVisible =
|
||||
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.NONE.value
|
||||
clock_bottom_margin_small.isVisible =
|
||||
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.SMALL.value
|
||||
clock_bottom_margin_medium.isVisible =
|
||||
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.MEDIUM.value
|
||||
clock_bottom_margin_large.isVisible =
|
||||
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.LARGE.value
|
||||
withContext(Dispatchers.Main) {
|
||||
generatedView.measure(0, 0)
|
||||
preview?.measure(0, 0)
|
||||
}
|
||||
|
||||
if ((Preferences.showClock && !time_container.isVisible) || (!Preferences.showClock && time_container.isVisible)) {
|
||||
if (Preferences.showClock) {
|
||||
time_container.layoutParams = time_container.layoutParams.apply {
|
||||
val bitmap = if (preview != null) {
|
||||
BitmapHelper.getBitmapFromView(
|
||||
generatedView,
|
||||
if (preview.width > 0) preview.width else generatedView.measuredWidth,
|
||||
generatedView.measuredHeight
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
// Clock
|
||||
time?.setTextColor(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true))
|
||||
time_am_pm?.setTextColor(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true))
|
||||
time?.setTextSize(
|
||||
TypedValue.COMPLEX_UNIT_SP,
|
||||
Preferences.clockTextSize.toPixel(requireContext())
|
||||
)
|
||||
time_am_pm?.setTextSize(
|
||||
TypedValue.COMPLEX_UNIT_SP,
|
||||
Preferences.clockTextSize.toPixel(requireContext()) / 5 * 2
|
||||
)
|
||||
time_am_pm?.isVisible = Preferences.showAMPMIndicator
|
||||
|
||||
// Clock bottom margin
|
||||
clock_bottom_margin_none?.isVisible =
|
||||
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.NONE.value
|
||||
clock_bottom_margin_small?.isVisible =
|
||||
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.SMALL.value
|
||||
clock_bottom_margin_medium?.isVisible =
|
||||
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.MEDIUM.value
|
||||
clock_bottom_margin_large?.isVisible =
|
||||
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.LARGE.value
|
||||
|
||||
if ((Preferences.showClock && (time?.alpha ?: 1f < 1f)) || (!Preferences.showClock && (time?.alpha ?: 0f > 0f))) {
|
||||
if (Preferences.showClock) {
|
||||
time_container?.layoutParams = time_container.layoutParams.apply {
|
||||
height = RelativeLayout.LayoutParams.WRAP_CONTENT
|
||||
}
|
||||
time_container?.measure(0, 0)
|
||||
}
|
||||
val initialHeight = time_container?.measuredHeight ?: 0
|
||||
ValueAnimator.ofFloat(
|
||||
if (Preferences.showClock) 0f else 1f,
|
||||
if (Preferences.showClock) 1f else 0f
|
||||
).apply {
|
||||
duration = 500L
|
||||
addUpdateListener {
|
||||
val animatedValue = animatedValue as Float
|
||||
time_container?.layoutParams =
|
||||
time_container.layoutParams.apply {
|
||||
height = (initialHeight * animatedValue).toInt()
|
||||
}
|
||||
time?.alpha = animatedValue
|
||||
}
|
||||
addListener(
|
||||
onStart = {
|
||||
if (Preferences.showClock) {
|
||||
time_container?.isVisible = true
|
||||
}
|
||||
},
|
||||
onEnd = {
|
||||
if (!Preferences.showClock) {
|
||||
time_container?.isVisible = false
|
||||
}
|
||||
}
|
||||
)
|
||||
}.start()
|
||||
|
||||
if (preview != null) {
|
||||
ValueAnimator.ofInt(
|
||||
preview.height,
|
||||
PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel(
|
||||
requireContext()
|
||||
) else 0
|
||||
).apply {
|
||||
duration = 500L
|
||||
addUpdateListener {
|
||||
if (preview != null) {
|
||||
val animatedValue = animatedValue as Int
|
||||
val layoutParams = preview.layoutParams
|
||||
layoutParams.height = animatedValue
|
||||
preview.layoutParams = layoutParams
|
||||
}
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
} else {
|
||||
time_container?.layoutParams = time_container.layoutParams.apply {
|
||||
height = RelativeLayout.LayoutParams.WRAP_CONTENT
|
||||
}
|
||||
time_container.measure(0, 0)
|
||||
time_container?.measure(0, 0)
|
||||
}
|
||||
val initialHeight = time_container.measuredHeight
|
||||
ValueAnimator.ofFloat(
|
||||
if (Preferences.showClock) 0f else 1f,
|
||||
if (Preferences.showClock) 1f else 0f
|
||||
).apply {
|
||||
duration = 500L
|
||||
addUpdateListener {
|
||||
val animatedValue = animatedValue as Float
|
||||
time_container.layoutParams = time_container.layoutParams.apply {
|
||||
height = (initialHeight * animatedValue).toInt()
|
||||
}
|
||||
}
|
||||
addListener(
|
||||
onStart = {
|
||||
if (Preferences.showClock) {
|
||||
time_container.isVisible = true
|
||||
}
|
||||
},
|
||||
onEnd = {
|
||||
if (!Preferences.showClock) {
|
||||
time_container.isVisible = false
|
||||
|
||||
if (preview != null && preview.height == 0) {
|
||||
ValueAnimator.ofInt(
|
||||
preview.height,
|
||||
PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel(
|
||||
requireContext()
|
||||
) else 0
|
||||
).apply {
|
||||
duration = 300L
|
||||
addUpdateListener {
|
||||
if (preview != null) {
|
||||
val animatedValue = animatedValue as Int
|
||||
val layoutParams = preview.layoutParams
|
||||
layoutParams.height = animatedValue
|
||||
preview?.layoutParams = layoutParams
|
||||
}
|
||||
}
|
||||
)
|
||||
}.start()
|
||||
|
||||
ValueAnimator.ofInt(
|
||||
preview.height,
|
||||
PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel(
|
||||
requireContext()
|
||||
) else 0
|
||||
).apply {
|
||||
duration = 500L
|
||||
addUpdateListener {
|
||||
val animatedValue = animatedValue as Int
|
||||
val layoutParams = preview.layoutParams
|
||||
layoutParams.height = animatedValue
|
||||
preview.layoutParams = layoutParams
|
||||
}
|
||||
}.start()
|
||||
} else {
|
||||
time_container.layoutParams = time_container.layoutParams.apply {
|
||||
height = RelativeLayout.LayoutParams.WRAP_CONTENT
|
||||
}.start()
|
||||
}
|
||||
time_container.measure(0, 0)
|
||||
}
|
||||
|
||||
if (preview.height == 0) {
|
||||
ValueAnimator.ofInt(
|
||||
preview.height,
|
||||
PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel(
|
||||
requireContext()
|
||||
) else 0
|
||||
).apply {
|
||||
duration = 300L
|
||||
addUpdateListener {
|
||||
val animatedValue = animatedValue as Int
|
||||
val layoutParams = preview.layoutParams
|
||||
layoutParams.height = animatedValue
|
||||
preview.layoutParams = layoutParams
|
||||
}
|
||||
}.start()
|
||||
widget_loader?.animate()?.scaleX(0f)?.scaleY(0f)?.alpha(0f)
|
||||
?.setDuration(200L)?.start()
|
||||
bitmap_container?.apply {
|
||||
setImageBitmap(bitmap)
|
||||
scaleX = 0.9f
|
||||
scaleY = 0.9f
|
||||
}
|
||||
widget?.animate()?.alpha(1f)?.start()
|
||||
}
|
||||
|
||||
widget_loader.animate().scaleX(0f).scaleY(0f).alpha(0f).setDuration(200L).start()
|
||||
bitmap_container.setImageBitmap(bitmap)
|
||||
widget.animate().alpha(1f).start()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ValueAnimator.ofInt(
|
||||
preview.height,
|
||||
0
|
||||
).apply {
|
||||
duration = 300L
|
||||
addUpdateListener {
|
||||
val animatedValue = animatedValue as Int
|
||||
val layoutParams = preview.layoutParams
|
||||
layoutParams.height = animatedValue
|
||||
preview.layoutParams = layoutParams
|
||||
}
|
||||
}.start()
|
||||
if (preview != null) {
|
||||
ValueAnimator.ofInt(
|
||||
preview.height,
|
||||
0
|
||||
).apply {
|
||||
duration = 300L
|
||||
addUpdateListener {
|
||||
if (preview != null) {
|
||||
val animatedValue = animatedValue as Int
|
||||
val layoutParams = preview.layoutParams
|
||||
layoutParams.height = animatedValue
|
||||
preview.layoutParams = layoutParams
|
||||
}
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
showErrorBadge()
|
||||
@ -271,13 +304,27 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
|
||||
activity?.let { act ->
|
||||
val wallpaper = act.getCurrentWallpaper()
|
||||
widget_bg.setImageDrawable(if (it) wallpaper else null)
|
||||
widget_bg.layoutParams = widget_bg.layoutParams.apply {
|
||||
if (wallpaper != null) {
|
||||
widget_bg.layoutParams =
|
||||
(widget_bg.layoutParams as ViewGroup.MarginLayoutParams).apply {
|
||||
|
||||
val metrics = DisplayMetrics()
|
||||
act.windowManager.defaultDisplay.getMetrics(metrics)
|
||||
val metrics = DisplayMetrics()
|
||||
act.windowManager.defaultDisplay.getMetrics(metrics)
|
||||
|
||||
height = metrics.heightPixels
|
||||
width = (wallpaper?.intrinsicWidth ?: 1) * metrics.heightPixels / (wallpaper?.intrinsicWidth ?: 1)
|
||||
val dimensions: Pair<Int, Int> = if (wallpaper.intrinsicWidth >= wallpaper.intrinsicHeight) {
|
||||
metrics.heightPixels to (wallpaper.intrinsicWidth) * metrics.heightPixels / (wallpaper.intrinsicHeight)
|
||||
} else {
|
||||
metrics.widthPixels to (wallpaper.intrinsicHeight) * metrics.widthPixels / (wallpaper.intrinsicWidth)
|
||||
}
|
||||
|
||||
setMargins(
|
||||
if (dimensions.first >= dimensions.second) (-80).toPixel(requireContext()) else 0,
|
||||
(-80).toPixel(requireContext()), 0, 0
|
||||
)
|
||||
|
||||
width = dimensions.first
|
||||
height = dimensions.second
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -302,8 +349,15 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
|
||||
tabs?.getTabAt(2)?.orCreateBadge?.apply {
|
||||
backgroundColor = ContextCompat.getColor(requireContext(), R.color.errorColorText)
|
||||
badgeGravity = BadgeDrawable.TOP_END
|
||||
}?.isVisible = Preferences.showWeather && (Preferences.weatherProviderApi == "" || (Preferences.customLocationAdd == "" && activity?.checkGrantedPermission(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else Manifest.permission.ACCESS_FINE_LOCATION) != true))
|
||||
|
||||
}?.isVisible = if (Preferences.showWeather) {
|
||||
(WeatherHelper.isKeyRequired() && WeatherHelper.getApiKey() == "")
|
||||
|| (Preferences.customLocationAdd == "" && activity?.checkGrantedPermission(
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else Manifest.permission.ACCESS_FINE_LOCATION
|
||||
) != true)
|
||||
|| (Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
// Music error indicator
|
||||
tabs?.getTabAt(4)?.orCreateBadge?.apply {
|
||||
@ -312,11 +366,6 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
|
||||
}?.isVisible = Preferences.showMusic && !NotificationManagerCompat.getEnabledListenerPackages(requireContext()).contains(requireContext().packageName)
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(preferences: SharedPreferences, p1: String) {
|
||||
updateUI()
|
||||
MainWidget.updateWidget(requireContext())
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
Preferences.preferences.registerOnSharedPreferenceChangeListener(this)
|
||||
@ -331,10 +380,29 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
private var delayJob: Job? = null
|
||||
|
||||
override fun onSharedPreferenceChanged(preferences: SharedPreferences, p1: String) {
|
||||
delayJob?.cancel()
|
||||
delayJob = lifecycleScope.launch(Dispatchers.IO) {
|
||||
delay(200)
|
||||
withContext(Dispatchers.Main) {
|
||||
updateUI()
|
||||
}
|
||||
}
|
||||
MainWidget.updateWidget(requireContext())
|
||||
}
|
||||
|
||||
class UpdateUiMessageEvent
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onMessageEvent(ignore: UpdateUiMessageEvent?) {
|
||||
updateUI()
|
||||
delayJob?.cancel()
|
||||
delayJob = lifecycleScope.launch(Dispatchers.IO) {
|
||||
delay(200)
|
||||
withContext(Dispatchers.Main) {
|
||||
updateUI()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@ -49,8 +50,8 @@ class SettingsFragment : Fragment() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enterTransition = MaterialSharedAxis.create(MaterialSharedAxis.X, true)
|
||||
returnTransition = MaterialSharedAxis.create(MaterialSharedAxis.X, false)
|
||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
@ -64,6 +65,8 @@ class SettingsFragment : Fragment() {
|
||||
binding.lifecycleOwner = this
|
||||
binding.viewModel = viewModel
|
||||
|
||||
subscribeUi(viewModel)
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
@ -74,7 +77,8 @@ class SettingsFragment : Fragment() {
|
||||
Navigation.findNavController(it).popBackStack()
|
||||
}
|
||||
|
||||
subscribeUi(viewModel)
|
||||
show_widget_preview_toggle.isChecked = Preferences.showPreview
|
||||
show_wallpaper_toggle.isChecked = Preferences.showWallpaper
|
||||
|
||||
setupListener()
|
||||
|
||||
@ -119,44 +123,26 @@ class SettingsFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun setupListener() {
|
||||
|
||||
action_show_widget_preview.setOnClickListener {
|
||||
maintainScrollPosition {
|
||||
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.action_show_widget_preview))
|
||||
.setSelectedValue(Preferences.showPreview)
|
||||
.addItem(
|
||||
getString(R.string.settings_visible),
|
||||
true
|
||||
)
|
||||
.addItem(
|
||||
getString(R.string.settings_not_visible),
|
||||
false
|
||||
)
|
||||
.addOnSelectItemListener { value ->
|
||||
Preferences.showPreview = value
|
||||
}.show()
|
||||
}
|
||||
show_widget_preview_toggle.isChecked = !show_widget_preview_toggle.isChecked
|
||||
}
|
||||
|
||||
show_widget_preview_toggle.setOnCheckedChangeListener { _, isChecked ->
|
||||
Preferences.showPreview = isChecked
|
||||
}
|
||||
|
||||
action_show_wallpaper.setOnClickListener {
|
||||
maintainScrollPosition {
|
||||
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_title_show_wallpaper))
|
||||
.setSelectedValue(Preferences.showWallpaper && activity?.checkGrantedPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == true)
|
||||
.addItem(
|
||||
getString(R.string.settings_visible),
|
||||
true
|
||||
)
|
||||
.addItem(
|
||||
getString(R.string.settings_not_visible),
|
||||
false
|
||||
)
|
||||
.addOnSelectItemListener { value ->
|
||||
if (value) {
|
||||
requirePermission()
|
||||
} else {
|
||||
Preferences.showWallpaper = value
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
|
||||
action_show_wallpaper.setOnClickListener {
|
||||
show_wallpaper_toggle.isChecked = !show_wallpaper_toggle.isChecked
|
||||
}
|
||||
|
||||
show_wallpaper_toggle.setOnCheckedChangeListener { _, isChecked ->
|
||||
if (isChecked) {
|
||||
requirePermission()
|
||||
} else {
|
||||
Preferences.showWallpaper = isChecked
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,23 +184,29 @@ class SettingsFragment : Fragment() {
|
||||
activity?.openURI("https://github.com/tommasoberlose/another-widget/issues")
|
||||
}
|
||||
|
||||
action_privacy_policy.setOnClickListener {
|
||||
activity?.openURI("https://github.com/tommasoberlose/another-widget/blob/master/privacy-policy.md")
|
||||
}
|
||||
|
||||
action_help_dev.setOnClickListener {
|
||||
startActivity(Intent(requireContext(), SupportDevActivity::class.java))
|
||||
}
|
||||
|
||||
action_refresh_widget.setOnClickListener {
|
||||
WeatherHelper.updateWeather(requireContext())
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
WeatherHelper.updateWeather(requireContext())
|
||||
}
|
||||
CalendarHelper.updateEventList(requireContext())
|
||||
MediaPlayerHelper.updatePlayingMediaInfo(requireContext())
|
||||
}
|
||||
}
|
||||
|
||||
private fun maintainScrollPosition(callback: () -> Unit) {
|
||||
val scrollPosition = scrollView.scrollY
|
||||
scrollView.isScrollable = false
|
||||
callback.invoke()
|
||||
lifecycleScope.launch {
|
||||
delay(200)
|
||||
scrollView.smoothScrollTo(0, scrollPosition)
|
||||
scrollView.isScrollable = true
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,8 +217,11 @@ class SettingsFragment : Fragment() {
|
||||
).withListener(object: MultiplePermissionsListener {
|
||||
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
|
||||
report?.let {
|
||||
Preferences.showWallpaper = false
|
||||
Preferences.showWallpaper = report.areAllPermissionsGranted()
|
||||
if (report.areAllPermissionsGranted()) {
|
||||
Preferences.showWallpaper = true
|
||||
} else {
|
||||
show_wallpaper_toggle?.isChecked = false
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun onPermissionRationaleShouldBeShown(
|
||||
|
@ -5,10 +5,12 @@ import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.BuildCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
@ -23,11 +25,15 @@ import com.karumi.dexter.listener.PermissionRequest
|
||||
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
|
||||
import com.tommasoberlose.anotherwidget.components.IconPackSelector
|
||||
import com.tommasoberlose.anotherwidget.components.MaterialBottomSheetDialog
|
||||
import com.tommasoberlose.anotherwidget.databinding.FragmentWeatherSettingsBinding
|
||||
import com.tommasoberlose.anotherwidget.global.Constants
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import com.tommasoberlose.anotherwidget.global.RequestCode
|
||||
import com.tommasoberlose.anotherwidget.helpers.ColorHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper
|
||||
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
|
||||
import com.tommasoberlose.anotherwidget.receivers.WeatherReceiver
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.ChooseApplicationActivity
|
||||
import com.tommasoberlose.anotherwidget.ui.activities.CustomLocationActivity
|
||||
@ -79,6 +85,8 @@ class WeatherTabFragment : Fragment() {
|
||||
binding: FragmentWeatherSettingsBinding,
|
||||
viewModel: MainViewModel
|
||||
) {
|
||||
binding.isWeatherVisible = Preferences.showWeather
|
||||
|
||||
viewModel.showWeatherWarning.observe(viewLifecycleOwner, Observer {
|
||||
weather_warning?.isVisible = it
|
||||
checkLocationPermission()
|
||||
@ -94,15 +102,24 @@ class WeatherTabFragment : Fragment() {
|
||||
checkLocationPermission()
|
||||
})
|
||||
|
||||
viewModel.weatherProviderApi.observe(viewLifecycleOwner, Observer {
|
||||
viewModel.weatherProvider.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
label_weather_provider.text = WeatherHelper.getProviderName(requireContext(), Constants.WeatherProvider.fromInt(it)!!)
|
||||
checkWeatherProviderConfig()
|
||||
}
|
||||
checkLocationPermission()
|
||||
})
|
||||
|
||||
viewModel.weatherProviderError.observe(viewLifecycleOwner, Observer {
|
||||
checkWeatherProviderConfig()
|
||||
})
|
||||
|
||||
viewModel.weatherProviderLocationError.observe(viewLifecycleOwner, Observer {
|
||||
checkWeatherProviderConfig()
|
||||
})
|
||||
|
||||
viewModel.customLocationAdd.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
background_location_warning.isVisible = it == ""
|
||||
label_custom_location?.text =
|
||||
if (it == "") getString(R.string.custom_location_gps) else it
|
||||
}
|
||||
@ -124,6 +141,19 @@ class WeatherTabFragment : Fragment() {
|
||||
checkLocationPermission()
|
||||
})
|
||||
|
||||
viewModel.weatherIconPack.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
label_weather_icon_pack?.text = getString(R.string.settings_weather_icon_pack_default).format((it + 1))
|
||||
// weather_icon_pack.setImageDrawable(ContextCompat.getDrawable(requireContext(), WeatherHelper.getWeatherIconResource("02d")))
|
||||
// if (it == Constants.WeatherIconPack.MINIMAL.value) {
|
||||
// weather_icon_pack.setColorFilter(ContextCompat.getColor(requireContext(), R.color.colorPrimaryText))
|
||||
// } else {
|
||||
// weather_icon_pack.setColorFilter(ContextCompat.getColor(requireContext(), android.R.color.transparent))
|
||||
// }
|
||||
}
|
||||
checkLocationPermission()
|
||||
})
|
||||
|
||||
viewModel.weatherAppName.observe(viewLifecycleOwner, Observer {
|
||||
maintainScrollPosition {
|
||||
weather_app_label?.text =
|
||||
@ -140,11 +170,17 @@ class WeatherTabFragment : Fragment() {
|
||||
|
||||
if (activity?.checkGrantedPermission(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else Manifest.permission.ACCESS_FINE_LOCATION) == true) {
|
||||
location_permission_alert?.isVisible = false
|
||||
background_location_warning.isVisible = Preferences.customLocationAdd == ""
|
||||
WeatherReceiver.setUpdates(requireContext())
|
||||
} else if (Preferences.showWeather && Preferences.customLocationAdd == "") {
|
||||
location_permission_alert?.isVisible = true
|
||||
background_location_warning.isVisible = false
|
||||
location_permission_alert?.setOnClickListener {
|
||||
requirePermission()
|
||||
MaterialBottomSheetDialog(requireContext(), message = getString(R.string.background_location_warning))
|
||||
.setPositiveButton(getString(android.R.string.ok)) {
|
||||
requirePermission()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
} else {
|
||||
location_permission_alert?.isVisible = false
|
||||
@ -152,11 +188,11 @@ class WeatherTabFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun checkWeatherProviderConfig() {
|
||||
label_weather_provider_api_key?.text =
|
||||
if (Preferences.weatherProviderApi == "") getString(R.string.settings_weather_provider_api_key_subtitle_not_set) else getString(
|
||||
R.string.settings_weather_provider_api_key_subtitle_all_set
|
||||
)
|
||||
label_weather_provider_api_key?.setTextColor(ContextCompat.getColor(requireContext(), if (Preferences.weatherProviderApi == "" && Preferences.showWeather) R.color.errorColorText else R.color.colorSecondaryText))
|
||||
weather_provider_error.isVisible = Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-"
|
||||
weather_provider_error?.text = Preferences.weatherProviderError
|
||||
|
||||
weather_provider_location_error.isVisible = Preferences.weatherProviderLocationError != ""
|
||||
weather_provider_location_error?.text = Preferences.weatherProviderLocationError
|
||||
}
|
||||
|
||||
private fun setupListener() {
|
||||
@ -172,7 +208,7 @@ class WeatherTabFragment : Fragment() {
|
||||
Preferences.showWeather = enabled
|
||||
}
|
||||
|
||||
action_weather_provider_api_key.setOnClickListener {
|
||||
action_weather_provider.setOnClickListener {
|
||||
if (Preferences.showWeather) {
|
||||
startActivityForResult(
|
||||
Intent(requireContext(), WeatherProviderActivity::class.java),
|
||||
@ -196,6 +232,11 @@ class WeatherTabFragment : Fragment() {
|
||||
.addItem(getString(R.string.fahrenheit), "F")
|
||||
.addItem(getString(R.string.celsius), "C")
|
||||
.addOnSelectItemListener { value ->
|
||||
if (value != Preferences.weatherTempUnit) {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
WeatherHelper.updateWeather(requireContext())
|
||||
}
|
||||
}
|
||||
Preferences.weatherTempUnit = value
|
||||
}.show()
|
||||
}
|
||||
@ -215,6 +256,12 @@ class WeatherTabFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
action_weather_icon_pack.setOnClickListener {
|
||||
if (Preferences.showWeather) {
|
||||
IconPackSelector(requireContext(), header = getString(R.string.settings_weather_icon_pack_title)).show()
|
||||
}
|
||||
}
|
||||
|
||||
action_weather_app.setOnClickListener {
|
||||
if (Preferences.showWeather) {
|
||||
startActivityForResult(
|
||||
@ -240,7 +287,7 @@ class WeatherTabFragment : Fragment() {
|
||||
MainWidget.updateWidget(requireContext())
|
||||
}
|
||||
RequestCode.WEATHER_PROVIDER_REQUEST_CODE.code -> {
|
||||
WeatherReceiver.setOneTimeUpdate(requireContext())
|
||||
checkLocationPermission()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -272,11 +319,11 @@ class WeatherTabFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun maintainScrollPosition(callback: () -> Unit) {
|
||||
val scrollPosition = scrollView.scrollY
|
||||
scrollView.isScrollable = false
|
||||
callback.invoke()
|
||||
lifecycleScope.launch {
|
||||
delay(200)
|
||||
scrollView.smoothScrollTo(0, scrollPosition)
|
||||
scrollView.isScrollable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,11 @@ package com.tommasoberlose.anotherwidget.ui.viewmodels
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.chibatching.kotpref.livedata.asLiveData
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
|
||||
class CustomDateViewModel(application: Application) : AndroidViewModel(application) {
|
||||
val dateInput: MutableLiveData<String> = MutableLiveData(if (Preferences.dateFormat == "") "EEEE, MMM dd" else Preferences.dateFormat)
|
||||
val isDateCapitalize = MutableLiveData<Boolean>(Preferences.isDateCapitalize)
|
||||
val isDateUppercase = MutableLiveData<Boolean>(Preferences.isDateUppercase)
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.tommasoberlose.anotherwidget.ui.viewmodels
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import android.content.pm.ResolveInfo
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.koolio.library.DownloadableFontList
|
||||
import com.koolio.library.Font
|
||||
import com.koolio.library.FontList
|
||||
import com.tommasoberlose.anotherwidget.BuildConfig
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class CustomFontViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
val fontList: MutableLiveData<ArrayList<Font>> = MutableLiveData()
|
||||
val searchInput: MutableLiveData<String> = MutableLiveData("")
|
||||
|
||||
init {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
|
||||
|
||||
val fontListCallback: DownloadableFontList.FontListCallback =
|
||||
object : DownloadableFontList.FontListCallback {
|
||||
override fun onFontListRetrieved(downloadedList: FontList?) {
|
||||
fontList.postValue(downloadedList?.fontArrayList)
|
||||
}
|
||||
|
||||
override fun onTypefaceRequestFailed(reason: Int) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
DownloadableFontList.requestDownloadableFontList(fontListCallback, BuildConfig.GOOGLE_API_KEY)
|
||||
}
|
||||
}
|
||||
}
|
@ -9,14 +9,27 @@ class MainViewModel : ViewModel() {
|
||||
// General Settings
|
||||
val textGlobalColor = Preferences.asLiveData(Preferences::textGlobalColor)
|
||||
val textGlobalAlpha = Preferences.asLiveData(Preferences::textGlobalAlpha)
|
||||
val textSecondaryColor = Preferences.asLiveData(Preferences::textSecondaryColor)
|
||||
val textSecondaryAlpha = Preferences.asLiveData(Preferences::textSecondaryAlpha)
|
||||
val backgroundCardColor = Preferences.asLiveData(Preferences::backgroundCardColor)
|
||||
val backgroundCardAlpha = Preferences.asLiveData(Preferences::backgroundCardAlpha)
|
||||
val textGlobalColorDark = Preferences.asLiveData(Preferences::textGlobalColorDark)
|
||||
val textGlobalAlphaDark = Preferences.asLiveData(Preferences::textGlobalAlphaDark)
|
||||
val textSecondaryColorDark = Preferences.asLiveData(Preferences::textSecondaryColorDark)
|
||||
val textSecondaryAlphaDark = Preferences.asLiveData(Preferences::textSecondaryAlphaDark)
|
||||
val backgroundCardColorDark = Preferences.asLiveData(Preferences::backgroundCardColorDark)
|
||||
val backgroundCardAlphaDark = Preferences.asLiveData(Preferences::backgroundCardAlphaDark)
|
||||
val textMainSize = Preferences.asLiveData(Preferences::textMainSize)
|
||||
val textSecondSize = Preferences.asLiveData(Preferences::textSecondSize)
|
||||
val textShadow = Preferences.asLiveData(Preferences::textShadow)
|
||||
val textShadowDark = Preferences.asLiveData(Preferences::textShadowDark)
|
||||
val customFont = Preferences.asLiveData(Preferences::customFont)
|
||||
val customFontFile = Preferences.asLiveData(Preferences::customFontFile)
|
||||
val customFontName = Preferences.asLiveData(Preferences::customFontName)
|
||||
val customFontVariant = Preferences.asLiveData(Preferences::customFontVariant)
|
||||
val secondRowInformation = Preferences.asLiveData(Preferences::secondRowInformation)
|
||||
val showDividers = Preferences.asLiveData(Preferences::showDividers)
|
||||
val secondRowTopMargin = Preferences.asLiveData(Preferences::secondRowTopMargin)
|
||||
|
||||
// Calendar Settings
|
||||
val showEvents = Preferences.asLiveData(Preferences::showEvents)
|
||||
@ -27,12 +40,17 @@ class MainViewModel : ViewModel() {
|
||||
val showNextEvent = Preferences.asLiveData(Preferences::showNextEvent)
|
||||
val openEventDetails = Preferences.asLiveData(Preferences::openEventDetails)
|
||||
val calendarAppName = Preferences.asLiveData(Preferences::calendarAppName)
|
||||
val widgetUpdateFrequency = Preferences.asLiveData(Preferences::widgetUpdateFrequency)
|
||||
val showOnlyBusyEvents = Preferences.asLiveData(Preferences::showOnlyBusyEvents)
|
||||
|
||||
// Clock Settings
|
||||
val showClock = Preferences.asLiveData(Preferences::showClock)
|
||||
val clockTextSize = Preferences.asLiveData(Preferences::clockTextSize)
|
||||
val clockTextColor = Preferences.asLiveData(Preferences::clockTextColor)
|
||||
val clockTextAlpha = Preferences.asLiveData(Preferences::clockTextAlpha)
|
||||
val clockTextColorDark = Preferences.asLiveData(Preferences::clockTextColorDark)
|
||||
val clockTextAlphaDark = Preferences.asLiveData(Preferences::clockTextAlphaDark)
|
||||
val showAMPMIndicator = Preferences.asLiveData(Preferences::showAMPMIndicator)
|
||||
|
||||
val clockAppName = Preferences.asLiveData(Preferences::clockAppName)
|
||||
val dateFormat = Preferences.asLiveData(Preferences::dateFormat)
|
||||
@ -46,16 +64,24 @@ class MainViewModel : ViewModel() {
|
||||
val weatherRefreshPeriod = Preferences.asLiveData(Preferences::weatherRefreshPeriod)
|
||||
|
||||
val weatherAppName = Preferences.asLiveData(Preferences::weatherAppName)
|
||||
val weatherProviderApi = Preferences.asLiveData(Preferences::weatherProviderApi)
|
||||
val weatherProviderApi = Preferences.asLiveData(Preferences::weatherProviderApiOpen)
|
||||
|
||||
val customLocationAdd = Preferences.asLiveData(Preferences::customLocationAdd)
|
||||
|
||||
val showWeatherWarning = Preferences.asLiveData(Preferences::showWeatherWarning)
|
||||
val weatherIconPack = Preferences.asLiveData(Preferences::weatherIconPack)
|
||||
val weatherProvider = Preferences.asLiveData(Preferences::weatherProvider)
|
||||
val weatherProviderError = Preferences.asLiveData(Preferences::weatherProviderError)
|
||||
val weatherProviderLocationError = Preferences.asLiveData(Preferences::weatherProviderLocationError)
|
||||
|
||||
// Glance
|
||||
val showGlance = Preferences.asLiveData(Preferences::showGlance)
|
||||
val showMusic = Preferences.asLiveData(Preferences::showMusic)
|
||||
val showNextAlarm = Preferences.asLiveData(Preferences::showNextAlarm)
|
||||
val showBatteryCharging = Preferences.asLiveData(Preferences::showBatteryCharging)
|
||||
val showDailySteps = Preferences.asLiveData(Preferences::showDailySteps)
|
||||
val customInfo = Preferences.asLiveData(Preferences::customNotes)
|
||||
val musicPlayersFilter = Preferences.asLiveData(Preferences::musicPlayersFilter)
|
||||
|
||||
// Advanced Settings
|
||||
val darkThemePreference = Preferences.asLiveData(Preferences::darkThemePreference)
|
||||
|
@ -0,0 +1,38 @@
|
||||
package com.tommasoberlose.anotherwidget.ui.viewmodels
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ResolveInfo
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.*
|
||||
import com.chibatching.kotpref.livedata.asLiveData
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class MusicPlayersFilterViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
val pm: PackageManager by lazy { application.packageManager }
|
||||
val appList: MutableLiveData<List<ResolveInfo>> = MutableLiveData()
|
||||
val searchInput: MutableLiveData<String> = MutableLiveData("")
|
||||
var musicPlayersFilter = Preferences.asLiveData(Preferences::musicPlayersFilter)
|
||||
|
||||
init {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val mainIntent = Intent(Intent.ACTION_MAIN, null).apply {
|
||||
addCategory(Intent.CATEGORY_LAUNCHER)
|
||||
}
|
||||
|
||||
val app = application.packageManager.queryIntentActivities(mainIntent, 0)
|
||||
val sortedApp = app.sortedWith(Comparator { app1: ResolveInfo, app2: ResolveInfo ->
|
||||
app1.loadLabel(pm).toString().compareTo(app2.loadLabel(pm).toString())
|
||||
})
|
||||
withContext(Dispatchers.Main) {
|
||||
appList.postValue(sortedApp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.tommasoberlose.anotherwidget.ui.viewmodels
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.chibatching.kotpref.livedata.asLiveData
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.global.Constants
|
||||
import com.tommasoberlose.anotherwidget.global.Preferences
|
||||
|
||||
class WeatherProviderViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
val weatherProvider = Preferences.asLiveData(Preferences::weatherProvider)
|
||||
val weatherProviderError = Preferences.asLiveData(Preferences::weatherProviderError)
|
||||
val weatherProviderLocationError = Preferences.asLiveData(Preferences::weatherProviderLocationError)
|
||||
|
||||
|
||||
}
|
@ -11,14 +11,18 @@ import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import android.os.Looper
|
||||
import android.text.format.DateUtils
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.RemoteViews
|
||||
import android.widget.TextView
|
||||
import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.provider.FontRequest
|
||||
import androidx.core.provider.FontsContractCompat
|
||||
import androidx.core.view.isVisible
|
||||
import com.tommasoberlose.anotherwidget.R
|
||||
import com.tommasoberlose.anotherwidget.db.EventRepository
|
||||
@ -26,15 +30,16 @@ 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.receivers.*
|
||||
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
|
||||
import com.tommasoberlose.anotherwidget.utils.getCapWordString
|
||||
import com.tommasoberlose.anotherwidget.utils.toPixel
|
||||
import com.tommasoberlose.anotherwidget.utils.*
|
||||
import kotlinx.android.synthetic.main.the_widget.view.*
|
||||
import java.lang.Exception
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
class MainWidget : AppWidgetProvider() {
|
||||
@ -85,12 +90,17 @@ class MainWidget : AppWidgetProvider() {
|
||||
appWidgetId: Int) {
|
||||
val displayMetrics = Resources.getSystem().displayMetrics
|
||||
val width = displayMetrics.widthPixels
|
||||
val height = displayMetrics.heightPixels
|
||||
|
||||
val dimensions = WidgetHelper.WidgetSizeProvider(context, appWidgetManager).getWidgetsSize(appWidgetId)
|
||||
generateWidgetView(context, appWidgetId, appWidgetManager, dimensions.first - 8.toPixel(context) /*width - 16.toPixel(context)*/)
|
||||
|
||||
WidgetHelper.runWithCustomTypeface(context) {
|
||||
generateWidgetView(context, appWidgetId, appWidgetManager, min(dimensions.first - 8.toPixel(context), min(width, height) - 16.toPixel(context)), it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateWidgetView(context: Context, appWidgetId: Int, appWidgetManager: AppWidgetManager, w: Int) {
|
||||
private fun generateWidgetView(context: Context, appWidgetId: Int, appWidgetManager: AppWidgetManager, w: Int, typeface: Typeface? = null) {
|
||||
|
||||
var views = RemoteViews(context.packageName, R.layout.the_widget_sans)
|
||||
|
||||
try {
|
||||
@ -98,18 +108,18 @@ class MainWidget : AppWidgetProvider() {
|
||||
views.setInt(
|
||||
R.id.widget_shape_background,
|
||||
"setColorFilter",
|
||||
ColorHelper.getBackgroundColorRgb()
|
||||
ColorHelper.getBackgroundColorRgb(context.isDarkTheme())
|
||||
)
|
||||
views.setInt(
|
||||
R.id.widget_shape_background,
|
||||
"setImageAlpha",
|
||||
ColorHelper.getBackgroundAlpha()
|
||||
ColorHelper.getBackgroundAlpha(context.isDarkTheme())
|
||||
)
|
||||
val refreshIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
appWidgetId,
|
||||
IntentHelper.getWidgetUpdateIntent(context),
|
||||
0
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.widget_shape_background, refreshIntent)
|
||||
} catch (ex: Exception) {
|
||||
@ -122,7 +132,8 @@ class MainWidget : AppWidgetProvider() {
|
||||
|
||||
// Setup listener
|
||||
try {
|
||||
val generatedView = generateWidgetView(context)
|
||||
|
||||
val generatedView = generateWidgetView(context, typeface)
|
||||
views.setImageViewBitmap(
|
||||
R.id.bitmap_container,
|
||||
BitmapHelper.getBitmapFromView(generatedView, width = w)
|
||||
@ -138,9 +149,8 @@ class MainWidget : AppWidgetProvider() {
|
||||
}
|
||||
|
||||
private fun updateCalendarView(context: Context, v: View, views: RemoteViews, widgetID: Int): RemoteViews {
|
||||
val eventRepository = EventRepository(context)
|
||||
try {
|
||||
val eventRepository = EventRepository(context)
|
||||
|
||||
views.setImageViewBitmap(
|
||||
R.id.empty_date_rect,
|
||||
BitmapHelper.getBitmapFromView(v.empty_date, draw = false)
|
||||
@ -155,7 +165,7 @@ class MainWidget : AppWidgetProvider() {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getCalendarIntent(context),
|
||||
0
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.empty_date_rect, calPIntent)
|
||||
|
||||
@ -178,7 +188,7 @@ class MainWidget : AppWidgetProvider() {
|
||||
context,
|
||||
NewCalendarEventReceiver::class.java
|
||||
).apply { action = Actions.ACTION_GO_TO_NEXT_EVENT },
|
||||
0
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
)
|
||||
|
||||
@ -196,7 +206,7 @@ class MainWidget : AppWidgetProvider() {
|
||||
context,
|
||||
NewCalendarEventReceiver::class.java
|
||||
).apply { action = Actions.ACTION_GO_TO_PREVIOUS_EVENT },
|
||||
0
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
)
|
||||
} else {
|
||||
@ -208,7 +218,7 @@ class MainWidget : AppWidgetProvider() {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getEventIntent(context, nextEvent),
|
||||
0
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.next_event_rect, pIntent)
|
||||
views.setOnClickPendingIntent(R.id.next_event_difference_time_rect, pIntent)
|
||||
@ -231,7 +241,7 @@ class MainWidget : AppWidgetProvider() {
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getGoogleMapsIntentFromAddress(context, nextEvent.address),
|
||||
0
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.second_row_rect, mapIntent)
|
||||
} else {
|
||||
@ -243,7 +253,7 @@ class MainWidget : AppWidgetProvider() {
|
||||
nextEvent,
|
||||
forceEventDetails = true
|
||||
),
|
||||
0
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.second_row_rect, pIntentDetail)
|
||||
}
|
||||
@ -261,56 +271,123 @@ class MainWidget : AppWidgetProvider() {
|
||||
|
||||
views.setViewVisibility(R.id.empty_layout_rect, View.GONE)
|
||||
views.setViewVisibility(R.id.calendar_layout_rect, View.VISIBLE)
|
||||
} else if (MediaPlayerHelper.isSomeonePlaying(context)) {
|
||||
val musicIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getMusicIntent(context),
|
||||
0
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.second_row_rect, musicIntent)
|
||||
|
||||
views.setImageViewBitmap(
|
||||
R.id.next_event_rect,
|
||||
BitmapHelper.getBitmapFromView(v.next_event, draw = false)
|
||||
views.setViewVisibility(
|
||||
R.id.second_row_top_margin_small_sans,
|
||||
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.SMALL.value) View.VISIBLE else View.GONE
|
||||
)
|
||||
|
||||
views.setImageViewBitmap(
|
||||
R.id.second_row_rect,
|
||||
BitmapHelper.getBitmapFromView(v.second_row, draw = false)
|
||||
views.setViewVisibility(
|
||||
R.id.second_row_top_margin_medium_sans,
|
||||
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.MEDIUM.value) View.VISIBLE else View.GONE
|
||||
)
|
||||
views.setViewVisibility(R.id.second_row_rect, View.VISIBLE)
|
||||
|
||||
views.setViewVisibility(R.id.empty_layout_rect, View.GONE)
|
||||
views.setViewVisibility(R.id.calendar_layout_rect, View.VISIBLE)
|
||||
views.setOnClickPendingIntent(R.id.next_event_rect, calPIntent)
|
||||
} else if (Preferences.showNextAlarm && nextAlarm != "") {
|
||||
val alarmIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
widgetID,
|
||||
IntentHelper.getClockIntent(context),
|
||||
0
|
||||
views.setViewVisibility(
|
||||
R.id.second_row_top_margin_large_sans,
|
||||
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.value) View.VISIBLE else View.GONE
|
||||
)
|
||||
views.setOnClickPendingIntent(R.id.second_row_rect, alarmIntent)
|
||||
} else if (GlanceProviderHelper.showGlanceProviders(context) && v.calendar_layout.isVisible) {
|
||||
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.second_row_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.second_row_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.second_row_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.second_row_rect, fitIntent)
|
||||
showSomething = true
|
||||
break@loop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
views.setImageViewBitmap(
|
||||
R.id.next_event_rect,
|
||||
BitmapHelper.getBitmapFromView(v.next_event, draw = false)
|
||||
)
|
||||
|
||||
views.setImageViewBitmap(
|
||||
R.id.second_row_rect,
|
||||
BitmapHelper.getBitmapFromView(v.second_row, draw = false)
|
||||
)
|
||||
views.setViewVisibility(R.id.second_row_rect, View.VISIBLE)
|
||||
if (showSomething) {
|
||||
views.setImageViewBitmap(
|
||||
R.id.next_event_rect,
|
||||
BitmapHelper.getBitmapFromView(v.next_event, draw = false)
|
||||
)
|
||||
|
||||
views.setViewVisibility(R.id.empty_layout_rect, View.GONE)
|
||||
views.setViewVisibility(R.id.calendar_layout_rect, View.VISIBLE)
|
||||
views.setOnClickPendingIntent(R.id.next_event_rect, calPIntent)
|
||||
views.setImageViewBitmap(
|
||||
R.id.second_row_rect,
|
||||
BitmapHelper.getBitmapFromView(v.second_row, draw = false)
|
||||
)
|
||||
|
||||
views.setViewVisibility(R.id.second_row_rect, View.VISIBLE)
|
||||
views.setViewVisibility(R.id.empty_layout_rect, View.GONE)
|
||||
views.setViewVisibility(R.id.calendar_layout_rect, View.VISIBLE)
|
||||
views.setOnClickPendingIntent(R.id.next_event_rect, calPIntent)
|
||||
|
||||
|
||||
views.setViewVisibility(
|
||||
R.id.second_row_top_margin_small_sans,
|
||||
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.SMALL.value) View.VISIBLE else View.GONE
|
||||
)
|
||||
views.setViewVisibility(
|
||||
R.id.second_row_top_margin_medium_sans,
|
||||
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.MEDIUM.value) View.VISIBLE else View.GONE
|
||||
)
|
||||
views.setViewVisibility(
|
||||
R.id.second_row_top_margin_large_sans,
|
||||
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.value) View.VISIBLE else View.GONE
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
CrashlyticsReceiver.sendCrash(context, ex)
|
||||
} finally {
|
||||
eventRepository.close()
|
||||
}
|
||||
|
||||
return views
|
||||
@ -346,7 +423,7 @@ class MainWidget : AppWidgetProvider() {
|
||||
BitmapHelper.getBitmapFromView(v.calendar_weather, draw = false)
|
||||
)
|
||||
|
||||
if (WidgetHelper.showSpecialWeather(context)) {
|
||||
if (GlanceProviderHelper.showGlanceProviders(context)) {
|
||||
views.setViewVisibility(R.id.calendar_weather_rect, View.GONE)
|
||||
} else {
|
||||
views.setViewVisibility(R.id.special_weather_rect, View.GONE)
|
||||
@ -373,8 +450,8 @@ class MainWidget : AppWidgetProvider() {
|
||||
views.setViewVisibility(R.id.clock_bottom_margin_medium, View.GONE)
|
||||
views.setViewVisibility(R.id.clock_bottom_margin_large, View.GONE)
|
||||
} else {
|
||||
views.setTextColor(R.id.time, ColorHelper.getClockFontColor())
|
||||
views.setTextColor(R.id.time_am_pm, ColorHelper.getClockFontColor())
|
||||
views.setTextColor(R.id.time, ColorHelper.getClockFontColor(context.isDarkTheme()))
|
||||
views.setTextColor(R.id.time_am_pm, ColorHelper.getClockFontColor(context.isDarkTheme()))
|
||||
views.setTextViewTextSize(
|
||||
R.id.time,
|
||||
TypedValue.COMPLEX_UNIT_SP,
|
||||
@ -394,7 +471,7 @@ class MainWidget : AppWidgetProvider() {
|
||||
views.setOnClickPendingIntent(R.id.time, clockPIntent)
|
||||
views.setOnClickPendingIntent(R.id.time_am_pm, clockPIntent)
|
||||
views.setViewVisibility(R.id.time, View.VISIBLE)
|
||||
views.setViewVisibility(R.id.time_am_pm, View.VISIBLE)
|
||||
views.setViewVisibility(R.id.time_am_pm, if (Preferences.showAMPMIndicator) View.VISIBLE else View.GONE)
|
||||
|
||||
views.setViewVisibility(
|
||||
R.id.clock_bottom_margin_none,
|
||||
@ -423,11 +500,16 @@ class MainWidget : AppWidgetProvider() {
|
||||
|
||||
|
||||
// Generates the widget bitmap from the view
|
||||
fun generateWidgetView(context: Context): View {
|
||||
fun generateWidgetView(context: Context, typeface: Typeface? = null): View {
|
||||
val eventRepository = EventRepository(context)
|
||||
val v = View.inflate(context, R.layout.the_widget, null)
|
||||
|
||||
val now = Calendar.getInstance()
|
||||
v.loader.isVisible = false
|
||||
|
||||
val now = Calendar.getInstance().apply {
|
||||
set(Calendar.SECOND, 0)
|
||||
set(Calendar.MILLISECOND, 0)
|
||||
}
|
||||
|
||||
v.empty_layout.visibility = View.VISIBLE
|
||||
v.calendar_layout.visibility = View.GONE
|
||||
@ -442,12 +524,14 @@ class MainWidget : AppWidgetProvider() {
|
||||
|
||||
if (Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null) {
|
||||
// Multiple counter
|
||||
v.action_next.isVisible = Preferences.showNextEvent && eventRepository.getEventsCount() > 1
|
||||
v.action_previous.isVisible = Preferences.showNextEvent && eventRepository.getEventsCount() > 1
|
||||
v.action_next.isVisible =
|
||||
Preferences.showNextEvent && eventRepository.getEventsCount() > 1
|
||||
v.action_previous.isVisible =
|
||||
Preferences.showNextEvent && eventRepository.getEventsCount() > 1
|
||||
|
||||
v.next_event.text = nextEvent.title
|
||||
|
||||
if (Preferences.showDiffTime && now.timeInMillis < (nextEvent.startDate - 1000 * 60 * 60)) {
|
||||
if (Preferences.showDiffTime && now.timeInMillis < nextEvent.startDate) {
|
||||
v.next_event_difference_time.text = if (!nextEvent.allDay) {
|
||||
SettingsStringHelper.getDifferenceText(
|
||||
context,
|
||||
@ -456,7 +540,11 @@ class MainWidget : AppWidgetProvider() {
|
||||
)
|
||||
.toLowerCase(Locale.getDefault())
|
||||
} else {
|
||||
SettingsStringHelper.getAllDayEventDifferenceText(context, now.timeInMillis, nextEvent.startDate).toLowerCase(Locale.getDefault())
|
||||
SettingsStringHelper.getAllDayEventDifferenceText(
|
||||
context,
|
||||
now.timeInMillis,
|
||||
nextEvent.startDate
|
||||
).toLowerCase(Locale.getDefault())
|
||||
}
|
||||
v.next_event_difference_time.visibility = View.VISIBLE
|
||||
} else {
|
||||
@ -464,15 +552,30 @@ class MainWidget : AppWidgetProvider() {
|
||||
}
|
||||
|
||||
if (nextEvent.address != "" && Preferences.secondRowInformation == 1) {
|
||||
v.second_row_icon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.round_place))
|
||||
v.second_row_icon.setImageDrawable(
|
||||
ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.round_place
|
||||
)
|
||||
)
|
||||
v.next_event_date.text = nextEvent.address
|
||||
} else {
|
||||
v.second_row_icon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.round_today))
|
||||
v.second_row_icon.setImageDrawable(
|
||||
ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.round_today
|
||||
)
|
||||
)
|
||||
if (!nextEvent.allDay) {
|
||||
val startHour = DateFormat.getTimeInstance(DateFormat.SHORT).format(nextEvent.startDate)
|
||||
val endHour = DateFormat.getTimeInstance(DateFormat.SHORT).format(nextEvent.endDate)
|
||||
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)
|
||||
var dayDiff =
|
||||
TimeUnit.MILLISECONDS.toDays(nextEvent.endDate - nextEvent.startDate)
|
||||
|
||||
val startCal = Calendar.getInstance()
|
||||
startCal.timeInMillis = nextEvent.startDate
|
||||
@ -482,55 +585,180 @@ class MainWidget : AppWidgetProvider() {
|
||||
|
||||
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)) {
|
||||
} 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))
|
||||
multipleDay = String.format(
|
||||
" (+%s%s)",
|
||||
dayDiff,
|
||||
context.getString(R.string.day_char)
|
||||
)
|
||||
}
|
||||
v.next_event_date.text = String.format("%s - %s%s", startHour, endHour, multipleDay)
|
||||
v.next_event_date.text =
|
||||
String.format("%s - %s%s", startHour, endHour, multipleDay)
|
||||
|
||||
} else {
|
||||
val flags: Int = DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_ABBREV_MONTH
|
||||
v.next_event_date.text = DateUtils.formatDateTime(context, now.timeInMillis, flags).getCapWordString()
|
||||
val flags: Int =
|
||||
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_ABBREV_MONTH
|
||||
v.next_event_date.text =
|
||||
DateUtils.formatDateTime(context, nextEvent.startDate, flags)
|
||||
}
|
||||
}
|
||||
|
||||
v.empty_layout.visibility = View.GONE
|
||||
v.calendar_layout.visibility = View.VISIBLE
|
||||
} else if (MediaPlayerHelper.isSomeonePlaying(context)) {
|
||||
v.second_row_icon.setImageDrawable(
|
||||
ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.round_music_note
|
||||
)
|
||||
)
|
||||
v.next_event.text = DateHelper.getDateText(context, now)
|
||||
v.next_event_date.text = MediaPlayerHelper.getMediaInfo()
|
||||
v.empty_layout.visibility = View.GONE
|
||||
v.calendar_layout.visibility = View.VISIBLE
|
||||
} else if (Preferences.showNextAlarm && nextAlarm != "") {
|
||||
v.second_row_icon.setImageDrawable(
|
||||
ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.round_alarm
|
||||
)
|
||||
)
|
||||
v.next_event.text = DateHelper.getDateText(context, now)
|
||||
v.next_event_date.text = AlarmHelper.getNextAlarm(context)
|
||||
v.empty_layout.visibility = View.GONE
|
||||
v.calendar_layout.visibility = View.VISIBLE
|
||||
|
||||
v.second_row_top_margin_small.visibility =
|
||||
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.SMALL.value) View.VISIBLE else View.GONE
|
||||
v.second_row_top_margin_medium.visibility =
|
||||
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.MEDIUM.value) View.VISIBLE else View.GONE
|
||||
v.second_row_top_margin_large.visibility =
|
||||
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.value) View.VISIBLE else View.GONE
|
||||
} else if (GlanceProviderHelper.showGlanceProviders(context)) {
|
||||
v.second_row_icon.isVisible = true
|
||||
var showSomething = false
|
||||
loop@ for (provider: Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(
|
||||
context
|
||||
)) {
|
||||
when (provider) {
|
||||
Constants.GlanceProviderId.PLAYING_SONG -> {
|
||||
if (MediaPlayerHelper.isSomeonePlaying(context)) {
|
||||
v.second_row_icon.setImageDrawable(
|
||||
ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.round_music_note
|
||||
)
|
||||
)
|
||||
v.next_event_date.text = MediaPlayerHelper.getMediaInfo()
|
||||
showSomething = true
|
||||
break@loop
|
||||
}
|
||||
}
|
||||
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
|
||||
if (Preferences.showNextAlarm && nextAlarm != "") {
|
||||
v.second_row_icon.setImageDrawable(
|
||||
ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.round_alarm
|
||||
)
|
||||
)
|
||||
v.next_event_date.text = AlarmHelper.getNextAlarm(context)
|
||||
showSomething = true
|
||||
break@loop
|
||||
}
|
||||
}
|
||||
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
|
||||
if (Preferences.showBatteryCharging) {
|
||||
BatteryHelper.updateBatteryInfo(context)
|
||||
if (Preferences.isCharging) {
|
||||
v.second_row_icon.isVisible = false
|
||||
val batteryLevel = BatteryHelper.getBatteryLevel(context)
|
||||
if (batteryLevel != 100) {
|
||||
v.next_event_date.text = context.getString(R.string.charging)
|
||||
} else {
|
||||
v.next_event_date.text =
|
||||
context.getString(R.string.charged)
|
||||
}
|
||||
showSomething = true
|
||||
break@loop
|
||||
} else if (Preferences.isBatteryLevelLow) {
|
||||
v.second_row_icon.isVisible = false
|
||||
v.next_event_date.text =
|
||||
context.getString(R.string.battery_low_warning)
|
||||
showSomething = true
|
||||
break@loop
|
||||
}
|
||||
}
|
||||
}
|
||||
Constants.GlanceProviderId.CUSTOM_INFO -> {
|
||||
if (Preferences.customNotes.isNotEmpty()) {
|
||||
v.second_row_icon.isVisible = false
|
||||
v.next_event_date.text = Preferences.customNotes
|
||||
v.next_event_date.gravity
|
||||
v.next_event_date.maxLines = 2
|
||||
showSomething = true
|
||||
break@loop
|
||||
}
|
||||
}
|
||||
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
|
||||
if (Preferences.showDailySteps && Preferences.googleFitSteps > 0) {
|
||||
v.second_row_icon.isVisible = false
|
||||
v.next_event_date.text =
|
||||
context.getString(R.string.daily_steps_counter)
|
||||
.format(Preferences.googleFitSteps)
|
||||
showSomething = true
|
||||
break@loop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showSomething) {
|
||||
v.next_event.text = DateHelper.getDateText(context, now)
|
||||
v.empty_layout.visibility = View.GONE
|
||||
v.calendar_layout.visibility = View.VISIBLE
|
||||
|
||||
v.second_row_top_margin_small.visibility =
|
||||
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.SMALL.value) View.VISIBLE else View.GONE
|
||||
v.second_row_top_margin_medium.visibility =
|
||||
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.MEDIUM.value) View.VISIBLE else View.GONE
|
||||
v.second_row_top_margin_large.visibility =
|
||||
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.value) View.VISIBLE else View.GONE
|
||||
} else {
|
||||
v.second_row_icon.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Color
|
||||
listOf<TextView>(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.next_event_date, v.divider2, v.calendar_temp, v.divider3, v.special_temp).forEach {
|
||||
it.setTextColor(ColorHelper.getFontColor())
|
||||
listOf<TextView>(
|
||||
v.empty_date,
|
||||
v.divider1,
|
||||
v.temp,
|
||||
v.next_event,
|
||||
v.next_event_difference_time,
|
||||
v.divider3,
|
||||
v.special_temp
|
||||
).forEach {
|
||||
it.setTextColor(ColorHelper.getFontColor(context.applicationContext.isDarkTheme()))
|
||||
}
|
||||
|
||||
listOf<ImageView>(v.second_row_icon, v.action_next, v.action_previous).forEach {
|
||||
it.setColorFilter(ColorHelper.getFontColor())
|
||||
if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.value) {
|
||||
listOf<ImageView>(v.action_next, v.action_previous)
|
||||
} else {
|
||||
listOf<ImageView>(
|
||||
v.action_next,
|
||||
v.action_previous,
|
||||
v.empty_weather_icon,
|
||||
v.special_weather_icon
|
||||
)
|
||||
}.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>(v.next_event_date, v.divider2, v.calendar_temp).forEach {
|
||||
it.setTextColor(ColorHelper.getSecondaryFontColor(context.applicationContext.isDarkTheme()))
|
||||
}
|
||||
|
||||
if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.value) {
|
||||
listOf<ImageView>(v.second_row_icon)
|
||||
} else {
|
||||
listOf<ImageView>(v.second_row_icon, v.weather_icon)
|
||||
}.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
|
||||
@ -553,11 +781,11 @@ class MainWidget : AppWidgetProvider() {
|
||||
v.second_row_icon.scaleX = Preferences.textSecondSize / 18f
|
||||
v.second_row_icon.scaleY = Preferences.textSecondSize / 18f
|
||||
|
||||
v.weather_icon.scaleX = Preferences.textSecondSize / 16f
|
||||
v.weather_icon.scaleY = Preferences.textSecondSize / 16f
|
||||
v.weather_icon.scaleX = Preferences.textSecondSize / 14f
|
||||
v.weather_icon.scaleY = Preferences.textSecondSize / 14f
|
||||
|
||||
v.empty_weather_icon.scaleX = Preferences.textMainSize / 20f
|
||||
v.empty_weather_icon.scaleY = Preferences.textMainSize / 20f
|
||||
v.empty_weather_icon.scaleX = Preferences.textMainSize / 18f
|
||||
v.empty_weather_icon.scaleY = Preferences.textMainSize / 18f
|
||||
|
||||
v.action_next.scaleX = Preferences.textMainSize / 28f
|
||||
v.action_next.scaleY = Preferences.textMainSize / 28f
|
||||
@ -570,34 +798,82 @@ class MainWidget : AppWidgetProvider() {
|
||||
|
||||
|
||||
// Shadows
|
||||
val shadowRadius = when (Preferences.textShadow) {
|
||||
0 -> 0f
|
||||
1 -> 5f
|
||||
2 -> 5f
|
||||
else -> 5f
|
||||
}
|
||||
val shadowColor = when (Preferences.textShadow) {
|
||||
0 -> Color.TRANSPARENT
|
||||
1 -> R.color.black_50
|
||||
2 -> Color.BLACK
|
||||
else -> R.color.black_50
|
||||
}
|
||||
val shadowDy = when (Preferences.textShadow) {
|
||||
0 -> 0f
|
||||
1 -> 0f
|
||||
2 -> 1f
|
||||
else -> 0f
|
||||
}
|
||||
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>(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.next_event_date, v.divider2, v.calendar_temp, v.divider3, v.special_temp).forEach {
|
||||
listOf<TextView>(
|
||||
v.empty_date,
|
||||
v.divider1,
|
||||
v.temp,
|
||||
v.next_event,
|
||||
v.next_event_difference_time,
|
||||
v.next_event_date,
|
||||
v.divider2,
|
||||
v.calendar_temp,
|
||||
v.divider3,
|
||||
v.special_temp
|
||||
).forEach {
|
||||
it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor)
|
||||
}
|
||||
|
||||
// Custom Font
|
||||
if (Preferences.customFont == Constants.CUSTOM_FONT_PRODUCT_SANS) {
|
||||
val productSans: Typeface = Typeface.createFromAsset(context.assets, "fonts/product_sans_regular.ttf")
|
||||
listOf<TextView>(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.next_event_date, v.divider2, v.calendar_temp, v.divider3, v.special_temp).forEach {
|
||||
it.typeface = productSans
|
||||
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>(
|
||||
v.empty_date,
|
||||
v.divider1,
|
||||
v.temp,
|
||||
v.next_event,
|
||||
v.next_event_difference_time,
|
||||
v.next_event_date,
|
||||
v.divider2,
|
||||
v.calendar_temp,
|
||||
v.divider3,
|
||||
v.special_temp
|
||||
).forEach {
|
||||
it.typeface = googleSans
|
||||
}
|
||||
} else if (Preferences.customFont == Constants.CUSTOM_FONT_DOWNLOADED && typeface != null) {
|
||||
listOf<TextView>(
|
||||
v.empty_date,
|
||||
v.divider1,
|
||||
v.temp,
|
||||
v.next_event,
|
||||
v.next_event_difference_time,
|
||||
v.next_event_date,
|
||||
v.divider2,
|
||||
v.calendar_temp,
|
||||
v.divider3,
|
||||
v.special_temp
|
||||
).forEach {
|
||||
it.typeface = typeface
|
||||
}
|
||||
}
|
||||
|
||||
@ -606,7 +882,12 @@ class MainWidget : AppWidgetProvider() {
|
||||
v.weather.visibility = View.VISIBLE
|
||||
v.calendar_weather.visibility = View.VISIBLE
|
||||
v.special_weather.visibility = View.VISIBLE
|
||||
val currentTemp = String.format(Locale.getDefault(), "%.0f °%s", Preferences.weatherTemp, Preferences.weatherRealTempUnit)
|
||||
val currentTemp = String.format(
|
||||
Locale.getDefault(),
|
||||
"%d °%s",
|
||||
Preferences.weatherTemp.roundToInt(),
|
||||
Preferences.weatherRealTempUnit
|
||||
)
|
||||
|
||||
val icon: String = Preferences.weatherIcon
|
||||
if (icon == "") {
|
||||
@ -614,9 +895,9 @@ class MainWidget : AppWidgetProvider() {
|
||||
v.empty_weather_icon.visibility = View.GONE
|
||||
v.special_weather_icon.visibility = View.GONE
|
||||
} else {
|
||||
v.weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(icon))
|
||||
v.empty_weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(icon))
|
||||
v.special_weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(icon))
|
||||
v.weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon))
|
||||
v.empty_weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon))
|
||||
v.special_weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon))
|
||||
v.weather_icon.visibility = View.VISIBLE
|
||||
v.empty_weather_icon.visibility = View.VISIBLE
|
||||
v.special_weather_icon.visibility = View.VISIBLE
|
||||
@ -626,7 +907,7 @@ class MainWidget : AppWidgetProvider() {
|
||||
v.calendar_temp.text = currentTemp
|
||||
v.special_temp.text = currentTemp
|
||||
|
||||
if (WidgetHelper.showSpecialWeather(context)) {
|
||||
if (GlanceProviderHelper.showGlanceProviders(context)) {
|
||||
v.calendar_weather.visibility = View.GONE
|
||||
} else {
|
||||
v.special_weather.visibility = View.GONE
|
||||
@ -642,6 +923,8 @@ class MainWidget : AppWidgetProvider() {
|
||||
it.isVisible = Preferences.showDividers
|
||||
}
|
||||
|
||||
eventRepository.close()
|
||||
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ fun View.expand() {
|
||||
}
|
||||
}
|
||||
|
||||
fun View.collapse() {
|
||||
fun View.collapse(duration: Long = 500L) {
|
||||
if (visibility != View.GONE) {
|
||||
val initialHeight = measuredHeight
|
||||
|
||||
@ -114,7 +114,7 @@ fun View.collapse() {
|
||||
}
|
||||
}
|
||||
|
||||
a.duration = 500L //(initialHeight / v.context.resources.displayMetrics.density).toLong()
|
||||
a.duration = duration //(initialHeight / v.context.resources.displayMetrics.density).toLong()
|
||||
startAnimation(a)
|
||||
}
|
||||
}
|
||||
@ -143,11 +143,11 @@ fun Context.isTablet(): Boolean {
|
||||
}
|
||||
|
||||
fun String.md5(): String {
|
||||
val MD5 = "MD5"
|
||||
val mD5 = "MD5"
|
||||
try {
|
||||
// Create MD5 Hash
|
||||
val digest = java.security.MessageDigest
|
||||
.getInstance(MD5)
|
||||
.getInstance(mD5)
|
||||
digest.update(toByteArray())
|
||||
val messageDigest = digest.digest()
|
||||
|
||||
@ -172,7 +172,7 @@ fun String.isValidEmail(): Boolean
|
||||
= this.isNotEmpty() &&
|
||||
Patterns.EMAIL_ADDRESS.matcher(this).matches()
|
||||
|
||||
fun Activity.isDarkTheme(): Boolean {
|
||||
fun Context.isDarkTheme(): Boolean {
|
||||
return resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
|
||||
}
|
||||
|
||||
@ -212,4 +212,22 @@ fun String.getCapWordString(): String {
|
||||
} catch (e: Exception) {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.checkIfFitInstalled(): Boolean {
|
||||
return try {
|
||||
packageManager.getPackageInfo("com.google.android.apps.fitness", PackageManager.GET_ACTIVITIES)
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun Intent.isDefaultSet(context: Context): Boolean {
|
||||
val pm = context.packageManager
|
||||
return try {
|
||||
resolveActivity(pm) != null && resolveActivity(pm).packageName.isNotBlank()
|
||||
} catch (ex: java.lang.Exception) {
|
||||
false
|
||||
}
|
||||
}
|
BIN
app/src/main/res/drawable-hdpi/ic_capitalize.png
Normal file
After Width: | Height: | Size: 471 B |
BIN
app/src/main/res/drawable-hdpi/outline_info_white.png
Normal file
After Width: | Height: | Size: 518 B |
BIN
app/src/main/res/drawable-hdpi/outline_info_white_18.png
Normal file
After Width: | Height: | Size: 449 B |
BIN
app/src/main/res/drawable-hdpi/outline_info_white_36.png
Normal file
After Width: | Height: | Size: 751 B |
BIN
app/src/main/res/drawable-hdpi/outline_info_white_48.png
Normal file
After Width: | Height: | Size: 953 B |
BIN
app/src/main/res/drawable-hdpi/round_access_alarm.png
Normal file
After Width: | Height: | Size: 868 B |
BIN
app/src/main/res/drawable-hdpi/round_access_alarm_black_18.png
Normal file
After Width: | Height: | Size: 459 B |