Compare commits

..

82 Commits

Author SHA1 Message Date
3fba50fd2c Fix #265 2021-01-11 09:49:00 +01:00
06583197c7 Fix widget refresh intent 2021-01-11 09:42:55 +01:00
5176331e84 Version 2.2.2-beta5 2021-01-11 01:34:38 +01:00
65f83caeb5 Merge pull request #267 from Moutony/patch-27
French v2.2.2 (120)
2021-01-11 01:33:22 +01:00
60e8c267bf Version 2.2.2-beta4 2021-01-11 01:32:29 +01:00
10a3204808 Fix #263 2021-01-11 01:30:31 +01:00
98c509ef27 French v2.2.2 (120) 2021-01-10 19:06:20 +01:00
ab1df499af beta version 2.2.2-beta3 2021-01-10 18:02:44 +01:00
ed9a4042c8 Fix #259 2021-01-10 17:56:48 +01:00
a102776214 Merge branch 'master' of github.com:tommasoberlose/another-widget into develop 2021-01-10 17:21:34 +01:00
0778ad4df5 Merge pull request #264 from Drumber/translation
Update German strings.xml
2021-01-10 17:21:27 +01:00
8dce8a74b3 Fix #242 2021-01-10 17:20:52 +01:00
24bb811f93 Fix update calendar service 2021-01-10 15:54:54 +01:00
0500e8d8e8 Update strings.xml
Added missing translations
2021-01-10 14:16:19 +01:00
d2087d094f Beta version 2.2.2-beta1 2021-01-10 12:52:20 +01:00
34fb35f2ab Fix #262 2021-01-10 12:48:17 +01:00
108ecdece0 Update transitions 2021-01-10 02:08:15 +01:00
e3f4995e93 Fix icon selector 2021-01-10 00:41:36 +01:00
0cdc5a3c6d Merge branch 'master' of github.com:tommasoberlose/another-widget into develop 2021-01-09 20:28:03 +01:00
9f039eec3c Merge pull request #258 from Moutony/patch-26
Update strings.xml
2021-01-09 20:27:51 +01:00
e9effbe799 Update animations 2021-01-09 20:26:23 +01:00
889783bb4e update ui 2021-01-09 18:33:22 +01:00
d32f680519 Update UI 2021-01-09 00:24:05 +01:00
c1d14f93bf Improve performances 2021-01-08 17:49:12 +01:00
b903fff10f UI update 2021-01-08 01:17:48 +01:00
20c5ce61b4 Update ui 2021-01-07 22:51:45 +01:00
5bb81772f4 UI update 2021-01-07 18:24:20 +01:00
526a9ac6ac Change UI 2021-01-07 16:11:10 +01:00
98db1380b7 Move to kotlin data binding and update main fragoment 2021-01-07 15:09:58 +01:00
ce9b343e0e Change file names 2021-01-07 12:44:37 +01:00
21ec3c3f5d Update strings.xml 2021-01-06 22:06:43 +01:00
88950a84b4 Update strings.xml 2021-01-06 21:58:01 +01:00
fb853975e0 Update app navigation 2021-01-06 21:31:01 +01:00
329eee6339 Add new dark icons 2021-01-06 21:11:10 +01:00
c595168320 Merge develop 2021-01-06 17:33:24 +01:00
61e3e43fd7 Publish v2.1.1 2021-01-06 17:30:42 +01:00
1513b96313 Bugfixes, fix #257 2021-01-06 17:26:32 +01:00
1cc5558d9e Add dark icons 2021-01-06 17:19:36 +01:00
e12e908496 UI update 2021-01-05 17:53:00 +01:00
2aed9e3b25 Merge branch 'develop' of github.com:tommasoberlose/another-widget into design-update 2021-01-05 11:08:26 +01:00
536ed64d41 Default temp unit and weather provider based on locale 2021-01-05 11:08:13 +01:00
6b6ec633ee Update main design 2021-01-05 10:46:12 +01:00
5b2d245e80 Merge develop 2021-01-04 12:07:59 +01:00
0aec34dcd2 Bugfixes 2021-01-04 12:04:25 +01:00
b2a9b9fbb3 Fix #255 2021-01-03 17:51:31 +01:00
768f04825e Building 2021-01-03 17:25:00 +01:00
80ee877bf2 Merge pull request #252 from devindang/master
Update new strings for Chinese Simplified
2021-01-03 17:24:18 +01:00
ac7839ecdc Merge pull request #244 from Moutony/patch-25
Update French Greetings
2021-01-03 17:24:11 +01:00
66d0214cd9 Move the location updates to a foreground service and fix multiple bugs 2021-01-03 17:23:22 +01:00
e069b8f6ab greetings 2020-11-27 23:47:13 +08:00
8a681f0cd7 Update New Strings 2020-11-25 20:46:02 +08:00
92158ec5f2 Update strings.xml 2020-11-22 12:21:56 +01:00
ebb37f21ed Remove useless icons 2020-10-30 19:26:01 +01:00
2a389cb422 Update strings.xml 2020-10-29 12:12:27 +01:00
4504a0617e Update Fitness in Glance 2020-10-28 14:58:22 +01:00
9ed34ee17e Update French Greetings
Thanks for adding antislash before apostrophes. I always forget it haha
But, in French typography, there is a (narrow) no-break space U+202F before these punctuations ! ? ; :
2020-10-28 14:47:11 +01:00
a7294edfd4 Fix notification listener 2020-10-27 18:28:21 +01:00
49ca17803e Update translations 2020-10-27 18:21:55 +01:00
97ab72081d Update the UI 2020-10-27 18:13:27 +01:00
e6fee0dfe1 Merge branch 'master' of github.com:tommasoberlose/another-widget into develop 2020-10-27 18:13:15 +01:00
e0c4f24c43 Merge pull request #239 from Vgbhieel/master
Added Portuguese translation (values-pt)
2020-10-27 18:13:02 +01:00
2a7d0f171b Create strings,xml for portuguese values
translated all strings to portuguese
2020-10-23 00:55:39 -03:00
00dcfc3149 Merge branch 'master' of github.com:tommasoberlose/another-widget into develop 2020-10-20 10:39:19 +02:00
eb11b603aa Merge pull request #235 from Moutony/patch-24
Update French 2
2020-10-20 10:39:01 +02:00
4d2f624448 Merge branch 'master' of github.com:tommasoberlose/another-widget into develop 2020-10-20 10:38:47 +02:00
7581af4dd2 Merge branch 'english-strings' 2020-10-20 10:38:19 +02:00
0325af6582 Fix uppercase labels 2020-10-20 10:35:54 +02:00
4de0413a35 Fix multiple bugs 2020-10-20 10:27:05 +02:00
c54a24c889 Update strings.xml 2020-10-19 21:54:51 +02:00
98eccd2833 Update strings.xml 2020-10-19 21:54:17 +02:00
0119a20765 Update strings.xml 2020-10-19 21:51:12 +02:00
a1e54892fd Update strings.xml 2020-10-19 16:28:38 +02:00
17801fd164 Update strings.xml 2020-10-19 16:26:57 +02:00
e6087b2969 Merge pull request #234 from Moutony/patch-23
Update French
2020-10-19 15:35:06 +02:00
59b8ba26a2 Merge pull request #232 from d-l-n/master
Added and translated new strings
2020-10-19 15:35:01 +02:00
f190ee5d15 Merge pull request #231 from Drumber/translation
Update German translation
2020-10-19 15:34:48 +02:00
2f266ffcf8 Update strings.xml 2020-10-19 11:07:05 +02:00
0e376aba80 Update strings.xml 2020-10-19 11:06:29 +02:00
313e4fa92d Update French 2020-10-19 11:01:21 +02:00
a8ec754bc4 Added and translated new strings 2020-10-19 01:04:33 -03:00
f8d1188634 Update strings.xml
Added greetings and notification timeout strings
2020-10-18 18:47:52 +02:00
8216fc96b9 Update English strings.xml
Removed OpenWeatherMap former instructions.
Capitalized all action buttons.
name="action_save" translatable="false">OK
2020-10-18 16:15:22 +02:00
2862 changed files with 6815 additions and 4657 deletions

5
.gitignore vendored
View File

@ -8,4 +8,7 @@
.externalNativeBuild
/tasksintegration/build
/app/google-services.json
apikey.properties
apikey.properties
./.idea/*
app/release/*
/app/release/*

View File

@ -123,6 +123,29 @@
</PersistentState>
</value>
</entry>
<entry key="vectorWizard">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="vectorAssetStep">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="assetSourceType" value="FILE" />
<entry key="outputName" value="round_aspect_ratio_24" />
<entry key="sourceFile" value="$USER_HOME$/Desktop/round_aspect_ratio_24.svg" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</component>

Binary file not shown.

View File

@ -17,7 +17,6 @@
<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">

140
.idea/navEditor.xml generated
View File

@ -46,6 +46,146 @@
</LayoutPositions>
</value>
</entry>
<entry key="settings_nav_graph.xml">
<value>
<LayoutPositions>
<option name="myPositions">
<map>
<entry key="calendarTabFragment">
<value>
<LayoutPositions>
<option name="myPosition">
<Point>
<option name="x" value="190" />
<option name="y" value="4" />
</Point>
</option>
</LayoutPositions>
</value>
</entry>
<entry key="clockTabFragment">
<value>
<LayoutPositions>
<option name="myPosition">
<Point>
<option name="x" value="494" />
<option name="y" value="302" />
</Point>
</option>
</LayoutPositions>
</value>
</entry>
<entry key="generalTabFragment">
<value>
<LayoutPositions>
<option name="myPosition">
<Point>
<option name="x" value="-48" />
<option name="y" value="133" />
</Point>
</option>
</LayoutPositions>
</value>
</entry>
<entry key="gesturesFragment">
<value>
<LayoutPositions>
<option name="myPosition">
<Point>
<option name="x" value="703" />
<option name="y" value="14" />
</Point>
</option>
</LayoutPositions>
</value>
</entry>
<entry key="glanceTabFragment">
<value>
<LayoutPositions>
<option name="myPosition">
<Point>
<option name="x" value="2" />
<option name="y" value="-198" />
</Point>
</option>
</LayoutPositions>
</value>
</entry>
<entry key="tabSelectorFragment">
<value>
<LayoutPositions>
<option name="myPosition">
<Point>
<option name="x" value="-537" />
<option name="y" value="216" />
</Point>
</option>
<option name="myPositions">
<map>
<entry key="action_tabSelectorFragment_to_calendarTabFragment">
<value>
<LayoutPositions />
</value>
</entry>
<entry key="action_tabSelectorFragment_to_clockTabFragment">
<value>
<LayoutPositions />
</value>
</entry>
<entry key="action_tabSelectorFragment_to_generalTabFragment">
<value>
<LayoutPositions />
</value>
</entry>
<entry key="action_tabSelectorFragment_to_glanceTabFragment">
<value>
<LayoutPositions />
</value>
</entry>
<entry key="action_tabSelectorFragment_to_typographyTabFragment">
<value>
<LayoutPositions />
</value>
</entry>
<entry key="action_tabSelectorFragment_to_weatherTabFragment">
<value>
<LayoutPositions />
</value>
</entry>
</map>
</option>
</LayoutPositions>
</value>
</entry>
<entry key="typographyTabFragment">
<value>
<LayoutPositions>
<option name="myPosition">
<Point>
<option name="x" value="470" />
<option name="y" value="-207" />
</Point>
</option>
</LayoutPositions>
</value>
</entry>
<entry key="weatherTabFragment">
<value>
<LayoutPositions>
<option name="myPosition">
<Point>
<option name="x" value="-301" />
<option name="y" value="-160" />
</Point>
</option>
</LayoutPositions>
</value>
</entry>
</map>
</option>
</LayoutPositions>
</value>
</entry>
</map>
</option>
</component>

View File

@ -6,7 +6,6 @@ apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'realm-android'
@ -16,15 +15,15 @@ apikeyProperties.load(new FileInputStream(apikeyPropertiesFile))
android {
compileSdkVersion 29
compileSdkVersion 30
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.tommasoberlose.anotherwidget"
minSdkVersion 23
targetSdkVersion 29
versionCode 109
versionName "2.0.15"
targetSdkVersion 30
versionCode 125
versionName "2.2.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("String", "GOOGLE_API_KEY", apikeyProperties['GOOGLE_API_KEY'])
@ -55,11 +54,11 @@ android {
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
}
dataBinding {
enabled = true
}
viewBinding.enabled = true
buildFeatures {
dataBinding = true
viewBinding = true
}
}
dependencies {
@ -72,11 +71,12 @@ dependencies {
// UI
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
implementation 'com.google.android.material:material:1.3.0-alpha03'
implementation 'androidx.browser:browser:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'com.google.android.material:material:1.3.0-beta01'
implementation 'androidx.browser:browser:1.3.0'
implementation 'net.idik:slimadapter:2.1.2'
implementation 'com.google.android:flexbox:2.0.1'
implementation 'com.kyleduo.switchbutton:library:2.0.3'
// Lifecycle
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
@ -89,12 +89,12 @@ dependencies {
implementation 'org.greenrobot:eventbus:3.2.0'
// Navigation
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.2'
// Other
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'joda-time:joda-time:2.10.6'
implementation 'net.danlew:android.joda:2.10.9'
implementation 'me.everything:providers-android:1.0.1'
implementation 'com.github.warkiz.widget:indicatorseekbar:2.1.2'
@ -103,7 +103,7 @@ dependencies {
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-fitness:20.0.0'
implementation 'com.google.android.gms:play-services-auth:18.1.0'
//Weather
@ -111,8 +111,8 @@ dependencies {
implementation 'com.google.android.gms:play-services-location:17.1.0'
// Billing
implementation 'com.android.billingclient:billing:3.0.1'
implementation 'com.android.billingclient:billing-ktx:3.0.1'
implementation 'com.android.billingclient:billing:3.0.2'
implementation 'com.android.billingclient:billing-ktx:3.0.2'
// KTX
implementation "androidx.core:core-ktx:1.3.2"
@ -128,10 +128,10 @@ dependencies {
implementation "com.github.haroldadmin:NetworkResponseAdapter:4.0.1"
//Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
// Add the Firebase SDK for Crashlytics.
implementation 'com.google.firebase:firebase-crashlytics:17.2.2'
implementation 'com.google.firebase:firebase-crashlytics:17.3.0'
// Preferences
implementation 'com.chibatching.kotpref:kotpref:2.11.0'

Binary file not shown.

View File

@ -5,7 +5,6 @@
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.android.vending.BILLING" />
@ -13,7 +12,7 @@
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.gms.permission.ACTIVITY_RECOGNITION"/>
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:allowBackup="true"
@ -32,16 +31,16 @@
<category android:name="android.intent.category.LAUNCHER" />
</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" />
<activity android:name=".ui.activities.AppNotificationsFilterActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.ChooseApplicationActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.CustomFontActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.CustomLocationActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.WeatherProviderActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.settings.SupportDevActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.CustomDateActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.settings.IntegrationsActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.MusicPlayersFilterActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.AppNotificationsFilterActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.tabs.MediaInfoFormatActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<receiver android:name=".ui.widgets.MainWidget">
<intent-filter>
@ -151,9 +150,21 @@
</receiver>
<service
android:name=".services.UpdateCalendarJob"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true"/>
android:name=".services.UpdateCalendarService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="dataSync" />
<service
android:name=".services.LocationService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="location" />
</application>
<queries>
<package android:name="com.google.android.apps.fitness"/>
<intent>
<action android:name="android.intent.action.MAIN" />
</intent>
</queries>
</manifest>

View File

@ -1,13 +1,16 @@
package com.tommasoberlose.anotherwidget
import android.Manifest
import android.app.Application
import android.util.Log
import androidx.appcompat.app.AppCompatDelegate
import com.chibatching.kotpref.Kotpref
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import io.realm.Realm
import io.realm.RealmConfiguration
import net.danlew.android.joda.JodaTimeAndroid
class AWApplication : Application() {
override fun onCreate() {

View File

@ -6,6 +6,7 @@ import android.graphics.Color
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.widget.GridLayout
import android.widget.ImageView
@ -21,18 +22,17 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.card.MaterialCardView
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.BottomSheetMenuHorBinding
import com.tommasoberlose.anotherwidget.databinding.BottomSheetMenuListBinding
import com.tommasoberlose.anotherwidget.helpers.ColorHelper
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark
import com.tommasoberlose.anotherwidget.utils.expand
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
import com.tommasoberlose.anotherwidget.utils.reveal
import com.tommasoberlose.anotherwidget.utils.toPixel
import com.warkiz.widget.IndicatorSeekBar
import com.warkiz.widget.OnSeekChangeListener
import com.warkiz.widget.SeekParams
import kotlinx.android.synthetic.main.bottom_sheet_menu_hor.*
import kotlinx.android.synthetic.main.bottom_sheet_menu_hor.view.*
import kotlinx.android.synthetic.main.bottom_sheet_menu_hor.view.color_loader
import kotlinx.android.synthetic.main.color_picker_menu_item.view.*
import kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter
import java.lang.Exception
@ -52,23 +52,22 @@ class BottomSheetColorPicker(
private var loadingJobs: ArrayList<Job> = ArrayList()
private lateinit var adapter: SlimAdapter
private var binding: BottomSheetMenuHorBinding = BottomSheetMenuHorBinding.inflate(LayoutInflater.from(context))
private var listBinding: BottomSheetMenuListBinding = BottomSheetMenuListBinding.inflate(LayoutInflater.from(context))
override fun show() {
val view = View.inflate(context, R.layout.bottom_sheet_menu_hor, null)
window?.setDimAmount(0f)
// Header
view.header.isVisible = header != null
view.header_text.text = header ?: ""
binding.header.isVisible = header != null
binding.headerText.text = header ?: ""
// Alpha
view.alpha_selector_container.isVisible = showAlphaSelector
view.alpha_selector.setProgress(alpha.toFloat())
view.text_alpha.text = "%s: %s%%".format(context.getString(R.string.alpha), alpha)
view.alpha_selector.onSeekChangeListener = object : OnSeekChangeListener {
binding.alphaSelectorContainer.isVisible = showAlphaSelector
binding.alphaSelector.setProgress(alpha.toFloat())
binding.textAlpha.text = "%s: %s%%".format(context.getString(R.string.alpha), alpha)
binding.alphaSelector.onSeekChangeListener = object : OnSeekChangeListener {
override fun onSeeking(seekParams: SeekParams?) {
seekParams?.let {
view.text_alpha.text = "%s: %s%%".format(context.getString(R.string.alpha), it.progress)
binding.textAlpha.text = "%s: %s%%".format(context.getString(R.string.alpha), it.progress)
onAlphaChangeListener?.invoke(it.progress)
}
}
@ -83,16 +82,16 @@ class BottomSheetColorPicker(
adapter = SlimAdapter.create()
loadingJobs.add(GlobalScope.launch(Dispatchers.IO) {
val listView = View.inflate(context, R.layout.bottom_sheet_menu_list, null) as RecyclerView
listView.setHasFixedSize(true)
listBinding.root.setHasFixedSize(true)
val mLayoutManager = GridLayoutManager(context, 6)
listView.layoutManager = mLayoutManager
listBinding.root.layoutManager = mLayoutManager
adapter
.register<Int>(R.layout.color_picker_menu_item) { item, injector ->
injector
.with<MaterialCardView>(R.id.color) {
it.setCardBackgroundColor(ColorStateList.valueOf(item))
it.strokeWidth = if ((colors.indexOf(item) == 0 && !context.isDarkTheme()) || (colors.indexOf(item) == 10 && context.isDarkTheme())) 2 else 0
}
.with<AppCompatImageView>(R.id.check) {
if (getSelected?.invoke() == item) {
@ -113,22 +112,22 @@ class BottomSheetColorPicker(
onColorSelected?.invoke(item)
val position = adapter.data.indexOf(item)
adapter.notifyItemChanged(position)
(listView.layoutManager as GridLayoutManager).scrollToPositionWithOffset(position,0)
(listBinding.root.layoutManager as GridLayoutManager).scrollToPositionWithOffset(position,0)
}
}
.attachTo(listView)
.attachTo(listBinding.root)
adapter.updateData(colors.toList())
withContext(Dispatchers.Main) {
view.color_loader.isVisible = false
view.list_container.addView(listView)
binding.colorLoader.isVisible = false
binding.listContainer.addView(listBinding.root)
this@BottomSheetColorPicker.behavior.state = BottomSheetBehavior.STATE_EXPANDED
view.list_container.isVisible = true
binding.listContainer.isVisible = true
}
})
setContentView(view)
setContentView(binding.root)
super.show()
}

View File

@ -1,6 +1,7 @@
package com.tommasoberlose.anotherwidget.components
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.widget.TextView
import androidx.core.content.ContextCompat
@ -8,9 +9,8 @@ import androidx.core.view.isVisible
import com.google.android.material.bottomsheet.BottomSheetDialog
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
import com.tommasoberlose.anotherwidget.databinding.BottomSheetMenuBinding
import com.tommasoberlose.anotherwidget.databinding.BottomSheetMenuItemBinding
/**
* [BottomSheetDialogFragment] that uses a custom
@ -24,6 +24,8 @@ open class BottomSheetMenu<T>(context: Context, private val header: String? = nu
private var callback: ((selectedValue: T) -> Unit)? = null
private var multipleSelectionCallback: ((selectedValues: ArrayList<T>) -> Unit)? = null
private var binding = BottomSheetMenuBinding.inflate(LayoutInflater.from(context))
fun setSelectedValue(res: T): BottomSheetMenu<T> {
selectedRes = ArrayList(listOf(res))
return this
@ -50,33 +52,31 @@ open class BottomSheetMenu<T>(context: Context, private val header: String? = nu
}
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 ?: ""
binding.header.isVisible = header != null
binding.headerText.text = header ?: ""
view.warning_text.isVisible = message != null
view.warning_text.text = message ?: ""
view.warning_text.setTextColor(ContextCompat.getColor(context, if (isMessageWarning) R.color.warningColorText else R.color.colorSecondaryText))
binding.warningText.isVisible = message != null
binding.warningText.text = message ?: ""
binding.warningText.setTextColor(ContextCompat.getColor(context, if (isMessageWarning) R.color.warningColorText else R.color.colorSecondaryText))
// Menu
for (item in items) {
val itemBinding = BottomSheetMenuItemBinding.inflate(LayoutInflater.from(context))
if (item.value != null) {
val itemView = View.inflate(context, R.layout.bottom_sheet_menu_item, null)
itemView.label.text = item.title
itemBinding.label.text = item.title
if (isMultiSelection) {
itemView.icon_check.isVisible = selectedRes.contains(item.value)
itemView.label.setTextColor(
itemBinding.iconCheck.isVisible = selectedRes.contains(item.value)
itemBinding.label.setTextColor(
if (selectedRes.contains(item.value)) ContextCompat.getColor(
context,
R.color.colorPrimaryText
) else ContextCompat.getColor(context, R.color.colorSecondaryText)
)
} else {
itemView.isSelected = selectedRes.contains(item.value)
itemBinding.root.isSelected = selectedRes.contains(item.value)
}
itemView.setOnClickListener {
itemBinding.root.setOnClickListener {
if (!isMultiSelection) {
callback?.invoke(item.value)
this.dismiss()
@ -88,8 +88,8 @@ open class BottomSheetMenu<T>(context: Context, private val header: String? = nu
}
multipleSelectionCallback?.invoke(selectedRes)
itemView.icon_check.isVisible = selectedRes.contains(item.value)
itemView.label.setTextColor(
itemBinding.iconCheck.isVisible = selectedRes.contains(item.value)
itemBinding.label.setTextColor(
if (selectedRes.contains(item.value)) ContextCompat.getColor(
context,
R.color.colorPrimaryText
@ -97,17 +97,16 @@ open class BottomSheetMenu<T>(context: Context, private val header: String? = nu
)
}
item.renderCallback?.invoke(itemView.label)
item.renderCallback?.invoke(itemBinding.label)
}
view.menu.addView(itemView)
binding.menu.addView(itemBinding.root)
} else {
val itemView = View.inflate(context, R.layout.bottom_sheet_menu_divider, null)
itemView.label.text = item.title
view.menu.addView(itemView)
itemBinding.label.text = item.title
binding.menu.addView(itemBinding.root)
}
}
setContentView(view)
setContentView(binding.root)
super.show()
}

View File

@ -5,32 +5,33 @@ 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.databinding.WeatherProviderSettingsLayoutBinding
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) {
private var binding: WeatherProviderSettingsLayoutBinding = WeatherProviderSettingsLayoutBinding.inflate(android.view.LayoutInflater.from(context))
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()
binding.apiKeyContainer.isVisible = WeatherHelper.isKeyRequired()
binding.actionSaveKey.isVisible = WeatherHelper.isKeyRequired()
WeatherHelper.getProviderInfoTitle(context).let { title ->
view.info_title.text = title
view.info_title.isVisible = title != ""
binding.infoTitle.text = title
binding.infoTitle.isVisible = title != ""
}
WeatherHelper.getProviderInfoSubtitle(context).let { subtitle ->
view.info_subtitle.text = subtitle
view.info_subtitle.isVisible = subtitle != ""
binding.infoSubtitle.text = subtitle
binding.infoSubtitle.isVisible = subtitle != ""
}
view.info_provider.text = WeatherHelper.getProviderName(context)
binding.infoProvider.text = WeatherHelper.getProviderName(context)
view.api_key.editText?.setText(when (Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) {
binding.apiKey.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
@ -41,12 +42,12 @@ class BottomSheetWeatherProviderSettings(context: Context, callback: () -> Unit)
null -> ""
})
view.action_open_provider.setOnClickListener {
binding.actionOpenProvider.setOnClickListener {
context.openURI(WeatherHelper.getProviderLink())
}
view.action_save_key.setOnClickListener {
val key = view.api_key.editText?.text.toString()
binding.actionSaveKey.setOnClickListener {
val key = binding.apiKey.editText?.text.toString()
when (Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) {
Constants.WeatherProvider.OPEN_WEATHER -> Preferences.weatherProviderApiOpen = key
Constants.WeatherProvider.WEATHER_BIT -> Preferences.weatherProviderApiWeatherBit = key
@ -59,6 +60,6 @@ class BottomSheetWeatherProviderSettings(context: Context, callback: () -> Unit)
dismiss()
}
setContentView(view)
setContentView(binding.root)
}
}

View File

@ -1,27 +1,30 @@
package com.tommasoberlose.anotherwidget.components
import android.content.Context
import android.view.LayoutInflater
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.databinding.CustomNotesDialogLayoutBinding
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) {
class CustomNotesDialog(context: Context, callback: (() -> Unit)?) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
private var binding: CustomNotesDialogLayoutBinding = CustomNotesDialogLayoutBinding.inflate(LayoutInflater.from(context))
init {
val view = View.inflate(context, R.layout.custom_notes_dialog_layout, null)
view.notes.setText(Preferences.customNotes)
binding.notes.setText(Preferences.customNotes)
view.action_positive.setOnClickListener {
Preferences.customNotes = view.notes.text.toString()
binding.actionPositive.setOnClickListener {
Preferences.customNotes = binding.notes.text.toString()
this.dismiss()
callback?.invoke()
}
view.notes.requestFocus()
binding.notes.requestFocus()
setContentView(view)
setContentView(binding.root)
}
}

View File

@ -7,10 +7,10 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import androidx.core.app.NotificationManagerCompat
import androidx.core.view.isVisible
import androidx.navigation.Navigation
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
@ -21,6 +21,7 @@ import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.GlanceProviderSettingsLayoutBinding
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper
@ -28,19 +29,22 @@ import com.tommasoberlose.anotherwidget.helpers.AlarmHelper
import com.tommasoberlose.anotherwidget.helpers.GreetingsHelper
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver
import com.tommasoberlose.anotherwidget.ui.activities.AppNotificationsFilterActivity
import com.tommasoberlose.anotherwidget.ui.activities.MusicPlayersFilterActivity
import com.tommasoberlose.anotherwidget.ui.activities.tabs.AppNotificationsFilterActivity
import com.tommasoberlose.anotherwidget.ui.activities.tabs.MediaInfoFormatActivity
import com.tommasoberlose.anotherwidget.ui.activities.tabs.MusicPlayersFilterActivity
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import kotlinx.android.synthetic.main.glance_provider_settings_layout.view.*
import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus
class GlanceSettingsDialog(val context: Activity, val provider: Constants.GlanceProviderId, private val statusCallback: (() -> Unit)?) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
private var binding: GlanceProviderSettingsLayoutBinding = GlanceProviderSettingsLayoutBinding.inflate(LayoutInflater.from(context))
override fun show() {
val view = View.inflate(context, R.layout.glance_provider_settings_layout, null)
/* TITLE */
view.title.text = when (provider) {
binding.title.text = when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> context.getString(R.string.settings_show_music_title)
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> context.getString(R.string.settings_show_next_alarm_title)
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> context.getString(R.string.settings_low_battery_level_title)
@ -48,10 +52,11 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> context.getString(R.string.settings_daily_steps_title)
Constants.GlanceProviderId.NOTIFICATIONS -> context.getString(R.string.settings_show_notifications_title)
Constants.GlanceProviderId.GREETINGS -> context.getString(R.string.settings_show_greetings_title)
Constants.GlanceProviderId.EVENTS -> context.getString(R.string.settings_show_events_as_glance_provider_title)
}
/* SUBTITLE*/
view.subtitle.text = when (provider) {
binding.subtitle.text = when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> context.getString(R.string.settings_show_music_subtitle)
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> context.getString(R.string.settings_show_next_alarm_subtitle)
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> context.getString(R.string.settings_low_battery_level_subtitle)
@ -59,53 +64,59 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> context.getString(R.string.settings_daily_steps_subtitle)
Constants.GlanceProviderId.NOTIFICATIONS -> context.getString(R.string.settings_show_notifications_subtitle)
Constants.GlanceProviderId.GREETINGS -> context.getString(R.string.settings_show_greetings_subtitle)
Constants.GlanceProviderId.EVENTS -> context.getString(R.string.settings_show_events_as_glance_provider_subtitle)
}
/* SONG */
view.action_filter_music_players.isVisible = provider == Constants.GlanceProviderId.PLAYING_SONG
binding.actionFilterMusicPlayers.isVisible = provider == Constants.GlanceProviderId.PLAYING_SONG
binding.actionChangeMediaInfoFormat.isVisible = provider == Constants.GlanceProviderId.PLAYING_SONG
if (provider == Constants.GlanceProviderId.PLAYING_SONG) {
view.action_filter_music_players.setOnClickListener {
binding.actionFilterMusicPlayers.setOnClickListener {
dismiss()
context.startActivityForResult(Intent(context, MusicPlayersFilterActivity::class.java), 0)
}
checkNotificationPermission(view)
binding.actionChangeMediaInfoFormat.setOnClickListener {
dismiss()
context.startActivityForResult(Intent(context, MediaInfoFormatActivity::class.java), 0)
}
checkNotificationPermission()
}
/* ALARM */
view.alarm_set_by_container.isVisible = provider == Constants.GlanceProviderId.NEXT_CLOCK_ALARM
binding.alarmSetByContainer.isVisible = provider == Constants.GlanceProviderId.NEXT_CLOCK_ALARM
if (provider == Constants.GlanceProviderId.NEXT_CLOCK_ALARM) {
view.header.text = context.getString(R.string.information_header)
view.warning_container.isVisible = false
checkNextAlarm(view)
binding.header.text = context.getString(R.string.information_header)
binding.warningContainer.isVisible = false
checkNextAlarm()
}
/* GOOGLE STEPS */
view.action_toggle_google_fit.isVisible = provider == Constants.GlanceProviderId.GOOGLE_FIT_STEPS
binding.actionToggleGoogleFit.isVisible = provider == Constants.GlanceProviderId.GOOGLE_FIT_STEPS
if (provider == Constants.GlanceProviderId.GOOGLE_FIT_STEPS) {
view.warning_container.isVisible = false
checkFitnessPermission(view)
checkGoogleFitConnection(view)
binding.warningContainer.isVisible = false
checkFitnessPermission()
checkGoogleFitConnection()
}
/* BATTERY INFO */
if (provider == Constants.GlanceProviderId.BATTERY_LEVEL_LOW) {
view.warning_container.isVisible = false
view.header.isVisible = false
view.divider.isVisible = false
binding.warningContainer.isVisible = false
binding.header.isVisible = false
binding.divider.isVisible = false
}
/* NOTIFICATIONS */
view.action_filter_notifications_app.isVisible = provider == Constants.GlanceProviderId.NOTIFICATIONS
view.action_change_notification_timer.isVisible = provider == Constants.GlanceProviderId.NOTIFICATIONS
binding.actionFilterNotificationsApp.isVisible = provider == Constants.GlanceProviderId.NOTIFICATIONS
binding.actionChangeNotificationTimer.isVisible = provider == Constants.GlanceProviderId.NOTIFICATIONS
if (provider == Constants.GlanceProviderId.NOTIFICATIONS) {
checkLastNotificationsPermission(view)
checkLastNotificationsPermission()
val stringArray = context.resources.getStringArray(R.array.glance_notifications_timeout)
view.action_filter_notifications_app.setOnClickListener {
binding.actionFilterNotificationsApp.setOnClickListener {
dismiss()
context.startActivityForResult(Intent(context, AppNotificationsFilterActivity::class.java), 0)
}
view.notification_timer_label.text = stringArray[Preferences.hideNotificationAfter]
view.action_change_notification_timer.setOnClickListener {
binding.notificationTimerLabel.text = stringArray[Preferences.hideNotificationAfter]
binding.actionChangeNotificationTimer.setOnClickListener {
val dialog = BottomSheetMenu<Int>(context, header = context.getString(R.string.glance_notification_hide_timeout_title)).setSelectedValue(Preferences.hideNotificationAfter)
Constants.GlanceNotificationTimer.values().forEachIndexed { index, timeout ->
dialog.addItem(stringArray[index], timeout.value)
@ -119,13 +130,20 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
/* GREETINGS */
if (provider == Constants.GlanceProviderId.GREETINGS) {
view.warning_container.isVisible = false
view.header.isVisible = false
view.divider.isVisible = false
binding.warningContainer.isVisible = false
binding.header.isVisible = false
binding.divider.isVisible = false
}
/* EVENTS */
if (provider == Constants.GlanceProviderId.EVENTS) {
binding.header.isVisible = false
binding.divider.isVisible = false
checkCalendarConfig()
}
/* TOGGLE */
view.provider_switch.isChecked = when (provider) {
binding.providerSwitch.setCheckedImmediatelyNoEvent(when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> Preferences.showMusic
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> Preferences.showNextAlarm
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> Preferences.showBatteryCharging
@ -133,11 +151,12 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> Preferences.showDailySteps
Constants.GlanceProviderId.NOTIFICATIONS -> Preferences.showNotifications
Constants.GlanceProviderId.GREETINGS -> Preferences.showGreetings
}
Constants.GlanceProviderId.EVENTS -> Preferences.showEventsAsGlanceProvider
})
var job: Job? = null
view.provider_switch.setOnCheckedChangeListener { _, isChecked ->
binding.providerSwitch.setOnCheckedChangeListener { _, isChecked ->
job?.cancel()
job = GlobalScope.launch(Dispatchers.IO) {
delay(300)
@ -145,18 +164,18 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> {
Preferences.showMusic = isChecked
checkNotificationPermission(view)
checkNotificationPermission()
}
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
Preferences.showNextAlarm = isChecked
checkNextAlarm(view)
checkNextAlarm()
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
Preferences.showBatteryCharging = isChecked
}
Constants.GlanceProviderId.NOTIFICATIONS -> {
Preferences.showNotifications = isChecked
checkLastNotificationsPermission(view)
checkLastNotificationsPermission()
}
Constants.GlanceProviderId.GREETINGS -> {
Preferences.showGreetings = isChecked
@ -184,9 +203,12 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
Preferences.showDailySteps = false
}
view.warning_container.isVisible = false
checkFitnessPermission(view)
checkGoogleFitConnection(view)
binding.warningContainer.isVisible = false
checkFitnessPermission()
checkGoogleFitConnection()
}
Constants.GlanceProviderId.EVENTS -> {
Preferences.showEventsAsGlanceProvider = isChecked
}
else -> {
}
@ -196,11 +218,11 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
}
}
setContentView(view)
setContentView(binding.root)
super.show()
}
private fun checkNextAlarm(view: View) {
private fun checkNextAlarm() {
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
val alarm = nextAlarmClock
if (alarm != null && alarm.showIntent != null) {
@ -210,56 +232,71 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
} catch (e: Exception) {
alarm.showIntent?.creatorPackage ?: ""
}
view.alarm_set_by_title.text = context.getString(R.string.settings_show_next_alarm_app_title).format(appNameOrPackage)
view.alarm_set_by_subtitle.text = if (AlarmHelper.isAlarmProbablyWrong(context)) context.getString(R.string.settings_show_next_alarm_app_subtitle_wrong) else context.getString(R.string.settings_show_next_alarm_app_subtitle_correct)
view.alarm_set_by_title.isVisible = true
binding.alarmSetByTitle.text = context.getString(R.string.settings_show_next_alarm_app_title).format(appNameOrPackage)
binding.alarmSetBySubtitle.text = if (AlarmHelper.isAlarmProbablyWrong(context)) context.getString(R.string.settings_show_next_alarm_app_subtitle_wrong) else context.getString(R.string.settings_show_next_alarm_app_subtitle_correct)
binding.alarmSetByContainer.isVisible = true
} else {
view.alarm_set_by_title.isVisible = false
binding.alarmSetByContainer.isVisible = false
binding.header.isVisible = false
binding.divider.isVisible = false
}
}
statusCallback?.invoke()
}
private fun checkNotificationPermission(view: View) {
private fun checkCalendarConfig() {
if (!Preferences.showEvents || !context.checkGrantedPermission(Manifest.permission.READ_CALENDAR)) {
binding.warningContainer.isVisible = true
binding.warningTitle.text = context.getString(R.string.settings_show_events_as_glance_provider_error)
binding.warningContainer.setOnClickListener {
dismiss()
EventBus.getDefault().post(MainFragment.ChangeTabEvent(1))
}
} else {
binding.warningContainer.isVisible = false
}
}
private fun checkNotificationPermission() {
when {
ActiveNotificationsHelper.checkNotificationAccess(context) -> {
view.warning_container.isVisible = false
binding.warningContainer.isVisible = false
MediaPlayerHelper.updatePlayingMediaInfo(context)
}
Preferences.showMusic -> {
view.warning_container.isVisible = true
view.warning_title.text = context.getString(R.string.settings_request_notification_access)
view.warning_container.setOnClickListener {
binding.warningContainer.isVisible = true
binding.warningTitle.text = context.getString(R.string.settings_request_notification_access)
binding.warningContainer.setOnClickListener {
context.startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"))
}
}
else -> {
view.warning_container.isVisible = false
binding.warningContainer.isVisible = false
}
}
statusCallback?.invoke()
}
private fun checkLastNotificationsPermission(view: View) {
private fun checkLastNotificationsPermission() {
when {
ActiveNotificationsHelper.checkNotificationAccess(context) -> {
view.warning_container.isVisible = false
binding.warningContainer.isVisible = false
}
Preferences.showNotifications -> {
view.warning_container.isVisible = true
view.warning_title.text = context.getString(R.string.settings_request_last_notification_access)
view.warning_container.setOnClickListener {
binding.warningContainer.isVisible = true
binding.warningTitle.text = context.getString(R.string.settings_request_last_notification_access)
binding.warningContainer.setOnClickListener {
context.startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"))
}
}
else -> {
view.warning_container.isVisible = false
binding.warningContainer.isVisible = false
}
}
statusCallback?.invoke()
}
private fun checkFitnessPermission(view: View) {
private fun checkFitnessPermission() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || context.checkGrantedPermission(
Manifest.permission.ACTIVITY_RECOGNITION)
) {
@ -270,10 +307,10 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
}
} else if (Preferences.showDailySteps) {
ActivityDetectionReceiver.unregisterFence(context)
view.warning_container.isVisible = true
view.warning_title.text = context.getString(R.string.settings_request_fitness_access)
view.warning_container.setOnClickListener {
requireFitnessPermission(view)
binding.warningContainer.isVisible = true
binding.warningTitle.text = context.getString(R.string.settings_request_fitness_access)
binding.warningContainer.setOnClickListener {
requireFitnessPermission()
}
} else {
ActivityDetectionReceiver.unregisterFence(context)
@ -281,36 +318,36 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
statusCallback?.invoke()
}
private fun checkGoogleFitConnection(view: View) {
private fun checkGoogleFitConnection() {
val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(context)
if (!GoogleSignIn.hasPermissions(account,
ActivityDetectionReceiver.FITNESS_OPTIONS
)) {
view.warning_container.isVisible = true
view.warning_title.text = context.getString(R.string.settings_request_fitness_access)
view.warning_container.setOnClickListener {
binding.warningContainer.isVisible = true
binding.warningTitle.text = context.getString(R.string.settings_request_fitness_access)
binding.warningContainer.setOnClickListener {
GoogleSignIn.requestPermissions(
context,
1,
account,
ActivityDetectionReceiver.FITNESS_OPTIONS)
}
view.action_connect_to_google_fit.isVisible = true
view.action_disconnect_to_google_fit.isVisible = false
view.action_connect_to_google_fit.setOnClickListener {
binding.actionConnectToGoogleFit.isVisible = true
binding.actionDisconnectToGoogleFit.isVisible = false
binding.actionConnectToGoogleFit.setOnClickListener {
GoogleSignIn.requestPermissions(
context,
1,
account,
ActivityDetectionReceiver.FITNESS_OPTIONS)
}
view.action_disconnect_to_google_fit.setOnClickListener(null)
view.google_fit_status_label.text = context.getString(R.string.google_fit_account_not_connected)
binding.actionDisconnectToGoogleFit.setOnClickListener(null)
binding.googleFitStatusLabel.text = context.getString(R.string.google_fit_account_not_connected)
} else {
view.action_connect_to_google_fit.isVisible = false
view.action_disconnect_to_google_fit.isVisible = true
view.action_connect_to_google_fit.setOnClickListener(null)
view.action_disconnect_to_google_fit.setOnClickListener {
binding.actionConnectToGoogleFit.isVisible = false
binding.actionDisconnectToGoogleFit.isVisible = true
binding.actionConnectToGoogleFit.setOnClickListener(null)
binding.actionDisconnectToGoogleFit.setOnClickListener {
GoogleSignIn.getClient(context, GoogleSignInOptions.Builder(
GoogleSignInOptions.DEFAULT_SIGN_IN).addExtension(
ActivityDetectionReceiver.FITNESS_OPTIONS
@ -318,11 +355,11 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
show()
}
}
view.google_fit_status_label.text = context.getString(R.string.google_fit_account_connected)
binding.googleFitStatusLabel.text = context.getString(R.string.google_fit_account_connected)
}
}
private fun requireFitnessPermission(view: View) {
private fun requireFitnessPermission() {
Dexter.withContext(context)
.withPermissions(
"com.google.android.gms.permission.ACTIVITY_RECOGNITION",
@ -330,7 +367,7 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACTIVITY_RECOGNITION else "com.google.android.gms.permission.ACTIVITY_RECOGNITION"
).withListener(object: MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
checkFitnessPermission(view)
checkFitnessPermission()
}
override fun onPermissionRationaleShouldBeShown(
permissions: MutableList<PermissionRequest>?,

View File

@ -1,43 +1,42 @@
package com.tommasoberlose.anotherwidget.components
import android.content.Context
import android.view.LayoutInflater
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.databinding.BottomSheetMenuBinding
import com.tommasoberlose.anotherwidget.databinding.IconPackMenuItemBinding
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) {
private var binding = BottomSheetMenuBinding.inflate(LayoutInflater.from(context))
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 ?: ""
binding.header.isVisible = header != null
binding.headerText.text = header ?: ""
view.warning_text.isVisible = false
binding.warningText.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
val itemBinding = IconPackMenuItemBinding.inflate(LayoutInflater.from(context))
itemBinding.label.text = context.getString(R.string.settings_weather_icon_pack_default).format(item.value + 1)
itemBinding.root.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)))
itemBinding.icon1.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "01d", item.value)))
itemBinding.icon2.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "01n", item.value)))
itemBinding.icon3.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "10d", item.value)))
itemBinding.icon4.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 {
listOf<ImageView>(itemBinding.icon1, itemBinding.icon2, itemBinding.icon3, itemBinding.icon4).forEach {
if (item == Constants.WeatherIconPack.MINIMAL) {
it.setColorFilter(ContextCompat.getColor(context, R.color.colorPrimaryText))
} else {
@ -45,13 +44,13 @@ class IconPackSelector(context: Context, private val header: String? = null) : B
}
}
itemView.setOnClickListener {
itemBinding.root.setOnClickListener {
Preferences.weatherIconPack = item.value
this.dismiss()
}
view.menu.addView(itemView)
binding.menu.addView(itemBinding.root)
}
setContentView(view)
setContentView(binding.root)
super.show()
}
}

View File

@ -1,11 +1,11 @@
package com.tommasoberlose.anotherwidget.components
import android.content.Context
import android.view.View
import android.view.LayoutInflater
import androidx.core.view.isVisible
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.tommasoberlose.anotherwidget.R
import kotlinx.android.synthetic.main.bottom_sheet_dialog.view.*
import com.tommasoberlose.anotherwidget.databinding.BottomSheetDialogBinding
typealias DialogCallback = () -> Unit
@ -20,6 +20,8 @@ class MaterialBottomSheetDialog(
private var positiveCallback: DialogCallback? = null
private var negativeCallback: DialogCallback? = null
private var binding = BottomSheetDialogBinding.inflate(LayoutInflater.from(context))
fun setPositiveButton(label: String? = context.getString(android.R.string.ok), callback: DialogCallback? = null): MaterialBottomSheetDialog {
positiveButtonLabel = label
positiveCallback = callback
@ -33,30 +35,28 @@ class MaterialBottomSheetDialog(
}
override fun show() {
val view = View.inflate(context, R.layout.bottom_sheet_dialog, null)
// Header
view.title.isVisible = title != null
view.title.text = title ?: ""
binding.title.isVisible = title != null
binding.title.text = title ?: ""
view.message.isVisible = message != null
view.message.text = message ?: ""
binding.message.isVisible = message != null
binding.message.text = message ?: ""
view.action_positive.isVisible = positiveButtonLabel != null
view.action_positive.text = positiveButtonLabel ?: ""
view.action_positive.setOnClickListener {
binding.actionPositive.isVisible = positiveButtonLabel != null
binding.actionPositive.text = positiveButtonLabel ?: ""
binding.actionPositive.setOnClickListener {
positiveCallback?.invoke()
this.dismiss()
}
view.action_negative.isVisible = negativeButtonLabel != null
view.action_negative.text = negativeButtonLabel ?: ""
view.action_negative.setOnClickListener {
binding.actionNegative.isVisible = negativeButtonLabel != null
binding.actionNegative.text = negativeButtonLabel ?: ""
binding.actionNegative.setOnClickListener {
negativeCallback?.invoke()
this.dismiss()
}
setContentView(view)
setContentView(binding.root)
super.show()
}

View File

@ -2,7 +2,6 @@ package com.tommasoberlose.anotherwidget.global
object Actions {
const val ACTION_EXTRA_OPEN_WEATHER_PROVIDER = "ACTION_EXTRA_OPEN_WEATHER_PROVIDER"
const val ACTION_EXTRA_DISABLE_GPS_NOTIFICATION = "ACTION_EXTRA_DISABLE_GPS_NOTIFICATION"
const val ACTION_TIME_UPDATE = "com.tommasoberlose.anotherwidget.action.TIME_UPDATE"
const val ACTION_ALARM_UPDATE = "com.tommasoberlose.anotherwidget.action.ALARM_UPDATE"
@ -14,4 +13,6 @@ object Actions {
const val ACTION_REPORT_CRASH = "com.tommasoberlose.anotherwidget.action.REPORT_CRASH"
const val ACTION_CLEAR_NOTIFICATION = "com.tommasoberlose.anotherwidget.action.CLEAR_NOTIFICATION"
const val ACTION_UPDATE_GREETINGS = "com.tommasoberlose.anotherwidget.action.UPDATE_GREETINGS"
const val ACTION_REFRESH = "com.tommasoberlose.anotherwidget.action.REFRESH"
}

View File

@ -32,7 +32,8 @@ object Constants {
CUSTOM_INFO("CUSTOM_INFO"),
GOOGLE_FIT_STEPS("GOOGLE_FIT_STEPS"),
NOTIFICATIONS("NOTIFICATIONS"),
GREETINGS("GREETINGS");
GREETINGS("GREETINGS"),
EVENTS("EVENTS");
companion object {
private val map = GlanceProviderId.values().associateBy(GlanceProviderId::id)

View File

@ -2,7 +2,12 @@ package com.tommasoberlose.anotherwidget.global
import android.os.Build
import androidx.appcompat.app.AppCompatDelegate.*
import androidx.core.os.ConfigurationCompat
import com.chibatching.kotpref.KotprefModel
import com.tommasoberlose.anotherwidget.helpers.IntentHelper
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
import com.tommasoberlose.anotherwidget.utils.isMetric
import java.util.*
object Preferences : KotprefModel() {
override val commitAllPropertiesByDefault: Boolean = true
@ -14,8 +19,8 @@ object Preferences : KotprefModel() {
var showWeather by booleanPref(key = "PREF_SHOW_WEATHER", default = false)
var weatherIcon by stringPref(key = "PREF_WEATHER_ICON", default = "")
var weatherTemp by floatPref(key = "PREF_WEATHER_TEMP", default = 0f)
var weatherTempUnit by stringPref(key = "PREF_WEATHER_TEMP_UNIT", default = "F")
var weatherRealTempUnit by stringPref(key = "PREF_WEATHER_REAL_TEMP_UNIT", default = "F")
var weatherTempUnit by stringPref(key = "PREF_WEATHER_TEMP_UNIT", default = if (ConfigurationCompat.getLocales(context.resources.configuration)[0].isMetric()) "C" else "F")
var weatherRealTempUnit by stringPref(key = "PREF_WEATHER_REAL_TEMP_UNIT", default = if (ConfigurationCompat.getLocales(context.resources.configuration)[0].isMetric()) "C" else "F")
var calendarAllDay by booleanPref(key = "PREF_CALENDAR_ALL_DAY", default = true)
var calendarFilter by stringPref(key = "PREF_CALENDAR_FILTER", default = "")
@ -43,7 +48,7 @@ object Preferences : KotprefModel() {
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 weatherProvider by intPref(default = if (ConfigurationCompat.getLocales(context.resources.configuration)[0].isMetric()) Constants.WeatherProvider.YR.value else Constants.WeatherProvider.WEATHER_GOV.value)
var weatherProviderError by stringPref(default = "")
var weatherProviderLocationError by stringPref(default = "")
var eventAppName by stringPref(key = "PREF_EVENT_APP_NAME", default = "")
@ -108,16 +113,13 @@ object Preferences : KotprefModel() {
// Settings
var showWallpaper by booleanPref(default = true)
var showBigClockWarning by booleanPref(default = true)
var showWeatherWarning by booleanPref(default = true)
var showPreview by booleanPref(default = true)
var showXiaomiWarning by booleanPref(default = true)
// Glance
var showGlance by booleanPref(default = true)
var enabledGlanceProviderOrder by stringPref(default = "")
var customNotes by stringPref(default = "")
var showNextAlarm by booleanPref(default = true)
var showNextAlarm by booleanPref(default = false)
var showBatteryCharging by booleanPref(default = false)
var isBatteryLevelLow by booleanPref(default = false)
var isCharging by booleanPref(default = false)
@ -133,14 +135,16 @@ object Preferences : KotprefModel() {
var lastNotificationPackage by stringPref(default = "")
var showMusic by booleanPref(default = false)
var mediaInfoFormat by stringPref(default = "")
var mediaInfoFormat by stringPref(default = MediaPlayerHelper.DEFAULT_MEDIA_INFO_FORMAT)
var mediaPlayerTitle by stringPref(default = "")
var mediaPlayerAlbum by stringPref(default = "")
var mediaPlayerArtist by stringPref(default = "")
var mediaPlayerPackage by stringPref(default = "")
var mediaPlayerPackage by stringPref(default = IntentHelper.DO_NOTHING_OPTION)
var musicPlayersFilter by stringPref(default = "")
var appNotificationsFilter by stringPref(default = "")
var showEventsAsGlanceProvider by booleanPref(default = false)
// Integrations
var installedIntegrations by intPref(default = 0)
}

View File

@ -9,6 +9,7 @@ import com.chibatching.kotpref.Kotpref
import com.chibatching.kotpref.blockingBulk
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.receivers.NotificationListener
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
object ActiveNotificationsHelper {
fun showLastNotification(): Boolean {
@ -23,6 +24,7 @@ object ActiveNotificationsHelper {
remove(Preferences::lastNotificationPackage)
remove(Preferences::lastNotificationIcon)
}
MainWidget.updateWidget(context)
}
fun checkNotificationAccess(context: Context): Boolean {

View File

@ -1,29 +1,16 @@
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.services.UpdateCalendarService
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
/**
@ -32,7 +19,7 @@ import kotlin.collections.ArrayList
object CalendarHelper {
fun updateEventList(context: Context) {
UpdateCalendarJob.enqueueWork(context, Intent())
UpdateCalendarService.enqueueWork(context)
}
fun getCalendarList(context: Context): List<me.everything.providers.android.calendar.Calendar> {

View File

@ -2,14 +2,16 @@ package com.tommasoberlose.anotherwidget.helpers
import android.annotation.SuppressLint
import android.graphics.Color
import android.util.Log
import com.tommasoberlose.anotherwidget.global.Preferences
import kotlin.math.roundToInt
object ColorHelper {
fun getFontColor(isDark: Boolean): Int {
return try {
Color.parseColor("#%s%s".format(if (!isDark) Preferences.textGlobalAlpha else Preferences.textGlobalAlphaDark, (if (!isDark) Preferences.textGlobalColor else Preferences.textGlobalColorDark).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")
}
@ -33,7 +35,10 @@ object ColorHelper {
fun getSecondaryFontColor(isDark: Boolean): Int {
return try {
Color.parseColor("#%s%s".format((if (!isDark) Preferences.textSecondaryAlpha else Preferences.textSecondaryAlphaDark), (if (!isDark) Preferences.textSecondaryColor else Preferences.textSecondaryColorDark).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")
}
@ -57,7 +62,10 @@ object ColorHelper {
fun getClockFontColor(isDark: Boolean): Int {
return try {
Color.parseColor("#%s%s".format((if (!isDark) Preferences.clockTextAlpha else Preferences.clockTextAlphaDark), (if (!isDark) Preferences.clockTextColor else Preferences.clockTextColorDark).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")
}
@ -81,7 +89,10 @@ object ColorHelper {
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("#", "")))
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")
}
@ -123,4 +134,14 @@ object ColorHelper {
val hexValue = this.toInt(16).toDouble()
return (hexValue * 100 / 255).roundToInt()
}
fun String.isColor(): Boolean {
return try {
Color.parseColor(this)
true
} catch (iae: IllegalArgumentException) {
iae.printStackTrace()
false
}
}
}

View File

@ -1,5 +1,6 @@
package com.tommasoberlose.anotherwidget.helpers
import android.Manifest
import android.content.Context
import android.util.Log
import com.tommasoberlose.anotherwidget.R
@ -7,6 +8,7 @@ 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.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.checkIfFitInstalled
import java.util.ArrayList
@ -20,7 +22,7 @@ object GlanceProviderHelper {
}
.toTypedArray()
providers.sortWith(Comparator { p1, p2 ->
return ArrayList(providers.filter { enabledProviders.contains(it.id) }.sortedWith(Comparator { p1, p2 ->
when {
enabledProviders.contains(p1.id) && enabledProviders.contains(p2.id) -> {
enabledProviders.indexOf(p1.id).compareTo(enabledProviders.indexOf(p2.id))
@ -35,8 +37,7 @@ object GlanceProviderHelper {
p1.id.compareTo(p2.id)
}
}
})
return ArrayList(providers.toList())
}) + providers.filter { !enabledProviders.contains(it.id) })
}
fun getGlanceProviderById(context: Context, providerId: Constants.GlanceProviderId): GlanceProvider? {
@ -44,49 +45,55 @@ object GlanceProviderHelper {
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_show_next_alarm_title),
R.drawable.round_access_alarm
R.drawable.round_access_alarm_24
)
}
Constants.GlanceProviderId.PLAYING_SONG -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_show_music_title),
R.drawable.round_music_note
R.drawable.round_music_note_24
)
}
Constants.GlanceProviderId.CUSTOM_INFO -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_custom_notes_title),
R.drawable.round_sticky_note_2
R.drawable.round_sticky_note_2_24
)
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_low_battery_level_title),
R.drawable.round_battery_charging_full
R.drawable.round_battery_charging_full_24
)
}
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_daily_steps_title),
R.drawable.round_near_me
R.drawable.round_favorite_border_24
)
}
Constants.GlanceProviderId.NOTIFICATIONS -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_show_notifications_title),
R.drawable.round_notifications
R.drawable.round_notifications_24
)
}
Constants.GlanceProviderId.GREETINGS -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_show_greetings_title),
R.drawable.round_history_edu
R.drawable.round_history_edu_24
)
}
Constants.GlanceProviderId.EVENTS -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_show_events_as_glance_provider_title),
R.drawable.round_event_note_24
)
}
}
}
fun saveGlanceProviderOrder(list: ArrayList<Constants.GlanceProviderId>) {
fun saveGlanceProviderOrder(list: List<Constants.GlanceProviderId>) {
Preferences.enabledGlanceProviderOrder = list.joinToString(separator = ",")
}
@ -94,7 +101,7 @@ object GlanceProviderHelper {
val eventRepository = EventRepository(context)
BatteryHelper.updateBatteryInfo(context)
val showGlance = Preferences.showGlance && (eventRepository.getEventsCount() == 0 || !Preferences.showEvents)
val showGlance = (eventRepository.getEventsCount() == 0 || !Preferences.showEvents || Preferences.showEventsAsGlanceProvider)
&& (
(Preferences.showNotifications && ActiveNotificationsHelper.showLastNotification()) ||
(Preferences.showNextAlarm && AlarmHelper.getNextAlarm(context) != "") ||
@ -102,7 +109,9 @@ object GlanceProviderHelper {
(Preferences.showBatteryCharging && Preferences.isCharging || Preferences.isBatteryLevelLow) ||
(Preferences.customNotes.isNotEmpty()) ||
(Preferences.showDailySteps && Preferences.googleFitSteps > 0) ||
(Preferences.showGreetings && GreetingsHelper.showGreetings())
(Preferences.showGreetings && GreetingsHelper.showGreetings()) ||
(Preferences.showEventsAsGlanceProvider && Preferences.showEvents && context.checkGrantedPermission(
Manifest.permission.READ_CALENDAR) && eventRepository.getNextEvent() != null)
)
eventRepository.close()
return showGlance

View File

@ -30,7 +30,7 @@ object ImageHelper {
canvas.drawColor(Color.TRANSPARENT)
canvas.save()
val rect = Rect()
val bounds = originalView.drawable.copyBounds()
// val bounds = originalView.drawable.copyBounds()
canvas.getClipBounds(rect)
rect.inset(-2 * getBlurRadius(context, cElevation).toInt(), -2 * getBlurRadius(context, cElevation).toInt())
canvas.save()

View File

@ -12,8 +12,10 @@ import android.provider.CalendarContract
import android.provider.CalendarContract.Events
import android.util.Log
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.global.Actions
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.models.Event
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.toast
import java.util.*
@ -21,6 +23,10 @@ import java.util.*
object IntentHelper {
const val DEFAULT_OPTION = ""
const val DO_NOTHING_OPTION = "DO_NOTHING"
const val REFRESH_WIDGET_OPTION = "REFRESH_WIDGET"
fun getWidgetUpdateIntent(context: Context): Intent {
val widgetManager = AppWidgetManager.getInstance(context)
val widgetComponent = ComponentName(context, MainWidget::class.java)
@ -31,6 +37,12 @@ object IntentHelper {
}
}
private fun getWidgetRefreshIntent(context: Context): Intent {
return Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_REFRESH
}
}
fun getGoogleMapsIntentFromAddress(context: Context, address: String): Intent {
val gmmIntentUri: Uri = Uri.parse("geo:0,0?q=$address")
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
@ -47,7 +59,7 @@ object IntentHelper {
fun getWeatherIntent(context: Context): Intent {
return when (Preferences.weatherAppPackage) {
"" -> {
DEFAULT_OPTION -> {
Intent(Intent.ACTION_VIEW).apply {
addCategory(Intent.CATEGORY_DEFAULT)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
@ -55,9 +67,12 @@ object IntentHelper {
component = ComponentName("com.google.android.googlequicksearchbox", "com.google.android.apps.gsa.velour.DynamicActivityTrampoline")
}
}
"_" -> {
DO_NOTHING_OPTION -> {
Intent()
}
REFRESH_WIDGET_OPTION -> {
getWidgetRefreshIntent(context)
}
else -> {
val pm: PackageManager = context.packageManager
try {
@ -79,14 +94,17 @@ object IntentHelper {
.appendPath(Calendar.getInstance().timeInMillis.toString())
.build()
return when (Preferences.calendarAppPackage) {
"" -> {
DEFAULT_OPTION -> {
Intent(Intent.ACTION_VIEW).apply {
data = calendarUri
}
}
"_" -> {
DO_NOTHING_OPTION -> {
Intent()
}
REFRESH_WIDGET_OPTION -> {
getWidgetRefreshIntent(context)
}
else -> {
val pm: PackageManager = context.packageManager
try {
@ -164,14 +182,17 @@ object IntentHelper {
fun getClockIntent(context: Context): Intent {
return when (Preferences.clockAppPackage) {
"" -> {
DEFAULT_OPTION -> {
Intent(AlarmClock.ACTION_SHOW_ALARMS).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
}
"_" -> {
DO_NOTHING_OPTION -> {
Intent()
}
REFRESH_WIDGET_OPTION -> {
getWidgetRefreshIntent(context)
}
else -> {
val pm: PackageManager = context.packageManager
try {
@ -191,7 +212,7 @@ object IntentHelper {
fun getMusicIntent(context: Context): Intent {
return when (Preferences.mediaPlayerPackage) {
"" -> {
DO_NOTHING_OPTION -> {
Intent()
}
else -> {

View File

@ -15,14 +15,32 @@ import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import java.lang.Exception
object MediaPlayerHelper {
const val MEDIA_INFO_TITLE = "%TITLE"
const val MEDIA_INFO_ARTIST = "%ARTIST"
const val MEDIA_INFO_ALBUM = "%ALBUM"
const val DEFAULT_MEDIA_INFO_FORMAT = "%TITLE, %ARTIST"
fun isSomeonePlaying(context: Context) = Preferences.showMusic && ActiveNotificationsHelper.checkNotificationAccess(context) && Preferences.mediaPlayerTitle != ""
fun getMediaInfo(): String {
return if (Preferences.mediaPlayerArtist == "") {
Preferences.mediaPlayerTitle
} else {
"%s, %s".format(Preferences.mediaPlayerTitle, Preferences.mediaPlayerArtist)
}
fun getMediaInfo(format: String = Preferences.mediaInfoFormat, title: String = Preferences.mediaPlayerTitle, artist: String = Preferences.mediaPlayerArtist, album: String = Preferences.mediaPlayerAlbum): String {
return when (format) {
"",
DEFAULT_MEDIA_INFO_FORMAT -> {
if (Preferences.mediaPlayerArtist == "") {
Preferences.mediaPlayerTitle
} else {
DEFAULT_MEDIA_INFO_FORMAT.replace(MEDIA_INFO_TITLE, title)
.replace(MEDIA_INFO_ARTIST, artist)
.replace(MEDIA_INFO_ALBUM, album)
}
}
else -> {
format.replace(MEDIA_INFO_TITLE, title)
.replace(MEDIA_INFO_ARTIST, artist)
.replace(MEDIA_INFO_ALBUM, album)
}
}
}
fun updatePlayingMediaInfo(context: Context) {

View File

@ -63,10 +63,20 @@ object SettingsStringHelper {
}
fun getVariantLabel(context: Context, variant: String): String = when {
variant == "italic" -> context.getString(R.string.font_italic)
variant.contains("100") && variant.contains("italic") -> context.getString(R.string.font_100_italic)
variant.contains("200") && variant.contains("italic") -> context.getString(R.string.font_200_italic)
variant.contains("300") && variant.contains("italic") -> context.getString(R.string.font_300_italic)
variant.contains("400") && variant.contains("italic") -> context.getString(R.string.font_400_italic)
variant.contains("500") && variant.contains("italic") -> context.getString(R.string.font_500_italic)
variant.contains("600") && variant.contains("italic") -> context.getString(R.string.font_600_italic)
variant.contains("700") && variant.contains("italic") -> context.getString(R.string.font_700_italic)
variant.contains("800") && variant.contains("italic") -> context.getString(R.string.font_800_italic)
variant.contains("900") && variant.contains("italic") -> context.getString(R.string.font_900_italic)
variant == "regular" || variant.contains("400") -> context.getString(R.string.font_400)
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)

View File

@ -10,6 +10,7 @@ import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
import com.tommasoberlose.anotherwidget.services.LocationService
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
@ -31,26 +32,8 @@ object WeatherHelper {
val networkApi = WeatherNetworkApi(context)
if (Preferences.customLocationAdd != "") {
networkApi.updateWeather()
} else if (context.checkGrantedPermission(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else Manifest.permission.ACCESS_FINE_LOCATION)) {
LocationServices.getFusedLocationProviderClient(context).lastLocation.addOnCompleteListener { task ->
if (task.isSuccessful) {
val location = task.result
if (location != null) {
Preferences.customLocationLat = location.latitude.toString()
Preferences.customLocationLon = location.longitude.toString()
}
CoroutineScope(Dispatchers.IO).launch {
networkApi.updateWeather()
}
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
} else {
CoroutineScope(Dispatchers.IO).launch {
networkApi.updateWeather()
}
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
}
}
} else if (context.checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
LocationService.requestNewLocation(context)
}
}

View File

@ -25,9 +25,8 @@ object WidgetHelper {
) {
fun getWidgetsSize(widgetId: Int): Pair<Int, Int> {
val isPortrait = context.resources.configuration.orientation == ORIENTATION_PORTRAIT
val width = getWidgetWidth(isPortrait, widgetId)
val height = getWidgetHeight(isPortrait, widgetId)
val width = getWidgetWidth(widgetId)
val height = getWidgetHeight(widgetId)
val widthInPx = context.dip(width)
val heightInPx = context.dip(height)
FirebaseCrashlytics.getInstance().setCustomKey("widthInPx", widthInPx)
@ -35,9 +34,9 @@ object WidgetHelper {
return widthInPx to heightInPx
}
private fun getWidgetWidth(isPortrait: Boolean, widgetId: Int): Int = getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
private fun getWidgetWidth(widgetId: Int): Int = getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
private fun getWidgetHeight(isPortrait: Boolean, widgetId: Int): Int = getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)
private fun getWidgetHeight(widgetId: Int): Int = getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)
private fun getWidgetSizeInDp(widgetId: Int, key: String): Int =
appWidgetManager.getAppWidgetOptions(widgetId).getInt(key, 0)

View File

@ -2,6 +2,7 @@ package com.tommasoberlose.anotherwidget.network
import android.content.Context
import android.util.Log
import com.chibatching.kotpref.Kotpref
import com.google.gson.internal.LinkedTreeMap
import com.haroldadmin.cnradapter.NetworkResponse
import com.haroldadmin.cnradapter.executeWithRetry
@ -26,6 +27,7 @@ import java.util.*
class WeatherNetworkApi(val context: Context) {
suspend fun updateWeather() {
Kotpref.init(context)
Preferences.weatherProviderError = "-"
Preferences.weatherProviderLocationError = ""
@ -344,7 +346,7 @@ class WeatherNetworkApi(val context: Context) {
private suspend fun useAccuweatherProvider(context: Context) {
if (Preferences.weatherProviderApiAccuweather != "") {
val repository = AccuweatherRepository()
// val repository = AccuweatherRepository()
// when (val response = repository.getWeather()) {
// is NetworkResponse.Success -> {

View File

@ -138,10 +138,8 @@ class ActivityDetectionReceiver : BroadcastReceiver() {
val endTime: Long = cal.timeInMillis
val readRequest = DataReadRequest.Builder()
.aggregate(
DataType.TYPE_STEP_COUNT_DELTA,
DataType.AGGREGATE_STEP_COUNT_DELTA
)
.aggregate(DataType.TYPE_STEP_COUNT_DELTA)
.aggregate(DataType.AGGREGATE_STEP_COUNT_DELTA)
.setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
.bucketByTime(1, TimeUnit.DAYS)
.build()

View File

@ -8,6 +8,7 @@ import android.os.Build
import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
import android.util.Log
import android.widget.Toast
import com.tommasoberlose.anotherwidget.global.Actions
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
@ -33,24 +34,20 @@ class NotificationListener : NotificationListenerService() {
val isGroupHeader = sbn.notification.flags and Notification.FLAG_GROUP_SUMMARY != 0
val isOngoing = sbn.notification.flags and Notification.FLAG_ONGOING_EVENT != 0
if (bundle.containsKey(Notification.EXTRA_TITLE) && !isGroupHeader && !isOngoing && ActiveNotificationsHelper.isAppAccepted(sbn.packageName)) {
if (bundle.containsKey(Notification.EXTRA_TITLE) && !isGroupHeader && !isOngoing && ActiveNotificationsHelper.isAppAccepted(sbn.packageName) && !sbn.packageName.contains("com.android.systemui")) {
Preferences.lastNotificationId = sbn.id
Preferences.lastNotificationTitle = bundle.getString(Notification.EXTRA_TITLE) ?: ""
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
try {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
Preferences.lastNotificationIcon = sbn.notification.smallIcon.resId
} catch (ex: Exception) {
Preferences.lastNotificationIcon = 0
}
Preferences.lastNotificationPackage = sbn.notification.smallIcon.resPackage
} else {
try {
} else {
@Suppress("DEPRECATION")
Preferences.lastNotificationIcon = sbn.notification.icon
} catch (ex: Exception) {
Preferences.lastNotificationIcon = 0
}
Preferences.lastNotificationPackage = sbn.packageName
} catch (ex: Exception) {
Preferences.lastNotificationIcon = 0
}
Preferences.lastNotificationPackage = sbn.packageName
MainWidget.updateWidget(this)
setTimeout(this)
}

View File

@ -12,12 +12,12 @@ 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.ActiveNotificationsHelper
import com.tommasoberlose.anotherwidget.helpers.BatteryHelper
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
import com.tommasoberlose.anotherwidget.helpers.*
import com.tommasoberlose.anotherwidget.models.Event
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.joda.time.Period
import java.util.*
@ -33,9 +33,9 @@ class UpdatesReceiver : BroadcastReceiver() {
Intent.ACTION_LOCALE_CHANGED,
Intent.ACTION_DATE_CHANGED,
Actions.ACTION_CALENDAR_UPDATE -> {
CalendarHelper.updateEventList(context)
ActiveNotificationsHelper.clearLastNotification(context)
MediaPlayerHelper.updatePlayingMediaInfo(context)
CalendarHelper.updateEventList(context)
}
"com.sec.android.widgetapp.APPWIDGET_RESIZE",
@ -55,6 +55,16 @@ class UpdatesReceiver : BroadcastReceiver() {
Actions.ACTION_UPDATE_GREETINGS -> {
MainWidget.updateWidget(context)
}
Actions.ACTION_REFRESH -> {
ActiveNotificationsHelper.clearLastNotification(context)
GlobalScope.launch(Dispatchers.IO) {
CalendarHelper.updateEventList(context)
MediaPlayerHelper.updatePlayingMediaInfo(context)
WeatherHelper.updateWeather(context)
}
}
}
}

View File

@ -0,0 +1,127 @@
package com.tommasoberlose.anotherwidget.services
import android.Manifest
import android.app.*
import android.app.job.JobScheduler
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.location.Address
import android.location.Geocoder
import android.os.IBinder
import android.util.Log
import androidx.core.app.*
import androidx.core.content.ContextCompat
import com.google.android.gms.location.LocationServices
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus
import java.lang.Exception
import java.util.*
import kotlin.collections.ArrayList
class LocationService : Service() {
private var job: Job? = null
override fun onCreate() {
super.onCreate()
startForeground(LOCATION_ACCESS_NOTIFICATION_ID, getLocationAccessNotification())
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
job?.cancel()
job = GlobalScope.launch(Dispatchers.IO) {
if (ActivityCompat.checkSelfPermission(
this@LocationService,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
LocationServices.getFusedLocationProviderClient(this@LocationService).lastLocation.addOnCompleteListener { task ->
val networkApi = WeatherNetworkApi(this@LocationService)
if (task.isSuccessful) {
val location = task.result
if (location != null) {
Preferences.customLocationLat = location.latitude.toString()
Preferences.customLocationLon = location.longitude.toString()
}
CoroutineScope(Dispatchers.IO).launch {
networkApi.updateWeather()
withContext(Dispatchers.Main) {
stopSelf()
}
}
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
} else {
CoroutineScope(Dispatchers.IO).launch {
networkApi.updateWeather()
withContext(Dispatchers.Main) {
stopSelf()
}
}
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
}
}
} else {
stopSelf()
}
}
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
job?.cancel()
job = null
}
companion object {
const val LOCATION_ACCESS_NOTIFICATION_ID = 28465
@JvmStatic
fun requestNewLocation(context: Context) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
context.startForegroundService(Intent(context, LocationService::class.java))
} else {
context.startService(Intent(context, LocationService::class.java))
}
}
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
private fun getLocationAccessNotification(): Notification {
with(NotificationManagerCompat.from(this)) {
// Create channel
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
createNotificationChannel(
NotificationChannel(
getString(R.string.location_access_notification_channel_id),
getString(R.string.location_access_notification_channel_name),
NotificationManager.IMPORTANCE_LOW
).apply {
description = getString(R.string.location_access_notification_channel_description)
}
)
}
val builder = NotificationCompat.Builder(this@LocationService, getString(R.string.location_access_notification_channel_id))
.setSmallIcon(R.drawable.ic_stat_notification)
.setContentTitle(getString(R.string.location_access_notification_title))
.setOngoing(true)
.setColor(ContextCompat.getColor(this@LocationService, R.color.colorAccent))
// Main intent that open the activity
builder.setContentIntent(PendingIntent.getActivity(this@LocationService, 0, Intent(this@LocationService, MainActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT))
return builder.build()
}
}
}

View File

@ -1,135 +0,0 @@
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.helpers.CalendarHelper.sortEvents
import com.tommasoberlose.anotherwidget.models.Event
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import 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 {
when (Preferences.showUntil) {
0 -> add(Calendar.HOUR, 3)
1 -> add(Calendar.HOUR, 6)
2 -> add(Calendar.HOUR, 12)
3 -> add(Calendar.DAY_OF_MONTH, 1)
4 -> add(Calendar.DAY_OF_MONTH, 3)
5 -> add(Calendar.DAY_OF_MONTH, 7)
6 -> add(Calendar.MINUTE, 30)
7 -> add(Calendar.HOUR, 1)
else -> add(Calendar.HOUR, 6)
}
}
if (!checkGrantedPermission(
Manifest.permission.READ_CALENDAR
)
) {
eventRepository.resetNextEventData()
} 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 sortedEvents = eventList.sortEvents()
val filteredEventList = sortedEvents
.applyFilters()
if (filteredEventList.isEmpty()) {
eventRepository.resetNextEventData()
eventRepository.clearEvents()
} else {
eventRepository.saveEvents(
sortedEvents
)
eventRepository.saveNextEventData(filteredEventList.first())
}
} catch (ignored: java.lang.Exception) {
}
}
} else {
eventRepository.resetNextEventData()
}
UpdatesReceiver.setUpdates(this)
MainWidget.updateWidget(this)
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
eventRepository.close()
}
}

View File

@ -0,0 +1,218 @@
package com.tommasoberlose.anotherwidget.services
import android.Manifest
import android.app.*
import android.content.Context
import android.content.Intent
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.applyFilters
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.sortEvents
import com.tommasoberlose.anotherwidget.models.Event
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import me.everything.providers.android.calendar.CalendarProvider
import org.greenrobot.eventbus.EventBus
import java.util.*
import kotlin.collections.ArrayList
class UpdateCalendarService : Service() {
companion object {
const val CALENDAR_SYNC_NOTIFICATION_ID = 28468
fun enqueueWork(context: Context) {
context.startService(Intent(context, UpdateCalendarService::class.java))
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
context.startForegroundService(Intent(context, UpdateCalendarService::class.java))
} else {
context.startService(Intent(context, UpdateCalendarService::class.java))
}
}
}
override fun onCreate() {
super.onCreate()
startForeground(CALENDAR_SYNC_NOTIFICATION_ID, getCalendarSyncNotification())
}
private var job: Job? = null
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
job?.cancel()
job = GlobalScope.launch(Dispatchers.IO) {
val eventRepository = EventRepository(this@UpdateCalendarService)
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 {
when (Preferences.showUntil) {
0 -> add(Calendar.HOUR, 3)
1 -> add(Calendar.HOUR, 6)
2 -> add(Calendar.HOUR, 12)
3 -> add(Calendar.DAY_OF_MONTH, 1)
4 -> add(Calendar.DAY_OF_MONTH, 3)
5 -> add(Calendar.DAY_OF_MONTH, 7)
6 -> add(Calendar.MINUTE, 30)
7 -> add(Calendar.HOUR, 1)
else -> add(Calendar.HOUR, 6)
}
}
if (!checkGrantedPermission(
Manifest.permission.READ_CALENDAR
)
) {
eventRepository.resetNextEventData()
} else {
try {
val provider = CalendarProvider(this@UpdateCalendarService)
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)
}
// Check all day events
val startDate = Calendar.getInstance()
startDate.timeInMillis = instance.begin
val endDate = Calendar.getInstance()
endDate.timeInMillis = instance.end
val isAllDay = e.allDay || (
startDate.get(Calendar.MILLISECOND) == 0
&& startDate.get(Calendar.SECOND) == 0
&& startDate.get(Calendar.MINUTE) == 0
&& startDate.get(Calendar.HOUR_OF_DAY) == 0
&& endDate.get(Calendar.MILLISECOND) == 0
&& endDate.get(Calendar.SECOND) == 0
&& endDate.get(Calendar.MINUTE) == 0
&& endDate.get(Calendar.HOUR_OF_DAY) == 0
)
eventList.add(
Event(
id = instance.id,
eventID = e.id,
title = e.title ?: "",
startDate = instance.begin,
endDate = instance.end,
calendarID = e.calendarId.toInt(),
allDay = isAllDay,
address = e.eventLocation ?: "",
selfAttendeeStatus = e.selfAttendeeStatus.toInt(),
availability = e.availability
)
)
}
} catch (ignored: Exception) {
}
}
}
val sortedEvents = eventList.sortEvents()
val filteredEventList = sortedEvents
.applyFilters()
if (filteredEventList.isEmpty()) {
eventRepository.resetNextEventData()
eventRepository.clearEvents()
} else {
eventRepository.saveEvents(
sortedEvents
)
eventRepository.saveNextEventData(filteredEventList.first())
}
} catch (ignored: java.lang.Exception) {
}
}
} else {
eventRepository.resetNextEventData()
}
UpdatesReceiver.setUpdates(this@UpdateCalendarService)
MainWidget.updateWidget(this@UpdateCalendarService)
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
eventRepository.close()
stopSelf()
}
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
job?.cancel()
job = null
}
private fun getCalendarSyncNotification(): Notification {
with(NotificationManagerCompat.from(this)) {
// Create channel
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
createNotificationChannel(
NotificationChannel(
getString(R.string.calendar_sync_notification_channel_id),
getString(R.string.calendar_sync_notification_channel_name),
NotificationManager.IMPORTANCE_LOW
).apply {
description = getString(R.string.calendar_sync_notification_channel_description)
}
)
}
val builder = NotificationCompat.Builder(this@UpdateCalendarService, getString(R.string.calendar_sync_notification_channel_id))
.setSmallIcon(R.drawable.ic_stat_notification)
.setContentTitle(getString(R.string.calendar_sync_notification_title))
.setOngoing(true)
.setColor(ContextCompat.getColor(this@UpdateCalendarService, R.color.colorAccent))
// Main intent that open the activity
builder.setContentIntent(PendingIntent.getActivity(this@UpdateCalendarService, 0, Intent(this@UpdateCalendarService, MainActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT))
return builder.build()
}
}
}

View File

@ -1,148 +0,0 @@
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.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.bumptech.glide.Glide
import com.tommasoberlose.anotherwidget.databinding.ActivityChooseApplicationBinding
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.ui.viewmodels.ChooseApplicationViewModel
import kotlinx.android.synthetic.main.activity_choose_application.*
import kotlinx.android.synthetic.main.activity_choose_application.action_back
import kotlinx.android.synthetic.main.activity_choose_application.clear_search
import kotlinx.android.synthetic.main.activity_choose_application.list_view
import kotlinx.android.synthetic.main.activity_choose_application.loader
import kotlinx.android.synthetic.main.activity_choose_application.search
import kotlinx.android.synthetic.main.activity_music_players_filter.*
import kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter
import net.idik.lib.slimadapter.SlimAdapterEx
class ChooseApplicationActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter
private lateinit var viewModel: ChooseApplicationViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(ChooseApplicationViewModel::class.java)
val binding = DataBindingUtil.setContentView<ActivityChooseApplicationBinding>(this, R.layout.activity_choose_application)
list_view.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager
adapter = SlimAdapterEx.create()
adapter
.register<String>(R.layout.application_info_layout) { _, injector ->
injector
.text(R.id.text, getString(R.string.default_name))
.image(R.id.icon, R.drawable.round_add_to_home_screen)
.with<ImageView>(R.id.icon) {
it.scaleX = 0.8f
it.scaleY = 0.8f
it.setColorFilter(ContextCompat.getColor(this, R.color.colorPrimaryText), android.graphics.PorterDuff.Mode.MULTIPLY)
}
.clicked(R.id.item) {
val resultIntent = Intent()
resultIntent.putExtra(Constants.RESULT_APP_NAME, "")
resultIntent.putExtra(Constants.RESULT_APP_PACKAGE, "")
setResult(Activity.RESULT_OK, resultIntent)
finish()
}
}
.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)
}
injector.clicked(R.id.item) {
saveApp(item)
}
}
.attachTo(list_view)
setupListener()
subscribeUi(binding, viewModel)
search.requestFocus()
}
private var filterJob: Job? = null
private fun subscribeUi(binding: ActivityChooseApplicationBinding, viewModel: ChooseApplicationViewModel) {
binding.viewModel = viewModel
binding.lifecycleOwner = this
viewModel.appList.observe(this, Observer {
updateList(list = it)
loader.visibility = View.INVISIBLE
})
viewModel.searchInput.observe(this, Observer { search ->
updateList(search = search)
clear_search.isVisible = search.isNotBlank()
})
}
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)
}
}
withContext(Dispatchers.Main) {
adapter.updateData(listOf("Default") + filteredList)
loader.visibility = View.INVISIBLE
}
}
}
}
private fun setupListener() {
action_back.setOnClickListener {
onBackPressed()
}
clear_search.setOnClickListener {
viewModel.searchInput.value = ""
}
}
private fun saveApp(app: ResolveInfo) {
val resultIntent = Intent()
resultIntent.putExtra(Constants.RESULT_APP_NAME, app.loadLabel(viewModel.pm))
resultIntent.putExtra(Constants.RESULT_APP_PACKAGE, app.activityInfo.packageName)
setResult(Activity.RESULT_OK, resultIntent)
finish()
}
}

View File

@ -1,93 +1,74 @@
package com.tommasoberlose.anotherwidget.ui.activities
import android.Manifest
import android.animation.ValueAnimator
import android.app.Activity
import android.appwidget.AppWidgetManager
import android.content.Intent
import android.content.SharedPreferences
import android.graphics.Bitmap
import android.graphics.Matrix
import android.net.Uri
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.View
import android.widget.RelativeLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.core.animation.addListener
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
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
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.MaterialBottomSheetDialog
import com.tommasoberlose.anotherwidget.databinding.ActivityMainBinding
import com.tommasoberlose.anotherwidget.global.Actions
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.global.RequestCode
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.ui.adapters.ViewPagerAdapter
import com.tommasoberlose.anotherwidget.ui.activities.tabs.WeatherProviderActivity
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 kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.the_widget_sans.*
import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
class MainActivity : AppCompatActivity() {
class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
private var mAppWidgetId: Int = -1
private lateinit var viewModel: MainViewModel
private lateinit var binding: ActivityMainBinding
private val mainNavController: NavController? by lazy {
Navigation.findNavController(
this,
R.id.content_fragment
)
}
private val settingsNavController: NavController? by lazy {
Navigation.findNavController(
this,
R.id.settings_fragment
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
binding = ActivityMainBinding.inflate(layoutInflater)
controlExtras(intent)
if (Preferences.showWallpaper) {
requirePermission()
}
setContentView(binding.root)
}
override fun onBackPressed() {
if (mainNavController?.currentDestination?.id == R.id.appMainFragment) {
if (mAppWidgetId > 0) {
addNewWidget()
if (settingsNavController?.navigateUp() == false) {
if (mAppWidgetId > 0) {
addNewWidget()
} else {
setResult(Activity.RESULT_OK)
finish()
}
} else {
setResult(Activity.RESULT_OK)
finish()
viewModel.fragmentScrollY.value = 0
}
} else {
super.onBackPressed()
@ -110,8 +91,8 @@ class MainActivity : AppCompatActivity() {
AppWidgetManager.INVALID_APPWIDGET_ID)
if (mAppWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
action_add_widget.visibility = View.VISIBLE
action_add_widget.setOnClickListener {
binding.actionAddWidget.visibility = View.VISIBLE
binding.actionAddWidget.setOnClickListener {
addNewWidget()
}
}
@ -153,4 +134,26 @@ class MainActivity : AppCompatActivity() {
})
.check()
}
override fun onResume() {
super.onResume()
if (Preferences.showEvents && !checkGrantedPermission(Manifest.permission.READ_CALENDAR)) {
Preferences.showEvents = false
}
}
override fun onStart() {
Preferences.preferences.registerOnSharedPreferenceChangeListener(this)
super.onStart()
}
override fun onStop() {
super.onStop()
Preferences.preferences.unregisterOnSharedPreferenceChangeListener(this)
}
override fun onSharedPreferenceChanged(p0: SharedPreferences?, p1: String?) {
MainWidget.updateWidget(this)
}
}

View File

@ -1,30 +1,29 @@
package com.tommasoberlose.anotherwidget.ui.activities
package com.tommasoberlose.anotherwidget.ui.activities.settings
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.ActivityIntegrationsBinding
import com.tommasoberlose.anotherwidget.ui.viewmodels.IntegrationsViewModel
import kotlinx.android.synthetic.main.activity_integrations.*
import com.tommasoberlose.anotherwidget.ui.viewmodels.settings.IntegrationsViewModel
import net.idik.lib.slimadapter.SlimAdapter
class IntegrationsActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter
private lateinit var viewModel: IntegrationsViewModel
private lateinit var binding: ActivityIntegrationsBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(IntegrationsViewModel::class.java)
val binding = DataBindingUtil.setContentView<ActivityIntegrationsBinding>(this, R.layout.activity_integrations)
binding = ActivityIntegrationsBinding.inflate(layoutInflater)
list_view.setHasFixedSize(true)
binding.listView.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager
binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapter.create()
adapter
@ -33,10 +32,12 @@ class IntegrationsActivity : AppCompatActivity() {
.text(R.id.text, getString(R.string.default_name))
}
.attachTo(list_view)
.attachTo(binding.listView)
setupListener()
subscribeUi(binding, viewModel)
setContentView(binding.root)
}
private fun subscribeUi(binding: ActivityIntegrationsBinding, viewModel: IntegrationsViewModel) {
@ -45,7 +46,7 @@ class IntegrationsActivity : AppCompatActivity() {
}
private fun setupListener() {
action_back.setOnClickListener {
binding.actionBack.setOnClickListener {
onBackPressed()
}
}

View File

@ -1,47 +1,38 @@
package com.tommasoberlose.anotherwidget.ui.activities
package com.tommasoberlose.anotherwidget.ui.activities.settings
import android.app.Activity
import android.content.Intent
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
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.android.billingclient.api.*
import com.android.billingclient.api.BillingClient.BillingResponseCode.OK
import com.android.billingclient.api.BillingClient.BillingResponseCode.USER_CANCELED
import com.chibatching.kotpref.bulk
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.ActivitySupportDevBinding
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.ui.viewmodels.SupportDevViewModel
import com.tommasoberlose.anotherwidget.ui.viewmodels.settings.SupportDevViewModel
import com.tommasoberlose.anotherwidget.utils.toast
import kotlinx.android.synthetic.main.activity_support_dev.*
import net.idik.lib.slimadapter.SlimAdapter
class SupportDevActivity : AppCompatActivity(), PurchasesUpdatedListener {
private lateinit var viewModel: SupportDevViewModel
private lateinit var adapter: SlimAdapter
private lateinit var binding: ActivitySupportDevBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(SupportDevViewModel::class.java)
viewModel.billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build()
DataBindingUtil.setContentView<ActivitySupportDevBinding>(this, R.layout.activity_support_dev)
binding = ActivitySupportDevBinding.inflate(layoutInflater)
list_view.setHasFixedSize(true)
binding.listView.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager
binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapter.create()
adapter
@ -62,20 +53,22 @@ class SupportDevActivity : AppCompatActivity(), PurchasesUpdatedListener {
viewModel.purchase(this, item)
}
}
.attachTo(list_view)
.attachTo(binding.listView)
viewModel.openConnection()
subscribeUi(viewModel)
action_back.setOnClickListener {
binding.actionBack.setOnClickListener {
onBackPressed()
}
setContentView(binding.root)
}
private fun subscribeUi(viewModel: SupportDevViewModel) {
viewModel.products.observe(this, Observer {
if (it.isNotEmpty()) {
loader.isVisible = false
binding.loader.isVisible = false
}
adapter.updateData(it.sortedWith(compareBy(SkuDetails::getPriceAmountMicros)))
})

View File

@ -1,4 +1,4 @@
package com.tommasoberlose.anotherwidget.ui.activities
package com.tommasoberlose.anotherwidget.ui.activities.tabs
import android.content.pm.ResolveInfo
import android.os.Bundle
@ -6,7 +6,6 @@ import android.view.View
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
@ -16,8 +15,7 @@ import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.ActivityAppNotificationsFilterBinding
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper
import com.tommasoberlose.anotherwidget.ui.viewmodels.AppNotificationsViewModel
import kotlinx.android.synthetic.main.activity_app_notifications_filter.*
import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.AppNotificationsViewModel
import kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter
@ -26,16 +24,17 @@ class AppNotificationsFilterActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter
private lateinit var viewModel: AppNotificationsViewModel
private lateinit var binding: ActivityAppNotificationsFilterBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(AppNotificationsViewModel::class.java)
val binding = DataBindingUtil.setContentView<ActivityAppNotificationsFilterBinding>(this, R.layout.activity_app_notifications_filter)
binding = ActivityAppNotificationsFilterBinding.inflate(layoutInflater)
list_view.setHasFixedSize(true)
binding.listView.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager
binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapter.create()
adapter
@ -60,12 +59,14 @@ class AppNotificationsFilterActivity : AppCompatActivity() {
}
.checked(R.id.checkBox, ActiveNotificationsHelper.isAppAccepted(item.activityInfo.packageName))
}
.attachTo(list_view)
.attachTo(binding.listView)
setupListener()
subscribeUi(binding, viewModel)
search.requestFocus()
binding.search.requestFocus()
setContentView(binding.root)
}
private var filterJob: Job? = null
@ -76,22 +77,22 @@ class AppNotificationsFilterActivity : AppCompatActivity() {
viewModel.appList.observe(this, Observer {
updateList(list = it)
loader.visibility = View.INVISIBLE
binding.loader.visibility = View.INVISIBLE
})
viewModel.searchInput.observe(this, Observer { search ->
updateList(search = search)
clear_search.isVisible = search.isNotBlank()
binding.clearSearch.isVisible = search.isNotBlank()
})
viewModel.appNotificationsFilter.observe(this, {
updateList()
clear_selection.isVisible = Preferences.appNotificationsFilter != ""
binding.clearSelection.isVisible = Preferences.appNotificationsFilter != ""
})
}
private fun updateList(list: List<ResolveInfo>? = viewModel.appList.value, search: String? = viewModel.searchInput.value) {
loader.visibility = View.VISIBLE
binding.loader.visibility = View.VISIBLE
filterJob?.cancel()
filterJob = lifecycleScope.launch(Dispatchers.IO) {
if (list != null && list.isNotEmpty()) {
@ -117,22 +118,22 @@ class AppNotificationsFilterActivity : AppCompatActivity() {
withContext(Dispatchers.Main) {
adapter.updateData(filteredList)
loader.visibility = View.INVISIBLE
binding.loader.visibility = View.INVISIBLE
}
}
}
}
private fun setupListener() {
action_back.setOnClickListener {
binding.actionBack.setOnClickListener {
onBackPressed()
}
clear_search.setOnClickListener {
binding.clearSearch.setOnClickListener {
viewModel.searchInput.value = ""
}
clear_selection.setOnClickListener {
binding.clearSelection.setOnClickListener {
Preferences.appNotificationsFilter = ""
}
}

View File

@ -0,0 +1,200 @@
package com.tommasoberlose.anotherwidget.ui.activities.tabs
import android.app.Activity
import android.os.Bundle
import com.tommasoberlose.anotherwidget.R
import android.content.Intent
import android.content.pm.ResolveInfo
import android.view.View
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
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.card.MaterialCardView
import com.tommasoberlose.anotherwidget.databinding.ActivityChooseApplicationBinding
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.helpers.IntentHelper
import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.ChooseApplicationViewModel
import kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter
import net.idik.lib.slimadapter.SlimAdapterEx
class ChooseApplicationActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter
private lateinit var viewModel: ChooseApplicationViewModel
private lateinit var binding: ActivityChooseApplicationBinding
private var selectedPackage: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
selectedPackage = intent.extras?.getString(Constants.RESULT_APP_PACKAGE)
viewModel = ViewModelProvider(this).get(ChooseApplicationViewModel::class.java)
binding = ActivityChooseApplicationBinding.inflate(layoutInflater)
binding.listView.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this)
binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapterEx.create()
adapter
.register<String>(R.layout.application_info_layout) { item, injector ->
when (item) {
IntentHelper.DO_NOTHING_OPTION -> {
injector
.text(R.id.text, getString(R.string.gestures_do_nothing))
.image(R.id.icon, R.drawable.round_no_cell_24)
.with<ImageView>(R.id.icon) {
it.scaleX = 0.8f
it.scaleY = 0.8f
it.setColorFilter(ContextCompat.getColor(this, R.color.colorPrimaryText), android.graphics.PorterDuff.Mode.MULTIPLY)
}
.clicked(R.id.item) {
val resultIntent = Intent()
resultIntent.putExtra(Constants.RESULT_APP_NAME, IntentHelper.DO_NOTHING_OPTION)
resultIntent.putExtra(Constants.RESULT_APP_PACKAGE, IntentHelper.DO_NOTHING_OPTION)
setResult(Activity.RESULT_OK, resultIntent)
finish()
}
.with<MaterialCardView>(R.id.item) {
it.strokeColor = ContextCompat.getColor(this, if (selectedPackage == IntentHelper.DO_NOTHING_OPTION) R.color.colorAccent else R.color.cardBorder)
it.setCardBackgroundColor(ContextCompat.getColor(this, if (selectedPackage == IntentHelper.DO_NOTHING_OPTION) R.color.colorAccent_op10 else R.color.colorPrimaryDark))
}
}
IntentHelper.REFRESH_WIDGET_OPTION -> {
injector
.text(R.id.text, getString(R.string.action_refresh_widget))
.image(R.id.icon, R.drawable.round_refresh)
.with<ImageView>(R.id.icon) {
it.scaleX = 0.8f
it.scaleY = 0.8f
it.setColorFilter(ContextCompat.getColor(this, R.color.colorPrimaryText), android.graphics.PorterDuff.Mode.MULTIPLY)
}
.clicked(R.id.item) {
val resultIntent = Intent()
resultIntent.putExtra(Constants.RESULT_APP_NAME, IntentHelper.REFRESH_WIDGET_OPTION)
resultIntent.putExtra(Constants.RESULT_APP_PACKAGE, IntentHelper.REFRESH_WIDGET_OPTION)
setResult(Activity.RESULT_OK, resultIntent)
finish()
}
.with<MaterialCardView>(R.id.item) {
it.strokeColor = ContextCompat.getColor(this, if (selectedPackage == IntentHelper.REFRESH_WIDGET_OPTION) R.color.colorAccent else R.color.cardBorder)
it.setCardBackgroundColor(ContextCompat.getColor(this, if (selectedPackage == IntentHelper.REFRESH_WIDGET_OPTION) R.color.colorAccent_op10 else R.color.colorPrimaryDark))
}
}
else -> {
injector
.text(R.id.text, getString(R.string.default_name))
.image(R.id.icon, R.drawable.round_add_to_home_screen_24)
.with<ImageView>(R.id.icon) {
it.scaleX = 0.8f
it.scaleY = 0.8f
it.setColorFilter(ContextCompat.getColor(this, R.color.colorPrimaryText), android.graphics.PorterDuff.Mode.MULTIPLY)
}
.clicked(R.id.item) {
val resultIntent = Intent()
resultIntent.putExtra(Constants.RESULT_APP_NAME, IntentHelper.DEFAULT_OPTION)
resultIntent.putExtra(Constants.RESULT_APP_PACKAGE, IntentHelper.DEFAULT_OPTION)
setResult(Activity.RESULT_OK, resultIntent)
finish()
}
.with<MaterialCardView>(R.id.item) {
it.strokeColor = ContextCompat.getColor(this, if (selectedPackage == IntentHelper.DEFAULT_OPTION) R.color.colorAccent else R.color.cardBorder)
it.setCardBackgroundColor(ContextCompat.getColor(this, if (selectedPackage == IntentHelper.DEFAULT_OPTION) R.color.colorAccent_op10 else R.color.colorPrimaryDark))
}
}
}
}
.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)
}
.clicked(R.id.item) {
saveApp(item)
}
.with<MaterialCardView>(R.id.item) {
it.strokeColor = ContextCompat.getColor(this, if (selectedPackage == item.activityInfo.packageName) R.color.colorAccent else R.color.cardBorder)
it.setCardBackgroundColor(ContextCompat.getColor(this, if (selectedPackage == item.activityInfo.packageName) R.color.colorAccent_op10 else R.color.colorPrimaryDark))
}
}
.attachTo(binding.listView)
setupListener()
subscribeUi(binding, viewModel)
binding.search.requestFocus()
setContentView(binding.root)
}
private var filterJob: Job? = null
private fun subscribeUi(binding: ActivityChooseApplicationBinding, viewModel: ChooseApplicationViewModel) {
binding.viewModel = viewModel
binding.lifecycleOwner = this
viewModel.appList.observe(this) {
updateList(list = it)
binding.loader.visibility = View.INVISIBLE
}
viewModel.searchInput.observe(this) { search ->
updateList(search = search)
binding.clearSearch.isVisible = search.isNotBlank()
}
}
private fun updateList(list: List<ResolveInfo>? = viewModel.appList.value, search: String? = viewModel.searchInput.value) {
binding.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)
}
}
withContext(Dispatchers.Main) {
adapter.updateData(listOf(IntentHelper.DO_NOTHING_OPTION, IntentHelper.DEFAULT_OPTION, IntentHelper.REFRESH_WIDGET_OPTION) + filteredList)
binding.loader.visibility = View.INVISIBLE
}
}
}
}
private fun setupListener() {
binding.actionBack.setOnClickListener {
onBackPressed()
}
binding.clearSearch.setOnClickListener {
viewModel.searchInput.value = ""
}
}
private fun saveApp(app: ResolveInfo) {
val resultIntent = Intent()
resultIntent.putExtra(Constants.RESULT_APP_NAME, app.loadLabel(viewModel.pm))
resultIntent.putExtra(Constants.RESULT_APP_PACKAGE, app.activityInfo.packageName)
setResult(Activity.RESULT_OK, resultIntent)
finish()
}
}

View File

@ -1,61 +1,53 @@
package com.tommasoberlose.anotherwidget.ui.activities
package com.tommasoberlose.anotherwidget.ui.activities.tabs
import android.app.Activity
import android.location.Address
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.ui.viewmodels.tabs.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
import kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter
import java.lang.Exception
import java.text.SimpleDateFormat
import java.util.*
class CustomDateActivity : AppCompatActivity() {
class CustomDateActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter
private lateinit var viewModel: CustomDateViewModel
private lateinit var binding: ActivityCustomDateBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(CustomDateViewModel::class.java)
val binding = DataBindingUtil.setContentView<ActivityCustomDateBinding>(this, R.layout.activity_custom_date)
binding = ActivityCustomDateBinding.inflate(layoutInflater)
list_view.setHasFixedSize(true)
binding.listView.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager
binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapter.create()
adapter
.register<String>(R.layout.custom_date_example_item) { item, injector ->
injector
.text(R.id.custom_date_example_format, item)
.text(R.id.custom_date_example_value, SimpleDateFormat(item, Locale.getDefault()).format(DATE.time))
.text(R.id.custom_date_example_value, SimpleDateFormat(item, Locale.getDefault()).format(
DATE.time))
}
.attachTo(list_view)
.attachTo(binding.listView)
adapter.updateData(
listOf(
@ -66,9 +58,11 @@ class CustomDateActivity : AppCompatActivity() {
setupListener()
subscribeUi(binding, viewModel)
date_format.requestFocus()
binding.dateFormat.requestFocus()
setContentView(binding.root)
}
private var formatJob: Job? = null
private fun subscribeUi(binding: ActivityCustomDateBinding, viewModel: CustomDateViewModel) {
@ -79,7 +73,7 @@ class CustomDateActivity : AppCompatActivity() {
formatJob?.cancel()
formatJob = lifecycleScope.launch(Dispatchers.IO) {
withContext(Dispatchers.Main) {
loader.visibility = View.VISIBLE
binding.loader.visibility = View.VISIBLE
}
delay(200)
@ -102,8 +96,8 @@ class CustomDateActivity : AppCompatActivity() {
}
withContext(Dispatchers.Main) {
loader.visibility = View.INVISIBLE
date_format_value.text = text
binding.loader.visibility = View.INVISIBLE
binding.dateFormatValue.text = text
}
}
@ -123,26 +117,26 @@ class CustomDateActivity : AppCompatActivity() {
private fun updateCapitalizeUi() {
when {
viewModel.isDateUppercase.value == true -> {
action_capitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.round_publish))
action_capitalize.alpha = 1f
binding.actionCapitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.round_publish))
binding.actionCapitalize.alpha = 1f
}
viewModel.isDateCapitalize.value == true -> {
action_capitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.ic_capitalize))
action_capitalize.alpha = 1f
binding.actionCapitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.ic_capitalize))
binding.actionCapitalize.alpha = 1f
}
else -> {
action_capitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.round_publish))
action_capitalize.alpha = 0.3f
binding.actionCapitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.round_publish))
binding.actionCapitalize.alpha = 0.3f
}
}
}
private fun setupListener() {
action_back.setOnClickListener {
binding.actionBack.setOnClickListener {
onBackPressed()
}
action_capitalize.setOnClickListener {
binding.actionCapitalize.setOnClickListener {
when {
viewModel.isDateUppercase.value == true -> {
viewModel.isDateCapitalize.value = false
@ -159,12 +153,12 @@ class CustomDateActivity : AppCompatActivity() {
}
}
action_capitalize.setOnLongClickListener {
binding.actionCapitalize.setOnLongClickListener {
toast(getString(R.string.action_capitalize_the_date))
true
}
action_date_format_info.setOnClickListener {
binding.actionDateFormatInfo.setOnClickListener {
openURI("https://developer.android.com/reference/java/text/SimpleDateFormat")
}
}

View File

@ -1,4 +1,4 @@
package com.tommasoberlose.anotherwidget.ui.activities
package com.tommasoberlose.anotherwidget.ui.activities.tabs
import android.app.Activity
import android.content.Intent
@ -6,6 +6,7 @@ import android.graphics.Typeface
import android.os.Bundle
import android.os.Handler
import android.os.HandlerThread
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
@ -13,28 +14,20 @@ 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.google.gson.Gson
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.android.synthetic.main.activity_choose_application.action_back
import kotlinx.android.synthetic.main.activity_choose_application.clear_search
import kotlinx.android.synthetic.main.activity_choose_application.list_view
import kotlinx.android.synthetic.main.activity_choose_application.loader
import kotlinx.android.synthetic.main.activity_choose_application.search
import kotlinx.android.synthetic.main.activity_music_players_filter.*
import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.CustomFontViewModel
import kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter
import net.idik.lib.slimadapter.diff.DefaultDiffCallback
@ -44,19 +37,17 @@ class CustomFontActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter
private lateinit var viewModel: CustomFontViewModel
private lateinit var binding: ActivityCustomFontBinding
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
)
binding = ActivityCustomFontBinding.inflate(layoutInflater)
list_view.setHasFixedSize(true)
binding.listView.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager
binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapter.create()
adapter.enableDiff(object: DefaultDiffCallback() {
@ -86,7 +77,7 @@ class CustomFontActivity : AppCompatActivity() {
injector.clicked(R.id.text) {
val dialog = BottomSheetMenu<String>(this, header = item)
listOf("100", "200", "regular", "500", "700", "800").forEachIndexed { index, s ->
listOf("100", "200", "regular", "500", "700", "800").forEachIndexed { _, s ->
dialog.addItem(SettingsStringHelper.getVariantLabel(this, s), s)
}
dialog.addOnSelectItemListener { value ->
@ -136,7 +127,7 @@ class CustomFontActivity : AppCompatActivity() {
if (item.fontVariants.isEmpty()) {
dialog.addItem(SettingsStringHelper.getVariantLabel(this, "regular"), -1)
} else {
item.fontVariants.filter { !it.contains("italic") }
item.fontVariants
.forEachIndexed { index, s ->
dialog.addItem(SettingsStringHelper.getVariantLabel(this, s), index)
}
@ -146,12 +137,14 @@ class CustomFontActivity : AppCompatActivity() {
}.show()
}
}
.attachTo(list_view)
.attachTo(binding.listView)
setupListener()
subscribeUi(binding, viewModel)
search.requestFocus()
binding.search.requestFocus()
setContentView(binding.root)
}
private var filterJob: Job? = null
@ -162,12 +155,12 @@ class CustomFontActivity : AppCompatActivity() {
viewModel.fontList.observe(this, Observer {
updateList(list = it)
loader.visibility = View.INVISIBLE
binding.loader.visibility = View.INVISIBLE
})
viewModel.searchInput.observe(this, Observer { search ->
updateList(search = search)
clear_search.isVisible = search.isNotBlank()
binding.clearSearch.isVisible = search.isNotBlank()
})
}
@ -175,7 +168,7 @@ class CustomFontActivity : AppCompatActivity() {
list: ArrayList<Font>? = viewModel.fontList.value,
search: String? = viewModel.searchInput.value
) {
loader.visibility = View.VISIBLE
binding.loader.visibility = View.VISIBLE
filterJob?.cancel()
filterJob = lifecycleScope.launch(Dispatchers.IO) {
if (list != null && list.isNotEmpty()) {
@ -209,18 +202,18 @@ class CustomFontActivity : AppCompatActivity() {
}
withContext(Dispatchers.Main) {
adapter.updateData(filteredList)
loader.visibility = View.INVISIBLE
binding.loader.visibility = View.INVISIBLE
}
}
}
}
private fun setupListener() {
action_back.setOnClickListener {
binding.actionBack.setOnClickListener {
onBackPressed()
}
clear_search.setOnClickListener {
binding.clearSearch.setOnClickListener {
viewModel.searchInput.value = ""
}
}

View File

@ -1,67 +1,46 @@
package com.tommasoberlose.anotherwidget.ui.activities
package com.tommasoberlose.anotherwidget.ui.activities.tabs
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.location.Address
import android.location.Geocoder
import android.os.Build
import android.os.Bundle
import com.tommasoberlose.anotherwidget.R
import android.text.Editable
import android.text.TextWatcher
import android.view.View
import android.view.Window
import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.appcompat.app.AppCompatActivity
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.bulk
import com.google.android.material.transition.MaterialFadeThrough
import com.google.android.material.transition.MaterialSharedAxis
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.components.MaterialBottomSheetDialog
import com.tommasoberlose.anotherwidget.databinding.ActivityChooseApplicationBinding
import com.tommasoberlose.anotherwidget.databinding.ActivityCustomLocationBinding
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.ui.viewmodels.ChooseApplicationViewModel
import com.tommasoberlose.anotherwidget.ui.viewmodels.CustomLocationViewModel
import kotlinx.android.synthetic.main.activity_custom_location.*
import kotlinx.android.synthetic.main.activity_custom_location.action_back
import kotlinx.android.synthetic.main.activity_custom_location.clear_search
import kotlinx.android.synthetic.main.activity_custom_location.list_view
import kotlinx.android.synthetic.main.activity_custom_location.loader
import kotlinx.android.synthetic.main.activity_music_players_filter.*
import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.CustomLocationViewModel
import kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.ThreadMode
import org.greenrobot.eventbus.Subscribe
class CustomLocationActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter
private lateinit var viewModel: CustomLocationViewModel
private lateinit var binding: ActivityCustomLocationBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(CustomLocationViewModel::class.java)
val binding = DataBindingUtil.setContentView<ActivityCustomLocationBinding>(this, R.layout.activity_custom_location)
binding = ActivityCustomLocationBinding.inflate(layoutInflater)
list_view.setHasFixedSize(true)
binding.listView.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager
binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapter.create()
adapter
@ -69,16 +48,12 @@ class CustomLocationActivity : AppCompatActivity() {
injector
.text(R.id.text, getString(R.string.custom_location_gps))
.clicked(R.id.text) {
MaterialBottomSheetDialog(this, message = getString(R.string.background_location_warning))
.setPositiveButton(getString(android.R.string.ok)) {
requirePermission()
}
.show()
requirePermission()
}
}
.register<Address>(R.layout.custom_location_item) { item, injector ->
injector.text(R.id.text, item.getAddressLine(0))
injector.clicked(R.id.text) {
injector.clicked(R.id.item) {
Preferences.bulk {
customLocationLat = item.latitude.toString()
customLocationLon = item.longitude.toString()
@ -88,7 +63,7 @@ class CustomLocationActivity : AppCompatActivity() {
}
}
}
.attachTo(list_view)
.attachTo(binding.listView)
viewModel.addresses.observe(this, Observer {
@ -98,9 +73,11 @@ class CustomLocationActivity : AppCompatActivity() {
setupListener()
subscribeUi(binding, viewModel)
location.requestFocus()
binding.location.requestFocus()
setContentView(binding.root)
}
private var searchJob: Job? = null
private fun subscribeUi(binding: ActivityCustomLocationBinding, viewModel: CustomLocationViewModel) {
@ -109,11 +86,11 @@ class CustomLocationActivity : AppCompatActivity() {
viewModel.addresses.observe(this, Observer {
adapter.updateData(listOf("Default") + it)
loader.visibility = View.INVISIBLE
binding.loader.visibility = View.INVISIBLE
})
viewModel.locationInput.observe(this, Observer { location ->
loader.visibility = View.VISIBLE
binding.loader.visibility = View.VISIBLE
searchJob?.cancel()
searchJob = lifecycleScope.launch(Dispatchers.IO) {
delay(200)
@ -129,18 +106,18 @@ class CustomLocationActivity : AppCompatActivity() {
}
withContext(Dispatchers.Main) {
viewModel.addresses.value = list
loader.visibility = View.INVISIBLE
binding.loader.visibility = View.INVISIBLE
}
}
clear_search.isVisible = location.isNotBlank()
binding.clearSearch.isVisible = location.isNotBlank()
})
}
private fun requirePermission() {
Dexter.withContext(this)
.withPermissions(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else Manifest.permission.ACCESS_FINE_LOCATION
Manifest.permission.ACCESS_FINE_LOCATION
).withListener(object: MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
report?.let {
@ -168,11 +145,11 @@ class CustomLocationActivity : AppCompatActivity() {
}
private fun setupListener() {
action_back.setOnClickListener {
binding.actionBack.setOnClickListener {
onBackPressed()
}
clear_search.setOnClickListener {
binding.clearSearch.setOnClickListener {
viewModel.locationInput.value = ""
}
}

View File

@ -0,0 +1,109 @@
package com.tommasoberlose.anotherwidget.ui.activities.tabs
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.chibatching.kotpref.blockingBulk
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.ActivityMediaInfoFormatBinding
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.MediaInfoFormatViewModel
import com.tommasoberlose.anotherwidget.utils.getCapWordString
import com.tommasoberlose.anotherwidget.utils.openURI
import com.tommasoberlose.anotherwidget.utils.toast
import kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter
import java.lang.Exception
import java.text.SimpleDateFormat
import java.util.*
class MediaInfoFormatActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter
private lateinit var viewModel: MediaInfoFormatViewModel
private lateinit var binding: ActivityMediaInfoFormatBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(MediaInfoFormatViewModel::class.java)
binding = ActivityMediaInfoFormatBinding.inflate(layoutInflater)
binding.listView.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this)
binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapter.create()
adapter
.register<String>(R.layout.custom_date_example_item) { item, injector ->
injector
.text(R.id.custom_date_example_format, item)
.text(
R.id.custom_date_example_value, MediaPlayerHelper.getMediaInfo(item, EXAMPLE_TITLE, EXAMPLE_ARTIST, EXAMPLE_ALBUM))
}
.attachTo(binding.listView)
adapter.updateData(
listOf(
MediaPlayerHelper.MEDIA_INFO_TITLE, MediaPlayerHelper.MEDIA_INFO_ARTIST, MediaPlayerHelper.MEDIA_INFO_ALBUM
)
)
setupListener()
subscribeUi(binding, viewModel)
binding.mediaInfoFormatInput.requestFocus()
setContentView(binding.root)
}
private var formatJob: Job? = null
private fun subscribeUi(binding: ActivityMediaInfoFormatBinding, viewModel: MediaInfoFormatViewModel) {
binding.viewModel = viewModel
binding.lifecycleOwner = this
viewModel.mediaInfoFormatInput.observe(this) { mediaInfoFormatInput ->
formatJob?.cancel()
formatJob = lifecycleScope.launch(Dispatchers.IO) {
withContext(Dispatchers.Main) {
binding.loader.visibility = View.VISIBLE
}
delay(200)
val text = MediaPlayerHelper.getMediaInfo(mediaInfoFormatInput, EXAMPLE_TITLE, EXAMPLE_ARTIST, EXAMPLE_ALBUM)
withContext(Dispatchers.Main) {
binding.loader.visibility = View.INVISIBLE
binding.mediaInfoFormatInputValue.text = text
}
}
}
}
private fun setupListener() {
binding.actionBack.setOnClickListener {
onBackPressed()
}
}
override fun onBackPressed() {
Preferences.blockingBulk {
mediaInfoFormat = viewModel.mediaInfoFormatInput.value ?: ""
}
super.onBackPressed()
}
companion object {
const val EXAMPLE_TITLE = "Thunderstruck"
const val EXAMPLE_ARTIST = "AC/DC"
const val EXAMPLE_ALBUM = "The Razors Edge"
}
}

View File

@ -1,50 +1,40 @@
package com.tommasoberlose.anotherwidget.ui.activities
package com.tommasoberlose.anotherwidget.ui.activities.tabs
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.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.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_music_players_filter.*
import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.MusicPlayersFilterViewModel
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
private lateinit var binding: ActivityMusicPlayersFilterBinding
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)
binding = ActivityMusicPlayersFilterBinding.inflate(layoutInflater)
list_view.setHasFixedSize(true)
binding.listView.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager
binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapter.create()
adapter
@ -69,12 +59,14 @@ class MusicPlayersFilterActivity : AppCompatActivity() {
}
.checked(R.id.checkBox, MediaPlayerHelper.isMusicPlayerAccepted(item.activityInfo.packageName))
}
.attachTo(list_view)
.attachTo(binding.listView)
setupListener()
subscribeUi(binding, viewModel)
search.requestFocus()
binding.search.requestFocus()
setContentView(binding.root)
}
private var filterJob: Job? = null
@ -83,24 +75,24 @@ class MusicPlayersFilterActivity : AppCompatActivity() {
binding.viewModel = viewModel
binding.lifecycleOwner = this
viewModel.appList.observe(this, Observer {
viewModel.appList.observe(this) {
updateList(list = it)
loader.visibility = View.INVISIBLE
})
binding.loader.visibility = View.INVISIBLE
}
viewModel.searchInput.observe(this, Observer { search ->
viewModel.searchInput.observe(this) { search ->
updateList(search = search)
clear_search.isVisible = search.isNotBlank()
})
binding.clearSearch.isVisible = search.isNotBlank()
}
viewModel.musicPlayersFilter.observe(this, {
viewModel.musicPlayersFilter.observe(this) {
updateList()
clear_selection.isVisible = Preferences.musicPlayersFilter != ""
})
binding.clearSelection.isVisible = Preferences.musicPlayersFilter != ""
}
}
private fun updateList(list: List<ResolveInfo>? = viewModel.appList.value, search: String? = viewModel.searchInput.value) {
loader.visibility = View.VISIBLE
binding.loader.visibility = View.VISIBLE
filterJob?.cancel()
filterJob = lifecycleScope.launch(Dispatchers.IO) {
if (list != null && list.isNotEmpty()) {
@ -126,22 +118,22 @@ class MusicPlayersFilterActivity : AppCompatActivity() {
withContext(Dispatchers.Main) {
adapter.updateData(filteredList)
loader.visibility = View.INVISIBLE
binding.loader.visibility = View.INVISIBLE
}
}
}
}
private fun setupListener() {
action_back.setOnClickListener {
binding.actionBack.setOnClickListener {
onBackPressed()
}
clear_search.setOnClickListener {
binding.clearSearch.setOnClickListener {
viewModel.searchInput.value = ""
}
clear_selection.setOnClickListener {
binding.clearSelection.setOnClickListener {
Preferences.musicPlayersFilter = ""
}
}

View File

@ -1,45 +1,25 @@
package com.tommasoberlose.anotherwidget.ui.activities
package com.tommasoberlose.anotherwidget.ui.activities.tabs
import android.app.Activity
import android.content.Intent
import android.content.pm.ResolveInfo
import android.os.Bundle
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 com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.WeatherProviderViewModel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.idik.lib.slimadapter.SlimAdapter
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
@ -49,16 +29,17 @@ class WeatherProviderActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter
private lateinit var viewModel: WeatherProviderViewModel
private lateinit var binding: ActivityWeatherProviderBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_weather_provider)
viewModel = ViewModelProvider(this).get(WeatherProviderViewModel::class.java)
binding = ActivityWeatherProviderBinding.inflate(layoutInflater)
list_view.setHasFixedSize(true)
binding.listView.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager
binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapter.create()
adapter
@ -74,7 +55,7 @@ class WeatherProviderActivity : AppCompatActivity() {
Preferences.weatherProvider = provider.value
updateListItem(oldValue)
updateListItem()
loader.isVisible = true
binding.loader.isVisible = true
lifecycleScope.launch {
WeatherHelper.updateWeather(this@WeatherProviderActivity)
@ -89,7 +70,7 @@ class WeatherProviderActivity : AppCompatActivity() {
Preferences.weatherProvider = provider.value
updateListItem(oldValue)
updateListItem()
loader.isVisible = true
binding.loader.isVisible = true
lifecycleScope.launch {
WeatherHelper.updateWeather(this@WeatherProviderActivity)
@ -112,7 +93,7 @@ class WeatherProviderActivity : AppCompatActivity() {
.clicked(R.id.action_configure) {
BottomSheetWeatherProviderSettings(this) {
lifecycleScope.launch {
loader.isVisible = true
binding.loader.isVisible = true
WeatherHelper.updateWeather(this@WeatherProviderActivity)
}
}.show()
@ -129,8 +110,8 @@ class WeatherProviderActivity : AppCompatActivity() {
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)
.image(R.id.action_configure, ContextCompat.getDrawable(this, if (WeatherHelper.isKeyRequired(provider)) R.drawable.round_settings_24 else R.drawable.outline_info_24))
}.attachTo(binding.listView)
adapter.updateData(
Constants.WeatherProvider.values().asList()
@ -140,6 +121,8 @@ class WeatherProviderActivity : AppCompatActivity() {
setupListener()
subscribeUi(viewModel)
setContentView(binding.root)
}
private fun subscribeUi(viewModel: WeatherProviderViewModel) {
@ -161,7 +144,7 @@ class WeatherProviderActivity : AppCompatActivity() {
}
private fun setupListener() {
action_back.setOnClickListener {
binding.actionBack.setOnClickListener {
onBackPressed()
}
}
@ -183,9 +166,9 @@ class WeatherProviderActivity : AppCompatActivity() {
@Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(ignore: MainFragment.UpdateUiMessageEvent?) {
loader.isVisible = Preferences.weatherProviderError == "-"
binding.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()
Snackbar.make(binding.listView, getString(R.string.settings_weather_provider_api_key_subtitle_all_set), Snackbar.LENGTH_LONG).show()
}
}
}

View File

@ -3,7 +3,7 @@ package com.tommasoberlose.anotherwidget.ui.adapters
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.tommasoberlose.anotherwidget.ui.fragments.*
import com.tommasoberlose.anotherwidget.ui.fragments.tabs.*
class ViewPagerAdapter(fragmentActivity: FragmentActivity) :
FragmentStateAdapter(fragmentActivity) {
@ -12,11 +12,11 @@ class ViewPagerAdapter(fragmentActivity: FragmentActivity) :
override fun createFragment(position: Int): Fragment {
return when (position) {
1 -> CalendarTabFragment.newInstance()
2 -> WeatherTabFragment.newInstance()
3 -> ClockTabFragment.newInstance()
1 -> CalendarFragment.newInstance()
2 -> WeatherFragment.newInstance()
3 -> ClockFragment.newInstance()
4 -> GlanceTabFragment.newInstance()
else -> GeneralTabFragment.newInstance()
else -> LayoutFragment.newInstance()
}
}
}

View File

@ -1,456 +0,0 @@
package com.tommasoberlose.anotherwidget.ui.fragments
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.provider.CalendarContract
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
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 com.chibatching.kotpref.bulk
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.models.CalendarSelector
import com.tommasoberlose.anotherwidget.databinding.FragmentCalendarSettingsBinding
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.global.RequestCode
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.IntentHelper
import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper
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 kotlin.Comparator
class CalendarTabFragment : Fragment() {
companion object {
fun newInstance() = CalendarTabFragment()
}
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
val binding = DataBindingUtil.inflate<FragmentCalendarSettingsBinding>(inflater, R.layout.fragment_calendar_settings, container, false)
subscribeUi(binding, viewModel)
binding.lifecycleOwner = this
binding.viewModel = viewModel
return binding.root
}
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()
}
private fun subscribeUi(
binding: FragmentCalendarSettingsBinding,
viewModel: MainViewModel
) {
binding.isCalendarEnabled = Preferences.showEvents
binding.isDiffEnabled = Preferences.showDiffTime || !Preferences.showEvents
viewModel.showEvents.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
binding.isCalendarEnabled = it
if (it) {
CalendarHelper.setEventUpdatesAndroidN(requireContext())
} 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_visible) else getString(R.string.settings_not_visible)
}
})
viewModel.secondRowInformation.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
second_row_info_label?.text = getString(SettingsStringHelper.getSecondRowInfoString(it))
}
})
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 -> ""
}
}
})
viewModel.showUntil.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
show_until_label?.text = getString(SettingsStringHelper.getShowUntilString(it))
}
updateCalendar()
})
viewModel.showNextEvent.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
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 = when {
Preferences.calendarAppName != "" -> Preferences.calendarAppName
else -> {
if (IntentHelper.getCalendarIntent(requireContext()).isDefaultSet(requireContext())) {
getString(
R.string.default_calendar_app
)
} else {
getString(R.string.nothing)
}
}
}
}
})
viewModel.openEventDetails.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
open_event_details_label?.text = if (it) getString(R.string.default_event_app) else getString(R.string.default_calendar_app)
}
})
}
private fun setupListener() {
action_show_events.setOnClickListener {
Preferences.showEvents = !Preferences.showEvents
if (Preferences.showEvents) {
requirePermission()
}
}
show_events_switch.setOnCheckedChangeListener { _, enabled: Boolean ->
Preferences.showEvents = enabled
if (Preferences.showEvents) {
requirePermission()
}
}
action_filter_calendar.setOnClickListener {
val calendarSelectorList: List<CalendarSelector> = CalendarHelper.getCalendarList(requireContext()).map {
CalendarSelector(
it.id,
it.displayName,
it.accountName
)
}.sortedWith(Comparator { cal1, cal2 ->
when {
cal1.accountName != cal2.accountName -> {
cal1.accountName.compareTo(cal2.accountName)
}
cal1.accountName == cal1.name -> {
-1
}
cal2.accountName == cal2.name -> {
1
}
else -> {
cal1.name.compareTo(cal2.name)
}
}
})
if (calendarSelectorList.isNotEmpty()) {
val filteredCalendarIds = CalendarHelper.getFilteredCalendarIdList()
val visibleCalendarIds = calendarSelectorList.map { it.id }.filter { id: Long -> !filteredCalendarIds.contains(id) }
val dialog = BottomSheetMenu<Long>(requireContext(), header = getString(R.string.settings_filter_calendar_subtitle), isMultiSelection = true)
.setSelectedValues(visibleCalendarIds)
calendarSelectorList.indices.forEach { index ->
if (index == 0 || calendarSelectorList[index].accountName != calendarSelectorList[index - 1].accountName) {
dialog.addItem(calendarSelectorList[index].accountName)
}
dialog.addItem(
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) })
updateCalendar()
}.show()
} else {
activity?.toast(getString(R.string.calendar_settings_list_error))
}
}
action_show_all_day.setOnClickListener {
if (Preferences.showEvents) {
show_all_day_toggle.isChecked = !show_all_day_toggle.isChecked
}
}
show_all_day_toggle.setOnCheckedChangeListener { _, isChecked ->
if (Preferences.showEvents) {
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) {
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) {
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.widgetUpdateFrequency = value
}.show()
}
}
action_second_row_info.setOnClickListener {
if (Preferences.showEvents) {
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_second_row_info_title)).setSelectedValue(Preferences.secondRowInformation)
(0 .. 1).forEach {
dialog.addItem(getString(SettingsStringHelper.getSecondRowInfoString(it)), it)
}
dialog.addOnSelectItemListener { value ->
Preferences.secondRowInformation = value
}.show()
}
}
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,3, 4, 5).forEach {
dialog.addItem(getString(SettingsStringHelper.getShowUntilString(it)), it)
}
dialog.addOnSelectItemListener { value ->
Preferences.showUntil = value
}.show()
}
}
action_open_event_details.setOnClickListener {
if (Preferences.showEvents) {
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_event_app_title)).setSelectedValue(Preferences.openEventDetails)
.addItem(getString(R.string.default_event_app), true)
.addItem(getString(R.string.default_calendar_app), false)
.addOnSelectItemListener { value ->
Preferences.openEventDetails = value
}.show()
}
}
action_calendar_app.setOnClickListener {
startActivityForResult(Intent(requireContext(), ChooseApplicationActivity::class.java), RequestCode.CALENDAR_APP_REQUEST_CODE.code)
}
}
private fun checkReadEventsPermission(showEvents: Boolean = Preferences.showEvents) {
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
} 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
read_calendar_permission_alert?.setOnClickListener {
requirePermission()
}
}
}
private fun updateCalendar() {
if (activity?.checkGrantedPermission(Manifest.permission.READ_CALENDAR) == true) {
CalendarHelper.updateEventList(requireContext())
}
}
private fun requirePermission() {
Dexter.withContext(requireContext())
.withPermissions(
Manifest.permission.READ_CALENDAR
).withListener(object: MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
report?.let {
if (report.areAllPermissionsGranted()){
checkReadEventsPermission()
} else {
Preferences.showEvents = false
}
}
}
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()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
RequestCode.CALENDAR_APP_REQUEST_CODE.code -> {
Preferences.bulk {
calendarAppName = data?.getStringExtra(Constants.RESULT_APP_NAME) ?: getString(R.string.default_calendar_app)
calendarAppPackage = data?.getStringExtra(Constants.RESULT_APP_PACKAGE) ?: ""
}
}
RequestCode.EVENT_APP_REQUEST_CODE.code -> {
Preferences.bulk {
eventAppName = data?.getStringExtra(Constants.RESULT_APP_NAME) ?: getString(R.string.default_event_app)
eventAppPackage = data?.getStringExtra(Constants.RESULT_APP_PACKAGE) ?: ""
}
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
private fun maintainScrollPosition(callback: () -> Unit) {
scrollView.isScrollable = false
callback.invoke()
lifecycleScope.launch {
delay(200)
scrollView.isScrollable = true
}
}
}

View File

@ -1,331 +0,0 @@
package com.tommasoberlose.anotherwidget.ui.fragments
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.os.Bundle
import android.text.format.DateFormat
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
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 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
import com.tommasoberlose.anotherwidget.global.RequestCode
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
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.lang.Exception
class ClockTabFragment : Fragment() {
companion object {
fun newInstance() = ClockTabFragment()
}
private lateinit var viewModel: MainViewModel
private lateinit var colors: IntArray
private lateinit var binding: FragmentClockSettingsBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
binding = DataBindingUtil.inflate<FragmentClockSettingsBinding>(inflater, R.layout.fragment_clock_settings, container, false)
subscribeUi(binding, viewModel)
binding.lifecycleOwner = this
binding.viewModel = viewModel
return binding.root
}
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) {
colors = lazyColors
}
}
setupListener()
}
private fun subscribeUi(
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
})
viewModel.showClock.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
show_clock_label?.text =
if (it) getString(R.string.show_clock_visible) else getString(R.string.show_clock_not_visible)
binding.isClockVisible = it
}
})
viewModel.clockTextSize.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
clock_text_size_label?.text = String.format("%.0fsp", it)
}
})
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(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()
}
}
})
viewModel.clockTextAlpha.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(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()
}
}
})
viewModel.clockBottomMargin.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
clock_bottom_margin_label?.text = when (it) {
Constants.ClockBottomMargin.NONE.value -> getString(R.string.settings_clock_bottom_margin_subtitle_none)
Constants.ClockBottomMargin.SMALL.value -> getString(R.string.settings_clock_bottom_margin_subtitle_small)
Constants.ClockBottomMargin.LARGE.value -> getString(R.string.settings_clock_bottom_margin_subtitle_large)
else -> getString(R.string.settings_clock_bottom_margin_subtitle_medium)
}
}
})
viewModel.clockAppName.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
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)
}
}
}
}
})
}
private fun setupListener() {
action_hide_large_clock_warning.setOnClickListener {
Preferences.showBigClockWarning = false
}
action_show_clock.setOnClickListener {
Preferences.showClock = !Preferences.showClock
}
show_clock_switch.setOnCheckedChangeListener { _, enabled: Boolean ->
Preferences.showClock = enabled
}
action_clock_text_size.setOnClickListener {
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
}
}
action_clock_text_color.setOnClickListener {
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 {
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) {
if (Preferences.showClock) {
startActivityForResult(
Intent(requireContext(), ChooseApplicationActivity::class.java),
RequestCode.CLOCK_APP_REQUEST_CODE.code
)
}
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK && requestCode == RequestCode.CLOCK_APP_REQUEST_CODE.code) {
Preferences.bulk {
clockAppName = data?.getStringExtra(Constants.RESULT_APP_NAME) ?: getString(R.string.default_clock_app)
clockAppPackage = data?.getStringExtra(Constants.RESULT_APP_PACKAGE) ?: ""
}
}
super.onActivityResult(requestCode, resultCode, data)
}
override fun onResume() {
binding.is24Format = DateFormat.is24HourFormat(requireContext())
super.onResume()
}
private fun maintainScrollPosition(callback: () -> Unit) {
scrollView.isScrollable = false
callback.invoke()
lifecycleScope.launch {
delay(200)
scrollView.isScrollable = true
}
}
}

View File

@ -1,535 +0,0 @@
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() {
companion object {
fun newInstance() = GeneralTabFragment()
}
private lateinit var viewModel: MainViewModel
private lateinit var colors: IntArray
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
val binding = DataBindingUtil.inflate<FragmentGeneralSettingsBinding>(inflater, R.layout.fragment_general_settings, container, false)
subscribeUi(viewModel)
binding.lifecycleOwner = this
binding.viewModel = viewModel
binding.isDarkModeEnabled = activity?.isDarkTheme() == true
return binding.root
}
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)
withContext(Dispatchers.Main) {
colors = lazyColors
}
}
}
@SuppressLint("DefaultLocale")
private fun subscribeUi(
viewModel: MainViewModel
) {
viewModel.textMainSize.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
main_text_size_label?.text = String.format("%.0fsp", it)
}
})
viewModel.textSecondSize.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
second_text_size_label?.text = String.format("%.0fsp", it)
}
})
viewModel.textGlobalColor.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
if (Preferences.textGlobalAlpha == "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.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()
}
}
})
viewModel.textGlobalAlpha.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
if (Preferences.textGlobalAlpha == "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.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)
}
}
})
viewModel.backgroundCardColor.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
if (Preferences.backgroundCardAlpha == "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.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()
}
}
})
viewModel.backgroundCardAlpha.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
if (Preferences.backgroundCardAlpha == "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.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 {
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 = 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 {
maintainScrollPosition {
show_dividers_label?.text =
if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
}
})
}
private fun setupListener() {
action_main_text_size.setOnClickListener {
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.title_main_text_size)).setSelectedValue(Preferences.textMainSize)
(40 downTo 10).filter { it % 2 == 0 }.forEach {
dialog.addItem("${it}sp", it.toFloat())
}
dialog.addOnSelectItemListener { value ->
Preferences.textMainSize = value
}.show()
}
action_second_text_size.setOnClickListener {
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.title_second_text_size)).setSelectedValue(Preferences.textSecondSize)
(40 downTo 10).filter { it % 2 == 0 }.forEach {
dialog.addItem("${it}sp", it.toFloat())
}
dialog.addOnSelectItemListener { value ->
Preferences.textSecondSize = value
}.show()
}
action_font_color.setOnClickListener {
BottomSheetColorPicker(requireContext(),
colors = colors,
header = getString(R.string.settings_font_color_title),
getSelected = { ColorHelper.getFontColorRgb(activity?.isDarkTheme() == true) },
onColorSelected = { color: Int ->
val colorString = Integer.toHexString(color)
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 = if (activity?.isDarkTheme() == true) Preferences.textGlobalAlphaDark.toIntValue() else Preferences.textGlobalAlpha.toIntValue(),
onAlphaChangeListener = { alpha ->
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(activity?.isDarkTheme() == true) },
onColorSelected = { color: Int ->
val colorString = Integer.toHexString(color)
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 = if (activity?.isDarkTheme() == true) Preferences.backgroundCardAlphaDark.toIntValue() else Preferences.backgroundCardAlpha.toIntValue(),
onAlphaChangeListener = { alpha ->
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(if (activity?.isDarkTheme() == true) Preferences.textShadowDark else Preferences.textShadow)
(2 downTo 0).forEach {
dialog.addItem(getString(SettingsStringHelper.getTextShadowString(it)), it)
}
dialog.addOnSelectItemListener { 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)
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 ->
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()
}
action_show_dividers.setOnClickListener {
show_dividers_toggle.isChecked = !show_dividers_toggle.isChecked
}
show_dividers_toggle.setOnCheckedChangeListener { _, isChecked ->
Preferences.showDividers = isChecked
}
}
private fun maintainScrollPosition(callback: () -> Unit) {
scrollView.isScrollable = false
callback.invoke()
lifecycleScope.launch {
delay(200)
scrollView.isScrollable = true
}
}
}

View File

@ -9,10 +9,12 @@ 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
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.RelativeLayout
import androidx.core.animation.addListener
import androidx.core.app.NotificationManagerCompat
@ -22,12 +24,17 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.Navigation
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.NavHostFragment
import com.google.android.material.badge.BadgeDrawable
import com.google.android.material.card.MaterialCardView
import com.google.android.material.tabs.TabLayoutMediator
import com.google.android.material.transition.MaterialSharedAxis
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.MaterialBottomSheetDialog
import com.tommasoberlose.anotherwidget.databinding.FragmentAppMainBinding
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.*
@ -38,14 +45,12 @@ 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.*
import kotlinx.android.synthetic.main.fragment_app_main.*
import kotlinx.android.synthetic.main.the_widget_sans.*
import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeListener {
class MainFragment : Fragment() {
companion object {
fun newInstance() = MainFragment()
@ -53,12 +58,13 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
}
private lateinit var viewModel: MainViewModel
private lateinit var binding: FragmentAppMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Y, true)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Y, false)
}
override fun onCreateView(
@ -66,41 +72,7 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
savedInstanceState: Bundle?
): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
return inflater.inflate(R.layout.fragment_app_main, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
// Viewpager
pager.adapter = ViewPagerAdapter(requireActivity())
pager.offscreenPageLimit = 4
TabLayoutMediator(tabs, pager) { tab, position ->
tab.text = when (position) {
0 -> getString(R.string.settings_general_title)
1 -> getString(R.string.settings_calendar_title)
2 -> getString(R.string.settings_weather_title)
3 -> getString(R.string.settings_clock_title)
4 -> getString(R.string.settings_at_a_glance_title)
else -> ""
}
}.attach()
// Init clock
if (Preferences.showClock) {
time.setTextColor(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true))
time.setTextSize(TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(requireContext()))
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
preview.layoutParams = preview.layoutParams.apply {
height = PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel(requireContext()) else 0
}
subscribeUi(viewModel)
binding = FragmentAppMainBinding.inflate(inflater)
// Warnings
if (getString(R.string.xiaomi_manufacturer).equals(Build.MANUFACTURER, ignoreCase = true) && Preferences.showXiaomiWarning) {
@ -117,6 +89,92 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
}
.show()
}
val navHost = childFragmentManager.findFragmentById(R.id.settings_fragment) as? NavHostFragment?
navHost?.navController?.addOnDestinationChangedListener { controller, destination, _ ->
val show = destination.id != R.id.tabSelectorFragment
binding.actionBack.animate().alpha(if (show) 1f else 0f).setDuration(200).translationX((if (show) 0f else 4f).convertDpToPixel(requireContext())).start()
binding.actionBack.setOnClickListener {
controller.navigateUp()
}
binding.actionSettings.animate().alpha(if (!show) 1f else 0f).setDuration(200).translationX((if (!show) 0f else -4f).convertDpToPixel(requireContext())).start()
binding.fragmentTitle.text = if (show) destination.label.toString() else getString(R.string.app_name)
}
binding.actionSettings.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_appMainFragment_to_appSettingsFragment,)
}
binding.preview.layoutParams = binding.preview.layoutParams.apply {
height = PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel(
requireContext()
) else 0
}
subscribeUi(viewModel)
return binding.root
}
private fun subscribeUi(viewModel: MainViewModel) {
viewModel.showWallpaper.observe(viewLifecycleOwner) {
if (it) {
val wallpaper = requireActivity().getCurrentWallpaper()
binding.widgetBg.setImageDrawable(if (it) wallpaper else null)
if (wallpaper != null) {
binding.widgetBg.layoutParams =
(binding.widgetBg.layoutParams as ViewGroup.MarginLayoutParams).apply {
val metrics = DisplayMetrics()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val display = requireActivity().display
display?.getRealMetrics(metrics)
} else {
@Suppress("DEPRECATION")
val display = requireActivity().windowManager.defaultDisplay
@Suppress("DEPRECATION")
display.getMetrics(metrics)
}
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(0, (-80).toPixel(requireContext()), 0, 0
)
width = dimensions.first
height = dimensions.second
}
}
} else {
binding.widgetBg.setImageDrawable(null)
}
}
viewModel.fragmentScrollY.observe(viewLifecycleOwner) {
binding.toolbar.cardElevation = if (it > 0) 24f else 0f
}
viewModel.showPreview.observe(viewLifecycleOwner) {
updatePreviewVisibility()
}
viewModel.clockPreferencesUpdate.observe(viewLifecycleOwner) {
updateClock()
}
viewModel.widgetPreferencesUpdate.observe(viewLifecycleOwner) {
onUpdateUiEvent(null)
}
viewModel.showClock.observe(viewLifecycleOwner) {
updateClockVisibility(it)
}
}
private var uiJob: Job? = null
@ -124,287 +182,166 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
private fun updateUI() {
uiJob?.cancel()
preview?.clearAnimation()
time_container?.clearAnimation()
if (Preferences.showPreview) {
preview?.setCardBackgroundColor(
ContextCompat.getColor(
lifecycleScope.launch(Dispatchers.IO) {
val bgColor: Int = ContextCompat.getColor(
requireContext(),
if (ColorHelper.getFontColor(activity?.isDarkTheme() == true)
if (ColorHelper.getFontColor(requireActivity().isDarkTheme())
.isColorDark()
) android.R.color.white else R.color.colorAccent
)
)
widget_shape_background?.setImageDrawable(
BitmapHelper.getTintedDrawable(
val wallpaperDrawable = BitmapHelper.getTintedDrawable(
requireContext(),
R.drawable.card_background,
ColorHelper.getBackgroundColor(activity?.isDarkTheme() == true)
ColorHelper.getBackgroundColor(requireActivity().isDarkTheme())
)
)
withContext(Dispatchers.Main) {
binding.preview.setCardBackgroundColor(bgColor)
binding.widgetDetail.widgetShapeBackground.setImageDrawable(wallpaperDrawable)
}
}
WidgetHelper.runWithCustomTypeface(requireContext()) { typeface ->
uiJob = lifecycleScope.launch(Dispatchers.IO) {
val generatedView = MainWidget.generateWidgetView(requireContext(), typeface)
val generatedView = MainWidget.generateWidgetView(requireContext(), typeface).root
withContext(Dispatchers.Main) {
generatedView.measure(0, 0)
preview?.measure(0, 0)
binding.preview.measure(0, 0)
}
val bitmap = if (preview != null) {
BitmapHelper.getBitmapFromView(
generatedView,
if (preview.width > 0) preview.width else generatedView.measuredWidth,
generatedView.measuredHeight
)
} else {
null
}
val bitmap = BitmapHelper.getBitmapFromView(
generatedView,
if (binding.preview.width > 0) binding.preview.width else generatedView.measuredWidth,
generatedView.measuredHeight
)
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)
}
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()
}
widget_loader?.animate()?.scaleX(0f)?.scaleY(0f)?.alpha(0f)
?.setDuration(200L)?.start()
bitmap_container?.apply {
binding.widgetDetail.bitmapContainer.apply {
setImageBitmap(bitmap)
scaleX = 0.9f
scaleY = 0.9f
}
widget?.animate()?.alpha(1f)?.start()
binding.widgetLoader.animate().scaleX(0f).scaleY(0f).alpha(0f)
.setDuration(200L).start()
binding.widget.animate().alpha(1f).start()
}
}
}
} else {
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()
private fun updateClock() {
// Clock
binding.widgetDetail.time.setTextColor(ColorHelper.getClockFontColor(requireActivity().isDarkTheme()))
binding.widgetDetail.timeAmPm.setTextColor(ColorHelper.getClockFontColor(requireActivity().isDarkTheme()))
binding.widgetDetail.time.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(requireContext())
)
binding.widgetDetail.timeAmPm.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(requireContext()) / 5 * 2
)
binding.widgetDetail.timeAmPm.isVisible = Preferences.showAMPMIndicator
// Clock bottom margin
binding.widgetDetail.clockBottomMarginNone.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.NONE.value
binding.widgetDetail.clockBottomMarginSmall.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.SMALL.value
binding.widgetDetail.clockBottomMarginMedium.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.MEDIUM.value
binding.widgetDetail.clockBottomMarginLarge.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.LARGE.value
}
private fun subscribeUi(viewModel: MainViewModel) {
viewModel.showWallpaper.observe(viewLifecycleOwner, Observer {
activity?.let { act ->
val wallpaper = act.getCurrentWallpaper()
widget_bg.setImageDrawable(if (it) wallpaper else null)
if (wallpaper != null) {
widget_bg.layoutParams =
(widget_bg.layoutParams as ViewGroup.MarginLayoutParams).apply {
private fun updateClockVisibility(showClock: Boolean) {
binding.widgetDetail.timeContainer.clearAnimation()
binding.widgetDetail.time.clearAnimation()
val metrics = DisplayMetrics()
act.windowManager.defaultDisplay.getMetrics(metrics)
updatePreviewVisibility()
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)
}
if (showClock) {
binding.widgetDetail.timeContainer.layoutParams = binding.widgetDetail.timeContainer.layoutParams.apply {
height = RelativeLayout.LayoutParams.WRAP_CONTENT
}
binding.widgetDetail.timeContainer.measure(0, 0)
}
setMargins(
if (dimensions.first >= dimensions.second) (-80).toPixel(requireContext()) else 0,
(-80).toPixel(requireContext()), 0, 0
)
width = dimensions.first
height = dimensions.second
if ((Preferences.showClock && binding.widgetDetail.time.alpha != 1f) || (!Preferences.showClock && binding.widgetDetail.time.alpha != 0f)) {
val initialHeight = binding.widgetDetail.timeContainer.measuredHeight
ValueAnimator.ofFloat(
if (showClock) 0f else 1f,
if (showClock) 1f else 0f
).apply {
duration = 300L
addUpdateListener {
val animatedValue = animatedValue as Float
binding.widgetDetail.timeContainer.layoutParams =
binding.widgetDetail.timeContainer.layoutParams.apply {
height = (initialHeight * animatedValue).toInt()
}
binding.widgetDetail.time.alpha = animatedValue
}
}
})
logo.setOnClickListener {
// startActivity(Intent(this, SupportDevActivity::class.java))
}
action_settings.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_appMainFragment_to_appSettingsFragment)
}.start()
}
}
private fun showErrorBadge() {
// Calendar error indicator
tabs?.getTabAt(1)?.orCreateBadge?.apply {
backgroundColor = ContextCompat.getColor(requireContext(), R.color.errorColorText)
badgeGravity = BadgeDrawable.TOP_END
}?.isVisible = Preferences.showEvents && activity?.checkGrantedPermission(Manifest.permission.READ_CALENDAR) != true
// Weather error indicator
tabs?.getTabAt(2)?.orCreateBadge?.apply {
backgroundColor = ContextCompat.getColor(requireContext(), R.color.errorColorText)
badgeGravity = BadgeDrawable.TOP_END
}?.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 != "-")
|| (Preferences.weatherProviderLocationError != "")
} else {
false
private fun updatePreviewVisibility() {
binding.preview.clearAnimation()
if (binding.preview.layoutParams.height != (if (Preferences.showPreview) PREVIEW_BASE_HEIGHT.toPixel(requireContext()) else 0) + (if (Preferences.showClock) 100.toPixel(
requireContext()
) else 0)) {
ValueAnimator.ofInt(
binding.preview.height,
(if (Preferences.showPreview) PREVIEW_BASE_HEIGHT.toPixel(requireContext()) else 0) + (if (Preferences.showClock) 100.toPixel(
requireContext()
) else 0)
).apply {
duration = 300L
addUpdateListener {
val animatedValue = animatedValue as Int
val layoutParams = binding.preview.layoutParams
layoutParams.height = animatedValue
binding.preview.layoutParams = layoutParams
}
}.start()
}
// Glance error indicator
tabs?.getTabAt(4)?.orCreateBadge?.apply {
backgroundColor = ContextCompat.getColor(requireContext(), R.color.errorColorText)
badgeGravity = BadgeDrawable.TOP_END
}?.isVisible = ((Preferences.showMusic || Preferences.showNotifications) && !ActiveNotificationsHelper.checkNotificationAccess(requireContext())) ||
(Preferences.showDailySteps && !(Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || requireActivity().checkGrantedPermission(Manifest.permission.ACTIVITY_RECOGNITION))) ||
(AlarmHelper.isAlarmProbablyWrong(requireContext()))
}
override fun onResume() {
super.onResume()
Preferences.preferences.registerOnSharedPreferenceChangeListener(this)
EventBus.getDefault().register(this)
showErrorBadge()
updateUI()
}
override fun onPause() {
Preferences.preferences.unregisterOnSharedPreferenceChangeListener(this)
EventBus.getDefault().unregister(this)
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
class ChangeTabEvent(val page: Int)
@Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(ignore: UpdateUiMessageEvent?) {
fun onUpdateUiEvent(ignore: UpdateUiMessageEvent?) {
delayJob?.cancel()
delayJob = lifecycleScope.launch(Dispatchers.IO) {
delay(200)
delay(300)
withContext(Dispatchers.Main) {
updateUI()
}
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onChangeTabEvent(ignore: ChangeTabEvent) {
val navHost = childFragmentManager.findFragmentById(R.id.settings_fragment) as? NavHostFragment?
navHost?.navController?.navigateUp()
}
}

View File

@ -4,7 +4,6 @@ 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
@ -15,6 +14,7 @@ import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.Navigation
import androidx.transition.TransitionInflater
import com.google.android.material.transition.MaterialSharedAxis
import com.karumi.dexter.Dexter
import com.karumi.dexter.MultiplePermissionsReport
@ -24,19 +24,19 @@ import com.karumi.dexter.listener.multi.MultiplePermissionsListener
import com.tommasoberlose.anotherwidget.BuildConfig
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
import com.tommasoberlose.anotherwidget.databinding.FragmentSettingsBinding
import com.tommasoberlose.anotherwidget.databinding.FragmentAppSettingsBinding
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.activities.SupportDevActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
import com.tommasoberlose.anotherwidget.ui.activities.IntegrationsActivity
import com.tommasoberlose.anotherwidget.ui.activities.settings.IntegrationsActivity
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.activities.settings.SupportDevActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.openURI
import kotlinx.android.synthetic.main.fragment_settings.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ -48,20 +48,22 @@ class SettingsFragment : Fragment() {
}
private lateinit var viewModel: MainViewModel
private lateinit var binding: FragmentAppSettingsBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
// sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Y, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Y, false)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
savedInstanceState: Bundle?,
): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
val binding = DataBindingUtil.inflate<FragmentSettingsBinding>(inflater, R.layout.fragment_settings, container, false)
binding = FragmentAppSettingsBinding.inflate(inflater)
binding.lifecycleOwner = this
binding.viewModel = viewModel
@ -74,25 +76,29 @@ class SettingsFragment : Fragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
action_back.setOnClickListener {
binding.actionBack.setOnClickListener {
Navigation.findNavController(it).popBackStack()
}
show_widget_preview_toggle.isChecked = Preferences.showPreview
show_wallpaper_toggle.isChecked = Preferences.showWallpaper
binding.showWidgetPreviewToggle.setCheckedImmediatelyNoEvent(Preferences.showPreview)
binding.showWallpaperToggle.setCheckedImmediatelyNoEvent(Preferences.showWallpaper)
setupListener()
app_version.text = "v%s (%s)".format(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)
binding.appVersion.text = "v%s (%s)".format(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)
binding.scrollView.viewTreeObserver.addOnScrollChangedListener {
binding.toolbar.cardElevation = if (binding.scrollView.scrollY > 0) 32f else 0f
}
}
private fun subscribeUi(
viewModel: MainViewModel
viewModel: MainViewModel,
) {
viewModel.darkThemePreference.observe(viewLifecycleOwner, Observer {
viewModel.darkThemePreference.observe(viewLifecycleOwner) {
AppCompatDelegate.setDefaultNightMode(it)
maintainScrollPosition {
theme?.text = when (it) {
binding.theme.text = when (it) {
AppCompatDelegate.MODE_NIGHT_NO -> getString(R.string.settings_subtitle_dark_theme_light)
AppCompatDelegate.MODE_NIGHT_YES -> getString(R.string.settings_subtitle_dark_theme_dark)
AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY -> getString(R.string.settings_subtitle_dark_theme_by_battery_saver)
@ -100,43 +106,45 @@ class SettingsFragment : Fragment() {
else -> ""
}
}
})
}
viewModel.installedIntegrations.observe(viewLifecycleOwner, Observer {
integrations_count_label?.text = getString(R.string.label_count_installed_integrations).format(it)
})
viewModel.installedIntegrations.observe(viewLifecycleOwner) {
binding.integrationsCountLabel.text =
getString(R.string.label_count_installed_integrations).format(
it)
}
viewModel.showPreview.observe(viewLifecycleOwner, Observer {
viewModel.showPreview.observe(viewLifecycleOwner) {
maintainScrollPosition {
show_widget_preview_label?.text =
binding.showWidgetPreviewLabel.text =
if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
}
})
}
viewModel.showWallpaper.observe(viewLifecycleOwner, Observer {
viewModel.showWallpaper.observe(viewLifecycleOwner) {
maintainScrollPosition {
show_wallpaper_label?.text =
if (it && activity?.checkGrantedPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == true) getString(
binding.showWallpaperLabel.text =
if (it && requireActivity().checkGrantedPermission(Manifest.permission.READ_EXTERNAL_STORAGE)) getString(
R.string.settings_visible
) else getString(R.string.settings_not_visible)
}
})
}
}
private fun setupListener() {
action_show_widget_preview.setOnClickListener {
show_widget_preview_toggle.isChecked = !show_widget_preview_toggle.isChecked
binding.actionShowWidgetPreview.setOnClickListener {
binding.showWidgetPreviewToggle.isChecked = !binding.showWidgetPreviewToggle.isChecked
}
show_widget_preview_toggle.setOnCheckedChangeListener { _, isChecked ->
binding.showWidgetPreviewToggle.setOnCheckedChangeListener { _, isChecked ->
Preferences.showPreview = isChecked
}
action_show_wallpaper.setOnClickListener {
show_wallpaper_toggle.isChecked = !show_wallpaper_toggle.isChecked
binding.actionShowWallpaper.setOnClickListener {
binding.showWallpaperToggle.isChecked = !binding.showWallpaperToggle.isChecked
}
show_wallpaper_toggle.setOnCheckedChangeListener { _, isChecked ->
binding.showWallpaperToggle.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
requirePermission()
} else {
@ -144,13 +152,14 @@ class SettingsFragment : Fragment() {
}
}
action_integrations.setOnClickListener {
binding.actionIntegrations.setOnClickListener {
startActivity(Intent(requireContext(), IntegrationsActivity::class.java))
}
action_change_theme.setOnClickListener {
binding.actionChangeTheme.setOnClickListener {
maintainScrollPosition {
BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_theme_title))
BottomSheetMenu<Int>(requireContext(),
header = getString(R.string.settings_theme_title))
.setSelectedValue(Preferences.darkThemePreference)
.addItem(
getString(R.string.settings_subtitle_dark_theme_light),
@ -170,62 +179,74 @@ class SettingsFragment : Fragment() {
}
}
action_translate.setOnClickListener {
activity?.openURI("https://github.com/tommasoberlose/another-widget/blob/master/app/src/main/res/values/strings.xml")
binding.actionTranslate.setOnClickListener {
requireActivity().openURI("https://github.com/tommasoberlose/another-widget/blob/master/app/src/main/res/values/strings.xml")
}
action_website.setOnClickListener {
activity?.openURI("http://tommasoberlose.com/")
binding.actionWebsite.setOnClickListener {
requireActivity().openURI("http://tommasoberlose.com/")
}
action_feedback.setOnClickListener {
activity?.openURI("https://github.com/tommasoberlose/another-widget/issues")
binding.actionFeedback.setOnClickListener {
requireActivity().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")
binding.actionPrivacyPolicy.setOnClickListener {
requireActivity().openURI("https://github.com/tommasoberlose/another-widget/blob/master/privacy-policy.md")
}
action_help_dev.setOnClickListener {
binding.actionHelpDev.setOnClickListener {
startActivity(Intent(requireContext(), SupportDevActivity::class.java))
}
action_refresh_widget.setOnClickListener {
viewLifecycleOwner.lifecycleScope.launch {
WeatherHelper.updateWeather(requireContext())
}
CalendarHelper.updateEventList(requireContext())
MediaPlayerHelper.updatePlayingMediaInfo(requireContext())
ActiveNotificationsHelper.clearLastNotification(requireContext())
binding.actionRefreshWidget.setOnClickListener {
binding.actionRefreshIcon
.animate()
.rotation((binding.actionRefreshIcon.rotation - binding.actionRefreshIcon.rotation % 360f) + 360f)
.withEndAction {
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
WeatherHelper.updateWeather(requireContext())
CalendarHelper.updateEventList(requireContext())
MediaPlayerHelper.updatePlayingMediaInfo(requireContext())
ActiveNotificationsHelper.clearLastNotification(requireContext())
}
}
.start()
}
}
private fun maintainScrollPosition(callback: () -> Unit) {
scrollView.isScrollable = false
binding.scrollView.isScrollable = false
callback.invoke()
lifecycleScope.launch {
delay(200)
scrollView.isScrollable = true
binding.scrollView.isScrollable = true
}
}
override fun onResume() {
super.onResume()
binding.showWallpaperToggle.setCheckedNoEvent(Preferences.showWallpaper && requireActivity().checkGrantedPermission(Manifest.permission.READ_EXTERNAL_STORAGE))
}
private fun requirePermission() {
Dexter.withContext(requireContext())
.withPermissions(
Manifest.permission.READ_EXTERNAL_STORAGE
).withListener(object: MultiplePermissionsListener {
).withListener(object : MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
report?.let {
if (report.areAllPermissionsGranted()) {
Preferences.showWallpaper = true
} else {
show_wallpaper_toggle?.isChecked = false
binding.showWallpaperToggle.setCheckedNoEvent(false)
}
}
}
override fun onPermissionRationaleShouldBeShown(
permissions: MutableList<PermissionRequest>?,
token: PermissionToken?
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.

View File

@ -1,331 +0,0 @@
package com.tommasoberlose.anotherwidget.ui.fragments
import android.Manifest
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
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.chibatching.kotpref.bulk
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.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
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.activities.WeatherProviderActivity
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.collapse
import com.tommasoberlose.anotherwidget.utils.expand
import kotlinx.android.synthetic.main.fragment_weather_settings.*
import kotlinx.android.synthetic.main.fragment_weather_settings.scrollView
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class WeatherTabFragment : Fragment() {
companion object {
fun newInstance() = WeatherTabFragment()
}
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
val binding = DataBindingUtil.inflate<FragmentWeatherSettingsBinding>(inflater, R.layout.fragment_weather_settings, container, false)
subscribeUi(binding, viewModel)
binding.lifecycleOwner = this
binding.viewModel = viewModel
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setupListener()
}
private fun subscribeUi(
binding: FragmentWeatherSettingsBinding,
viewModel: MainViewModel
) {
binding.isWeatherVisible = Preferences.showWeather
viewModel.showWeather.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
show_weather_label?.text =
if (it) getString(R.string.show_weather_visible) else getString(R.string.show_weather_not_visible)
checkWeatherProviderConfig()
binding.isWeatherVisible = it
}
checkLocationPermission()
checkWeatherProviderConfig()
})
viewModel.weatherProvider.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
label_weather_provider.text = WeatherHelper.getProviderName(requireContext(), Constants.WeatherProvider.fromInt(it)!!)
checkWeatherProviderConfig()
}
})
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
}
checkLocationPermission()
})
viewModel.weatherTempUnit.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
temp_unit?.text =
if (it == "F") getString(R.string.fahrenheit) else getString(R.string.celsius)
}
checkLocationPermission()
})
viewModel.weatherRefreshPeriod.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
label_weather_refresh_period?.text = getString(SettingsStringHelper.getRefreshPeriodString(it))
}
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 =
if (it != "") it else getString(R.string.default_weather_app)
}
})
}
private fun checkLocationPermission() {
// Background permission
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && activity?.checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION) == true && activity?.checkGrantedPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) != true) {
requirePermission()
}
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 = true
location_permission_alert?.setOnClickListener {
MaterialBottomSheetDialog(requireContext(), message = getString(R.string.background_location_warning))
.setPositiveButton(getString(android.R.string.ok)) {
requirePermission()
}
.show()
}
} else {
location_permission_alert?.isVisible = false
}
}
private fun checkWeatherProviderConfig() {
if (Preferences.showWeather && Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-" && !location_permission_alert.isVisible) {
weather_provider_error.expand()
} else {
weather_provider_error.collapse()
}
weather_provider_error?.text = Preferences.weatherProviderError
if (Preferences.showWeather && Preferences.weatherProviderLocationError != "" && !location_permission_alert.isVisible) {
weather_provider_location_error.expand()
} else {
weather_provider_location_error.collapse()
}
weather_provider_location_error?.text = Preferences.weatherProviderLocationError
}
private fun setupListener() {
action_show_weather.setOnClickListener {
Preferences.showWeather = !Preferences.showWeather
}
show_weather_switch.setOnCheckedChangeListener { _, enabled: Boolean ->
Preferences.showWeather = enabled
}
action_weather_provider.setOnClickListener {
if (Preferences.showWeather) {
startActivityForResult(
Intent(requireContext(), WeatherProviderActivity::class.java),
RequestCode.WEATHER_PROVIDER_REQUEST_CODE.code
)
}
}
action_custom_location.setOnClickListener {
if (Preferences.showWeather) {
startActivityForResult(
Intent(requireContext(), CustomLocationActivity::class.java),
Constants.RESULT_CODE_CUSTOM_LOCATION
)
}
}
action_change_unit.setOnClickListener {
if (Preferences.showWeather) {
BottomSheetMenu<String>(requireContext(), header = getString(R.string.settings_unit_title)).setSelectedValue(Preferences.weatherTempUnit)
.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()
}
}
action_weather_refresh_period.setOnClickListener {
if (Preferences.showWeather) {
val dialog =
BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_weather_refresh_period_title)).setSelectedValue(Preferences.weatherRefreshPeriod)
(5 downTo 0).forEach {
dialog.addItem(getString(SettingsStringHelper.getRefreshPeriodString(it)), it)
}
dialog
.addOnSelectItemListener { value ->
Preferences.weatherRefreshPeriod = value
}.show()
}
}
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(
Intent(requireContext(), ChooseApplicationActivity::class.java),
RequestCode.WEATHER_APP_REQUEST_CODE.code
)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
Constants.RESULT_CODE_CUSTOM_LOCATION -> {
WeatherReceiver.setUpdates(requireContext())
checkLocationPermission()
}
RequestCode.WEATHER_APP_REQUEST_CODE.code -> {
Preferences.bulk {
weatherAppName = data?.getStringExtra(Constants.RESULT_APP_NAME) ?: getString(R.string.default_weather_app)
weatherAppPackage = data?.getStringExtra(Constants.RESULT_APP_PACKAGE) ?: ""
}
MainWidget.updateWidget(requireContext())
}
RequestCode.WEATHER_PROVIDER_REQUEST_CODE.code -> {
checkLocationPermission()
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
private fun requirePermission() {
Dexter.withContext(requireContext())
.withPermissions(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else Manifest.permission.ACCESS_FINE_LOCATION
).withListener(object: MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
report?.let {
if (report.areAllPermissionsGranted()){
checkLocationPermission()
}
}
}
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) {
scrollView.isScrollable = false
callback.invoke()
lifecycleScope.launch {
delay(200)
scrollView.isScrollable = true
}
}
}

View File

@ -0,0 +1,292 @@
package com.tommasoberlose.anotherwidget.ui.fragments.tabs
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.provider.CalendarContract
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
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.bulk
import com.google.android.material.transition.MaterialSharedAxis
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
import com.tommasoberlose.anotherwidget.models.CalendarSelector
import com.tommasoberlose.anotherwidget.databinding.FragmentTabCalendarBinding
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.global.RequestCode
import com.tommasoberlose.anotherwidget.ui.activities.tabs.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.IntentHelper
import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.isDefaultSet
import com.tommasoberlose.anotherwidget.utils.toast
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.Comparator
class CalendarFragment : Fragment() {
companion object {
fun newInstance() = CalendarFragment()
}
private lateinit var viewModel: MainViewModel
private lateinit var binding: FragmentTabCalendarBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
binding = FragmentTabCalendarBinding.inflate(inflater)
subscribeUi(viewModel)
binding.lifecycleOwner = this
binding.viewModel = viewModel
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
binding.showAllDayToggle.setCheckedImmediatelyNoEvent(Preferences.calendarAllDay)
binding.showOnlyBusyEventsToggle.setCheckedImmediatelyNoEvent(Preferences.showOnlyBusyEvents)
binding.showDiffTimeToggle.setCheckedImmediatelyNoEvent(Preferences.showDiffTime)
setupListener()
binding.scrollView.viewTreeObserver.addOnScrollChangedListener {
viewModel.fragmentScrollY.value = binding.scrollView.scrollY
}
}
private fun subscribeUi(
viewModel: MainViewModel
) {
binding.isCalendarEnabled = Preferences.showEvents
binding.isDiffEnabled = Preferences.showDiffTime
viewModel.calendarAllDay.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.allDayLabel.text =
if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
}
}
viewModel.secondRowInformation.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.secondRowInfoLabel.text = getString(SettingsStringHelper.getSecondRowInfoString(it))
}
}
viewModel.showDiffTime.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.showDiffTimeLabel.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
binding.isDiffEnabled = it || !Preferences.showEvents
}
}
viewModel.widgetUpdateFrequency.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.widgetUpdateFrequencyLabel.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 -> ""
}
}
}
viewModel.showUntil.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.showUntilLabel.text = getString(SettingsStringHelper.getShowUntilString(it))
}
}
}
private fun setupListener() {
binding.actionFilterCalendar.setOnClickListener {
val calendarSelectorList: List<CalendarSelector> = CalendarHelper.getCalendarList(requireContext()).map {
CalendarSelector(
it.id,
it.displayName,
it.accountName
)
}.sortedWith { cal1, cal2 ->
when {
cal1.accountName != cal2.accountName -> {
cal1.accountName.compareTo(cal2.accountName)
}
cal1.accountName == cal1.name -> {
-1
}
cal2.accountName == cal2.name -> {
1
}
else -> {
cal1.name.compareTo(cal2.name)
}
}
}
if (calendarSelectorList.isNotEmpty()) {
val filteredCalendarIds = CalendarHelper.getFilteredCalendarIdList()
val visibleCalendarIds = calendarSelectorList.map { it.id }.filter { id: Long -> !filteredCalendarIds.contains(id) }
val dialog = BottomSheetMenu<Long>(requireContext(), header = getString(R.string.settings_filter_calendar_subtitle), isMultiSelection = true)
.setSelectedValues(visibleCalendarIds)
calendarSelectorList.indices.forEach { index ->
if (index == 0 || calendarSelectorList[index].accountName != calendarSelectorList[index - 1].accountName) {
dialog.addItem(calendarSelectorList[index].accountName)
}
dialog.addItem(
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) })
updateCalendar()
}.show()
} else {
requireActivity().toast(getString(R.string.calendar_settings_list_error))
}
}
binding.actionShowAllDay.setOnClickListener {
binding.showAllDayToggle.isChecked = !binding.showAllDayToggle.isChecked
}
binding.showAllDayToggle.setOnCheckedChangeListener { _, isChecked ->
Preferences.calendarAllDay = isChecked
MainWidget.updateWidget(requireContext())
}
binding.actionChangeAttendeeFilter.setOnClickListener {
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()
}
binding.actionShowOnlyBusyEvents.setOnClickListener {
binding.showOnlyBusyEventsToggle.isChecked = !binding.showOnlyBusyEventsToggle.isChecked
}
binding.showOnlyBusyEventsToggle.setOnCheckedChangeListener { _, isChecked ->
Preferences.showOnlyBusyEvents = isChecked
MainWidget.updateWidget(requireContext())
}
binding.actionShowDiffTime.setOnClickListener {
binding.showDiffTimeToggle.isChecked = !binding.showDiffTimeToggle.isChecked
}
binding.showDiffTimeToggle.setOnCheckedChangeListener { _, isChecked ->
Preferences.showDiffTime = isChecked
}
binding.actionWidgetUpdateFrequency.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.widgetUpdateFrequency = value
}.show()
}
}
binding.actionSecondRowInfo.setOnClickListener {
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_second_row_info_title)).setSelectedValue(Preferences.secondRowInformation)
(0 .. 1).forEach {
dialog.addItem(getString(SettingsStringHelper.getSecondRowInfoString(it)), it)
}
dialog.addOnSelectItemListener { value ->
Preferences.secondRowInformation = value
}.show()
}
binding.actionShowUntil.setOnClickListener {
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_show_until_title)).setSelectedValue(Preferences.showUntil)
intArrayOf(6,7,0,1,2,3, 4, 5).forEach {
dialog.addItem(getString(SettingsStringHelper.getShowUntilString(it)), it)
}
dialog.addOnSelectItemListener { value ->
Preferences.showUntil = value
updateCalendar()
}.show()
}
}
private fun updateCalendar() {
if (requireActivity().checkGrantedPermission(Manifest.permission.READ_CALENDAR)) {
CalendarHelper.updateEventList(requireContext())
}
}
private fun maintainScrollPosition(callback: () -> Unit) {
binding.scrollView.isScrollable = false
callback.invoke()
lifecycleScope.launch {
delay(200)
binding.scrollView.isScrollable = true
}
}
}

View File

@ -0,0 +1,196 @@
package com.tommasoberlose.anotherwidget.ui.fragments.tabs
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.text.format.DateFormat
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
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 com.chibatching.kotpref.bulk
import com.google.android.material.transition.MaterialSharedAxis
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.BottomSheetColorPicker
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
import com.tommasoberlose.anotherwidget.databinding.FragmentTabClockBinding
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.IntentHelper
import com.tommasoberlose.anotherwidget.ui.activities.tabs.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.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class ClockFragment : Fragment() {
companion object {
fun newInstance() = ClockFragment()
}
private lateinit var viewModel: MainViewModel
private lateinit var colors: IntArray
private lateinit var binding: FragmentTabClockBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
binding = FragmentTabClockBinding.inflate(inflater)
subscribeUi(viewModel)
binding.lifecycleOwner = this
binding.viewModel = viewModel
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
binding.ampmIndicatorToggle.setCheckedImmediatelyNoEvent(Preferences.showAMPMIndicator)
lifecycleScope.launch(Dispatchers.IO) {
val lazyColors = requireContext().resources.getIntArray(R.array.material_colors)
withContext(Dispatchers.Main) {
colors = lazyColors
}
}
setupListener()
binding.scrollView.viewTreeObserver?.addOnScrollChangedListener {
viewModel.fragmentScrollY.value = binding.scrollView.scrollY
}
}
private fun subscribeUi(
viewModel: MainViewModel
) {
binding.isClockVisible = Preferences.showClock
binding.is24Format = DateFormat.is24HourFormat(requireContext())
binding.isDarkModeEnabled = activity?.isDarkTheme() == true
viewModel.clockTextSize.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.clockTextSizeLabel.text = String.format("%.0fsp", it)
}
}
viewModel.showAMPMIndicator.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.ampmIndicatorLabel.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
}
}
viewModel.clockTextColor.observe(viewLifecycleOwner) {
maintainScrollPosition {
if (Preferences.clockTextAlpha == "00") {
binding.clockTextColorLabel.text = getString(R.string.transparent)
} else {
binding.clockTextColorLabel.text =
"#%s".format(Integer.toHexString(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true))).toUpperCase()
}
}
}
viewModel.clockTextColorDark.observe(viewLifecycleOwner) {
maintainScrollPosition {
if (Preferences.clockTextAlphaDark == "00") {
binding.clockTextColorLabel.text = getString(R.string.transparent)
} else {
binding.clockTextColorLabel.text =
"#%s".format(Integer.toHexString(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true))).toUpperCase()
}
}
}
}
private fun setupListener() {
binding.actionClockTextSize.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())
}
dialog.addOnSelectItemListener { value ->
Preferences.clockTextSize = value
}.show()
}
binding.actionAmpmIndicatorSize.setOnClickListener {
binding.ampmIndicatorToggle.isChecked = !binding.ampmIndicatorToggle.isChecked
}
binding.ampmIndicatorToggle.setOnCheckedChangeListener { _, isChecked ->
Preferences.showAMPMIndicator = isChecked
}
binding.actionClockTextColor.setOnClickListener {
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()
}
}
override fun onResume() {
binding.is24Format = DateFormat.is24HourFormat(requireContext())
super.onResume()
}
private fun maintainScrollPosition(callback: () -> Unit) {
binding.scrollView.isScrollable = false
callback.invoke()
lifecycleScope.launch {
delay(200)
binding.scrollView.isScrollable = true
}
}
}

View File

@ -0,0 +1,241 @@
package com.tommasoberlose.anotherwidget.ui.fragments.tabs
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
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.google.android.material.transition.MaterialSharedAxis
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.BottomSheetColorPicker
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
import com.tommasoberlose.anotherwidget.databinding.FragmentTabGesturesBinding
import com.tommasoberlose.anotherwidget.databinding.FragmentTabLayoutBinding
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.IntentHelper
import com.tommasoberlose.anotherwidget.ui.activities.tabs.CustomDateActivity
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.activities.tabs.ChooseApplicationActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
import com.tommasoberlose.anotherwidget.utils.isDefaultSet
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
class GesturesFragment : Fragment() {
companion object {
fun newInstance() = GesturesFragment()
}
private lateinit var viewModel: MainViewModel
private lateinit var binding: FragmentTabGesturesBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
binding = FragmentTabGesturesBinding.inflate(inflater)
subscribeUi(viewModel)
binding.lifecycleOwner = this
binding.viewModel = viewModel
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
binding.showMultipleEventsToggle.setCheckedImmediatelyNoEvent(Preferences.showNextEvent)
setupListener()
binding.scrollView.viewTreeObserver?.addOnScrollChangedListener {
viewModel.fragmentScrollY.value = binding.scrollView.scrollY
}
}
@SuppressLint("DefaultLocale")
private fun subscribeUi(
viewModel: MainViewModel
) {
viewModel.showNextEvent.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.showMultipleEventsLabel.text =
if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
}
}
viewModel.calendarAppName.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.calendarAppLabel.text = when {
it == IntentHelper.DO_NOTHING_OPTION -> getString(R.string.gestures_do_nothing)
it == IntentHelper.REFRESH_WIDGET_OPTION -> "None, the widget will be refreshed"
it != IntentHelper.DEFAULT_OPTION -> it
else -> {
if (IntentHelper.getCalendarIntent(requireContext()).isDefaultSet(requireContext())) {
getString(
R.string.default_calendar_app
)
} else {
getString(R.string.gestures_do_nothing)
}
}
}
}
}
viewModel.openEventDetails.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.openEventDetailsLabel.text = if (it) getString(R.string.default_event_app) else getString(R.string.default_calendar_app)
}
}
viewModel.clockAppName.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.clockAppLabel.text = when {
it == IntentHelper.DO_NOTHING_OPTION -> getString(R.string.gestures_do_nothing)
it == IntentHelper.REFRESH_WIDGET_OPTION -> "None, the widget will be refreshed"
it != IntentHelper.DEFAULT_OPTION -> it
else -> {
if (IntentHelper.getClockIntent(requireContext()).isDefaultSet(requireContext())) {
getString(
R.string.default_clock_app
)
} else {
getString(R.string.gestures_do_nothing)
}
}
}
}
}
viewModel.weatherAppName.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.weatherAppLabel.text = when {
it == IntentHelper.DO_NOTHING_OPTION -> getString(R.string.gestures_do_nothing)
it == IntentHelper.REFRESH_WIDGET_OPTION -> "None, the widget will be refreshed"
it != IntentHelper.DEFAULT_OPTION -> it
else -> getString(R.string.default_weather_app)
}
}
}
}
private fun setupListener() {
binding.actionShowMultipleEvents.setOnClickListener {
binding.showMultipleEventsToggle.isChecked = !binding.showMultipleEventsToggle.isChecked
}
binding.showMultipleEventsToggle.setOnCheckedChangeListener { _, isChecked ->
Preferences.showNextEvent = isChecked
}
binding.actionOpenEventDetails.setOnClickListener {
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_event_app_title)).setSelectedValue(Preferences.openEventDetails)
.addItem(getString(R.string.default_event_app), true)
.addItem(getString(R.string.default_calendar_app), false)
.addOnSelectItemListener { value ->
Preferences.openEventDetails = value
}
.show()
}
binding.actionCalendarApp.setOnClickListener {
startActivityForResult(Intent(requireContext(), ChooseApplicationActivity::class.java).apply {
putExtra(Constants.RESULT_APP_PACKAGE, Preferences.calendarAppPackage)
}, RequestCode.CALENDAR_APP_REQUEST_CODE.code)
}
binding.actionClockApp.setOnClickListener {
startActivityForResult(
Intent(requireContext(), ChooseApplicationActivity::class.java).apply {
putExtra(Constants.RESULT_APP_PACKAGE, Preferences.clockAppPackage)
},
RequestCode.CLOCK_APP_REQUEST_CODE.code
)
}
binding.actionWeatherApp.setOnClickListener {
startActivityForResult(
Intent(requireContext(), ChooseApplicationActivity::class.java).apply {
putExtra(Constants.RESULT_APP_PACKAGE, Preferences.weatherAppPackage)
},
RequestCode.WEATHER_APP_REQUEST_CODE.code
)
}
}
private fun maintainScrollPosition(callback: () -> Unit) {
binding.scrollView.isScrollable = false
callback.invoke()
lifecycleScope.launch {
delay(200)
binding.scrollView.isScrollable = true
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK && data != null && data.hasExtra(Constants.RESULT_APP_NAME) && data.hasExtra(Constants.RESULT_APP_PACKAGE)) {
when (requestCode) {
RequestCode.CALENDAR_APP_REQUEST_CODE.code -> {
Preferences.bulk {
calendarAppName = data.getStringExtra(Constants.RESULT_APP_NAME) ?: IntentHelper.DEFAULT_OPTION
calendarAppPackage = data.getStringExtra(Constants.RESULT_APP_PACKAGE) ?: IntentHelper.DEFAULT_OPTION
}
}
RequestCode.EVENT_APP_REQUEST_CODE.code -> {
Preferences.bulk {
eventAppName = data.getStringExtra(Constants.RESULT_APP_NAME) ?: IntentHelper.DEFAULT_OPTION
eventAppPackage = data.getStringExtra(Constants.RESULT_APP_PACKAGE) ?: IntentHelper.DEFAULT_OPTION
}
}
RequestCode.WEATHER_APP_REQUEST_CODE.code -> {
Preferences.bulk {
weatherAppName = data.getStringExtra(Constants.RESULT_APP_NAME) ?: IntentHelper.DEFAULT_OPTION
weatherAppPackage = data.getStringExtra(Constants.RESULT_APP_PACKAGE) ?: IntentHelper.DEFAULT_OPTION
}
}
RequestCode.CLOCK_APP_REQUEST_CODE.code -> {
Preferences.bulk {
clockAppName = data.getStringExtra(Constants.RESULT_APP_NAME) ?: IntentHelper.DEFAULT_OPTION
clockAppPackage = data.getStringExtra(Constants.RESULT_APP_PACKAGE) ?: IntentHelper.DEFAULT_OPTION
}
}
}
MainWidget.updateWidget(requireContext())
}
super.onActivityResult(requestCode, resultCode, data)
}
}

View File

@ -1,4 +1,4 @@
package com.tommasoberlose.anotherwidget.ui.fragments
package com.tommasoberlose.anotherwidget.ui.fragments.tabs
import android.Manifest
import android.app.Activity
@ -10,17 +10,16 @@ import android.content.IntentFilter
import android.graphics.Canvas
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 android.view.animation.AnimationUtils
import android.view.animation.LayoutAnimationController
import android.widget.ImageView
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.databinding.DataBindingUtil
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ItemTouchHelper
@ -30,10 +29,11 @@ import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.common.api.ApiException
import com.google.android.material.card.MaterialCardView
import com.google.android.material.transition.MaterialSharedAxis
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.CustomNotesDialog
import com.tommasoberlose.anotherwidget.components.GlanceSettingsDialog
import com.tommasoberlose.anotherwidget.databinding.FragmentGlanceSettingsBinding
import com.tommasoberlose.anotherwidget.databinding.FragmentTabGlanceBinding
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper
@ -45,13 +45,12 @@ import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver
import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver.Companion.FITNESS_OPTIONS
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.convertDpToPixel
import kotlinx.android.synthetic.main.fragment_glance_settings.*
import com.tommasoberlose.anotherwidget.utils.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.idik.lib.slimadapter.SlimAdapter
import java.util.*
class GlanceTabFragment : Fragment() {
@ -63,9 +62,15 @@ class GlanceTabFragment : Fragment() {
private var dialog: GlanceSettingsDialog? = null
private lateinit var adapter: SlimAdapter
private lateinit var viewModel: MainViewModel
private val list: ArrayList<Constants.GlanceProviderId> by lazy {
GlanceProviderHelper.getGlanceProviders(requireContext())
}
private lateinit var binding: FragmentTabGlanceBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
}
override fun onCreateView(
@ -74,12 +79,7 @@ class GlanceTabFragment : Fragment() {
): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
val binding = DataBindingUtil.inflate<FragmentGlanceSettingsBinding>(inflater,
R.layout.fragment_glance_settings,
container,
false)
subscribeUi(binding, viewModel)
binding = FragmentTabGlanceBinding.inflate(inflater)
binding.lifecycleOwner = this
binding.viewModel = viewModel
@ -91,9 +91,10 @@ class GlanceTabFragment : Fragment() {
super.onActivityCreated(savedInstanceState)
// List
providers_list.setHasFixedSize(true)
binding.providersList.hasFixedSize()
binding.providersList.isNestedScrollingEnabled = false
val mLayoutManager = LinearLayoutManager(context)
providers_list.layoutManager = mLayoutManager
binding.providersList.layoutManager = mLayoutManager
adapter = SlimAdapter.create()
adapter
@ -105,20 +106,21 @@ class GlanceTabFragment : Fragment() {
it.setImageDrawable(ContextCompat.getDrawable(requireContext(), item.icon))
}
.clicked(R.id.item) {
if (Preferences.showGlance) {
if (provider == Constants.GlanceProviderId.CUSTOM_INFO) {
CustomNotesDialog(requireContext()).show()
} else {
dialog = GlanceSettingsDialog(requireActivity(), provider) {
adapter.notifyItemRangeChanged(0, adapter.data.size)
}
dialog?.setOnDismissListener {
dialog = null
}
dialog?.show()
if (provider == Constants.GlanceProviderId.CUSTOM_INFO) {
CustomNotesDialog(requireContext()){
adapter.notifyItemRangeChanged(0, adapter.data.size)
}.show()
} else {
dialog = GlanceSettingsDialog(requireActivity(), provider) {
adapter.notifyItemRangeChanged(0, adapter.data.size)
}
dialog?.setOnDismissListener {
dialog = null
}
dialog?.show()
}
}
var isVisible = false
when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> {
when {
@ -126,104 +128,178 @@ class GlanceTabFragment : Fragment() {
MediaPlayerHelper.updatePlayingMediaInfo(requireContext())
injector.visibility(R.id.error_icon, View.GONE)
injector.visibility(R.id.info_icon, View.VISIBLE)
injector.text(R.id.label,
injector.text(
R.id.label,
if (Preferences.showMusic) getString(R.string.settings_visible) else getString(
R.string.settings_not_visible))
R.string.settings_not_visible
)
)
isVisible = Preferences.showMusic
}
Preferences.showMusic -> {
injector.visibility(R.id.error_icon, View.VISIBLE)
injector.visibility(R.id.info_icon, View.GONE)
injector.text(R.id.label, getString(R.string.settings_not_visible))
isVisible = false
}
else -> {
injector.visibility(R.id.error_icon, View.GONE)
injector.visibility(R.id.info_icon, View.VISIBLE)
injector.text(R.id.label, getString(R.string.settings_not_visible))
isVisible = false
}
}
}
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
injector.text(R.id.label,
injector.text(
R.id.label,
if (Preferences.showNextAlarm && !AlarmHelper.isAlarmProbablyWrong(
requireContext())
requireContext()
)
) getString(R.string.settings_visible) else getString(
R.string.settings_not_visible))
injector.visibility(R.id.error_icon,
R.string.settings_not_visible
)
)
injector.visibility(
R.id.error_icon,
if (Preferences.showNextAlarm && AlarmHelper.isAlarmProbablyWrong(
requireContext())
) View.VISIBLE else View.GONE)
injector.visibility(R.id.info_icon,
requireContext()
)
) View.VISIBLE else View.GONE
)
injector.visibility(
R.id.info_icon,
if (!(Preferences.showNextAlarm && AlarmHelper.isAlarmProbablyWrong(
requireContext()))
) View.VISIBLE else View.GONE)
requireContext()
))
) View.VISIBLE else View.GONE
)
isVisible = (Preferences.showNextAlarm && !AlarmHelper.isAlarmProbablyWrong(
requireContext()
))
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
injector.text(R.id.label,
injector.text(
R.id.label,
if (Preferences.showBatteryCharging) getString(R.string.settings_visible) else getString(
R.string.settings_not_visible))
R.string.settings_not_visible
)
)
injector.visibility(R.id.error_icon, View.GONE)
injector.visibility(R.id.info_icon, View.VISIBLE)
isVisible = Preferences.showBatteryCharging
}
Constants.GlanceProviderId.NOTIFICATIONS -> {
when {
ActiveNotificationsHelper.checkNotificationAccess(requireContext()) -> {
injector.visibility(R.id.error_icon, View.GONE)
injector.visibility(R.id.info_icon, View.VISIBLE)
injector.text(R.id.label,
injector.text(
R.id.label,
if (Preferences.showNotifications) getString(
R.string.settings_visible) else getString(R.string.settings_not_visible))
R.string.settings_visible
) else getString(R.string.settings_not_visible)
)
isVisible = Preferences.showNotifications
}
Preferences.showNotifications -> {
injector.visibility(R.id.error_icon, View.VISIBLE)
injector.visibility(R.id.info_icon, View.GONE)
injector.text(R.id.label, getString(R.string.settings_not_visible))
isVisible = false
}
else -> {
injector.visibility(R.id.error_icon, View.GONE)
injector.visibility(R.id.info_icon, View.VISIBLE)
injector.text(R.id.label, getString(R.string.settings_not_visible))
isVisible = false
}
}
}
Constants.GlanceProviderId.GREETINGS -> {
injector.text(R.id.label,
injector.text(
R.id.label,
if (Preferences.showGreetings) getString(R.string.settings_visible) else getString(
R.string.settings_not_visible))
R.string.settings_not_visible
)
)
injector.visibility(R.id.error_icon, View.GONE)
injector.visibility(R.id.info_icon, View.VISIBLE)
isVisible = Preferences.showGreetings
}
Constants.GlanceProviderId.CUSTOM_INFO -> {
injector.text(R.id.label,
injector.text(
R.id.label,
if (Preferences.customNotes != "") getString(R.string.settings_visible) else getString(
R.string.settings_not_visible))
R.string.settings_not_visible
)
)
injector.visibility(R.id.error_icon, View.GONE)
injector.visibility(R.id.info_icon, View.VISIBLE)
isVisible = Preferences.customNotes != ""
}
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(context)
if (GoogleSignIn.hasPermissions(account, FITNESS_OPTIONS) && (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || activity?.checkGrantedPermission(
Manifest.permission.ACTIVITY_RECOGNITION) == true)
val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(
context
)
if (GoogleSignIn.hasPermissions(
account,
FITNESS_OPTIONS
) && (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || requireActivity().checkGrantedPermission(
Manifest.permission.ACTIVITY_RECOGNITION
))
) {
injector.text(R.id.label,
injector.text(
R.id.label,
if (Preferences.showDailySteps) getString(R.string.settings_visible) else getString(
R.string.settings_not_visible))
R.string.settings_not_visible
)
)
injector.visibility(R.id.error_icon, View.GONE)
injector.visibility(R.id.info_icon, View.VISIBLE)
isVisible = Preferences.showDailySteps
} else if (Preferences.showDailySteps) {
ActivityDetectionReceiver.unregisterFence(requireContext())
injector.visibility(R.id.error_icon, View.VISIBLE)
injector.visibility(R.id.info_icon, View.GONE)
injector.text(R.id.label, getString(R.string.settings_not_visible))
isVisible = false
} else {
ActivityDetectionReceiver.unregisterFence(requireContext())
injector.text(R.id.label, getString(R.string.settings_not_visible))
injector.visibility(R.id.error_icon, View.GONE)
injector.visibility(R.id.info_icon, View.VISIBLE)
isVisible = false
}
}
Constants.GlanceProviderId.EVENTS -> {
isVisible =
Preferences.showEventsAsGlanceProvider
val hasError = !Preferences.showEvents || !requireContext().checkGrantedPermission(
Manifest.permission.READ_CALENDAR
)
injector.text(
R.id.label,
if (isVisible && !hasError) getString(R.string.settings_visible) else getString(
R.string.settings_not_visible
)
)
injector.visibility(
R.id.error_icon,
if (isVisible && hasError) View.VISIBLE else View.GONE
)
injector.visibility(
R.id.info_icon,
if (!(isVisible && hasError)) View.VISIBLE else View.GONE
)
}
}
injector.alpha(R.id.title, if (isVisible) 1f else .25f)
injector.alpha(R.id.label, if (isVisible) 1f else .25f)
injector.alpha(R.id.icon, if (isVisible) 1f else .25f)
}
.attachTo(providers_list)
.attachTo(binding.providersList)
val mIth = ItemTouchHelper(
object : ItemTouchHelper.SimpleCallback(
@ -231,8 +307,6 @@ class GlanceTabFragment : Fragment() {
0
) {
val list = GlanceProviderHelper.getGlanceProviders(requireContext())
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder,
@ -244,10 +318,6 @@ class GlanceTabFragment : Fragment() {
return true
}
override fun isItemViewSwipeEnabled(): Boolean {
return false
}
override fun onMoved(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
@ -257,14 +327,33 @@ class GlanceTabFragment : Fragment() {
x: Int,
y: Int
) {
with(list[fromPos]) {
list[fromPos] = list[toPos]
list[toPos] = this
with(list[toPos]) {
list[toPos] = list[fromPos]
list[fromPos] = this
}
GlanceProviderHelper.saveGlanceProviderOrder(list)
super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y)
}
override fun isItemViewSwipeEnabled(): Boolean {
return false
}
override fun clearView(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
) {
super.clearView(recyclerView, viewHolder)
GlanceProviderHelper.saveGlanceProviderOrder(
list
)
adapter.updateData(list.mapNotNull {
GlanceProviderHelper.getGlanceProviderById(
requireContext(),
it
)
})
}
override fun onChildDraw(
c: Canvas,
recyclerView: RecyclerView,
@ -276,24 +365,38 @@ class GlanceTabFragment : Fragment() {
) {
val view = viewHolder.itemView as MaterialCardView
if (isCurrentlyActive) {
ViewCompat.setElevation(view, 2f.convertDpToPixel(requireContext()))
view.setCardBackgroundColor(ContextCompat.getColor(requireContext(),
R.color.colorPrimary))
ViewCompat.setElevation(view, 8f.convertDpToPixel(requireContext()))
view.setCardBackgroundColor(
ContextCompat.getColor(
requireContext(),
R.color.cardBorder
)
)
} else {
ViewCompat.setElevation(view, 0f)
view.setCardBackgroundColor(ContextCompat.getColor(requireContext(),
R.color.colorPrimaryDark))
view.setCardBackgroundColor(
ContextCompat.getColor(
requireContext(),
R.color.colorPrimary
)
)
}
val topEdge = if ((view.top == 0 && dY < 0) || ((view.top + view.height >= recyclerView.height - 32f.convertDpToPixel(requireContext())) && dY > 0)) 0f else dY
val topEdge =
if ((view.top == 0 && dY < 0) || ((view.top + view.height >= recyclerView.height - 32f.convertDpToPixel(
requireContext()
)) && dY > 0)
) 0f else dY
super.onChildDraw(c,
super.onChildDraw(
c,
recyclerView,
viewHolder,
dX,
topEdge,
actionState,
isCurrentlyActive)
isCurrentlyActive
)
}
override fun onSwiped(
@ -304,40 +407,34 @@ class GlanceTabFragment : Fragment() {
}
})
mIth.attachToRecyclerView(providers_list)
adapter.updateData(
GlanceProviderHelper.getGlanceProviders(requireContext())
.mapNotNull { GlanceProviderHelper.getGlanceProviderById(requireContext(), it) }
)
providers_list.isNestedScrollingEnabled = false
mIth.attachToRecyclerView(binding.providersList)
setupListener()
}
private fun subscribeUi(
binding: FragmentGlanceSettingsBinding,
viewModel: MainViewModel,
) {
binding.isGlanceVisible = Preferences.showGlance
binding.scrollView.viewTreeObserver.addOnScrollChangedListener {
viewModel.fragmentScrollY.value = binding.scrollView.scrollY
}
viewModel.showGlance.observe(viewLifecycleOwner, Observer {
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)
lifecycleScope.launch(Dispatchers.IO) {
delay(500)
val l = list.mapNotNull { GlanceProviderHelper.getGlanceProviderById(
requireContext(),
it
) }
withContext(Dispatchers.Main) {
binding.loader.animate().scaleX(0f).scaleY(0f).alpha(0f).start()
adapter.updateData(l)
val controller =
AnimationUtils.loadLayoutAnimation(context, R.anim.layout_animation_fall_down)
binding.providersList.layoutAnimation = controller
adapter.notifyDataSetChanged()
binding.providersList.scheduleLayoutAnimation()
}
})
}
}
private fun setupListener() {
action_show_glance.setOnClickListener {
Preferences.showGlance = !Preferences.showGlance
}
show_glance_switch.setOnCheckedChangeListener { _, enabled: Boolean ->
Preferences.showGlance = enabled
}
}
private val nextAlarmChangeBroadcastReceiver = object : BroadcastReceiver() {
@ -348,15 +445,17 @@ class GlanceTabFragment : Fragment() {
override fun onStart() {
super.onStart()
activity?.registerReceiver(nextAlarmChangeBroadcastReceiver,
IntentFilter(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED))
requireActivity().registerReceiver(
nextAlarmChangeBroadcastReceiver,
IntentFilter(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)
)
if (dialog != null) {
dialog?.show()
}
}
override fun onStop() {
activity?.unregisterReceiver(nextAlarmChangeBroadcastReceiver)
requireActivity().unregisterReceiver(nextAlarmChangeBroadcastReceiver)
super.onStop()
}
@ -380,13 +479,15 @@ class GlanceTabFragment : Fragment() {
2 -> {
try {
val account: GoogleSignInAccount? = GoogleSignIn.getSignedInAccountFromIntent(
data).getResult(ApiException::class.java)
data
).getResult(ApiException::class.java)
if (!GoogleSignIn.hasPermissions(account, FITNESS_OPTIONS)) {
GoogleSignIn.requestPermissions(
requireActivity(),
1,
account,
FITNESS_OPTIONS)
FITNESS_OPTIONS
)
} else {
adapter.notifyItemRangeChanged(0, adapter.data.size)
}
@ -402,18 +503,9 @@ class GlanceTabFragment : Fragment() {
}
}
private fun maintainScrollPosition(callback: () -> Unit) {
scrollView.isScrollable = false
callback.invoke()
lifecycleScope.launch {
delay(200)
scrollView.isScrollable = true
}
}
override fun onResume() {
super.onResume()
adapter.notifyItemRangeChanged(0, adapter.data.size)
adapter.notifyItemRangeChanged(0, adapter.data?.size ?: 0)
if (dialog != null) {
dialog?.show()
}

View File

@ -0,0 +1,233 @@
package com.tommasoberlose.anotherwidget.ui.fragments.tabs
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
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.google.android.material.transition.MaterialSharedAxis
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.BottomSheetColorPicker
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
import com.tommasoberlose.anotherwidget.databinding.FragmentTabLayoutBinding
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
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.ui.activities.tabs.CustomDateActivity
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
class LayoutFragment : Fragment() {
companion object {
fun newInstance() = LayoutFragment()
}
private lateinit var viewModel: MainViewModel
private lateinit var colors: IntArray
private lateinit var binding: FragmentTabLayoutBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
binding = FragmentTabLayoutBinding.inflate(inflater)
subscribeUi(viewModel)
binding.lifecycleOwner = this
binding.viewModel = viewModel
binding.isDarkModeEnabled = requireActivity().isDarkTheme()
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
binding.showDividersToggle.setCheckedImmediatelyNoEvent(Preferences.showDividers)
setupListener()
lifecycleScope.launch(Dispatchers.IO) {
val lazyColors = requireContext().resources.getIntArray(R.array.material_colors)
withContext(Dispatchers.Main) {
colors = lazyColors
}
}
binding.scrollView.viewTreeObserver?.addOnScrollChangedListener {
viewModel.fragmentScrollY.value = binding.scrollView.scrollY
}
}
@SuppressLint("DefaultLocale")
private fun subscribeUi(
viewModel: MainViewModel
) {
viewModel.secondRowTopMargin.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.secondRowTopMarginLabel.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)
}
}
}
viewModel.clockBottomMargin.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.clockBottomMarginLabel.text = when (it) {
Constants.ClockBottomMargin.NONE.value -> getString(R.string.settings_clock_bottom_margin_subtitle_none)
Constants.ClockBottomMargin.SMALL.value -> getString(R.string.settings_clock_bottom_margin_subtitle_small)
Constants.ClockBottomMargin.LARGE.value -> getString(R.string.settings_clock_bottom_margin_subtitle_large)
else -> getString(R.string.settings_clock_bottom_margin_subtitle_medium)
}
}
}
viewModel.backgroundCardColor.observe(viewLifecycleOwner) {
maintainScrollPosition {
if (Preferences.backgroundCardAlpha == "00") {
binding.backgroundColorLabel.text = getString(R.string.transparent)
} else {
binding.backgroundColorLabel.text =
"#%s".format(Integer.toHexString(ColorHelper.getBackgroundColor(requireActivity().isDarkTheme()))).toUpperCase()
}
}
}
viewModel.showDividers.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.showDividersLabel.text =
if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
}
}
}
private fun setupListener() {
binding.actionSecondRowTopMarginSize.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()
}
binding.actionClockBottomMarginSize.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()
}
binding.actionBackgroundColor.setOnClickListener {
BottomSheetColorPicker(requireContext(),
colors = colors,
header = getString(R.string.settings_background_color_title),
getSelected = { ColorHelper.getBackgroundColorRgb(requireActivity().isDarkTheme()) },
onColorSelected = { color: Int ->
val colorString = Integer.toHexString(color)
if (requireActivity().isDarkTheme()) {
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 = if (requireActivity().isDarkTheme()) Preferences.backgroundCardAlphaDark.toIntValue() else Preferences.backgroundCardAlpha.toIntValue(),
onAlphaChangeListener = { alpha ->
if (requireActivity().isDarkTheme()) {
Preferences.backgroundCardAlphaDark = alpha.toHexValue()
} else {
Preferences.backgroundCardAlpha = alpha.toHexValue()
}
}
).show()
}
binding.actionShowDividers.setOnClickListener {
binding.showDividersToggle.isChecked = !binding.showDividersToggle.isChecked
}
binding.showDividersToggle.setOnCheckedChangeListener { _, isChecked ->
Preferences.showDividers = isChecked
}
}
private fun maintainScrollPosition(callback: () -> Unit) {
binding.scrollView.isScrollable = false
callback.invoke()
lifecycleScope.launch {
delay(200)
binding.scrollView.isScrollable = true
}
}
}

View File

@ -0,0 +1,206 @@
package com.tommasoberlose.anotherwidget.ui.fragments.tabs
import android.Manifest
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
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.navigation.Navigation
import com.google.android.material.transition.MaterialSharedAxis
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.MaterialBottomSheetDialog
import com.tommasoberlose.anotherwidget.databinding.FragmentPreferencesBinding
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
import com.tommasoberlose.anotherwidget.receivers.WeatherReceiver
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.utils.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class PreferencesFragment : Fragment() {
companion object {
fun newInstance() = PreferencesFragment()
}
private lateinit var viewModel: MainViewModel
private lateinit var binding: FragmentPreferencesBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
binding = FragmentPreferencesBinding.inflate(inflater)
subscribeUi(viewModel)
binding.lifecycleOwner = this
binding.viewModel = viewModel
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setupListener()
binding.showEventsSwitch.setCheckedImmediatelyNoEvent(Preferences.showEvents)
binding.showWeatherSwitch.setCheckedImmediatelyNoEvent(Preferences.showWeather)
binding.showClockSwitch.setCheckedImmediatelyNoEvent(Preferences.showClock)
binding.scrollView.viewTreeObserver.addOnScrollChangedListener {
viewModel.fragmentScrollY.value = binding.scrollView.scrollY
}
}
private fun subscribeUi(
viewModel: MainViewModel
) {
viewModel.showEvents.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.showEventsSwitch.setCheckedImmediatelyNoEvent(it)
if (it) {
CalendarHelper.setEventUpdatesAndroidN(requireContext())
} else {
CalendarHelper.removeEventUpdatesAndroidN(requireContext())
}
}
}
viewModel.showWeather.observe(viewLifecycleOwner) {
checkWeatherProviderConfig()
}
viewModel.weatherProviderError.observe(viewLifecycleOwner) {
checkWeatherProviderConfig()
}
viewModel.weatherProviderLocationError.observe(viewLifecycleOwner) {
checkWeatherProviderConfig()
}
}
private fun setupListener() {
binding.actionTypography.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_tabSelectorFragment_to_typographyTabFragment)
}
binding.actionGeneralSettings.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_tabSelectorFragment_to_generalTabFragment)
}
binding.actionShowEvents.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_tabSelectorFragment_to_calendarTabFragment)
}
binding.showEventsSwitch.setOnCheckedChangeListener { _, enabled: Boolean ->
if (enabled) {
requireCalendarPermission()
} else {
Preferences.showEvents = enabled
}
}
binding.actionShowWeather.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_tabSelectorFragment_to_weatherTabFragment)
}
binding.showWeatherSwitch.setOnCheckedChangeListener { _, enabled: Boolean ->
Preferences.showWeather = enabled
if (enabled) {
WeatherReceiver.setUpdates(requireContext())
} else {
WeatherReceiver.removeUpdates(requireContext())
}
}
binding.actionShowClock.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_tabSelectorFragment_to_clockTabFragment)
}
binding.showClockSwitch.setOnCheckedChangeListener { _, enabled: Boolean ->
Preferences.showClock = enabled
}
binding.actionShowGlance.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_tabSelectorFragment_to_glanceTabFragment)
}
binding.actionTabDefaultApp.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_tabSelectorFragment_to_gesturesFragment)
}
}
private fun requireCalendarPermission() {
Dexter.withContext(requireContext())
.withPermissions(
Manifest.permission.READ_CALENDAR
).withListener(object: MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
report?.let {
val granted = report.areAllPermissionsGranted()
Preferences.showEvents = granted
if (granted) {
CalendarHelper.updateEventList(requireContext())
}
}
}
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 checkWeatherProviderConfig() {
binding.weatherProviderError.isVisible = Preferences.showWeather && Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-"
binding.weatherProviderError.text = Preferences.weatherProviderError
binding.weatherProviderLocationError.isVisible = Preferences.showWeather && Preferences.weatherProviderLocationError != ""
binding.weatherProviderLocationError.text = Preferences.weatherProviderLocationError
}
override fun onResume() {
super.onResume()
binding.showEventsSwitch.setCheckedNoEvent(Preferences.showEvents && requireActivity().checkGrantedPermission(Manifest.permission.READ_CALENDAR))
}
private fun maintainScrollPosition(callback: () -> Unit) {
binding.scrollView.isScrollable = false
callback.invoke()
lifecycleScope.launch {
delay(200)
binding.scrollView.isScrollable = true
}
}
}

View File

@ -0,0 +1,325 @@
package com.tommasoberlose.anotherwidget.ui.fragments.tabs
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.chibatching.kotpref.blockingBulk
import com.chibatching.kotpref.bulk
import com.google.android.material.transition.MaterialSharedAxis
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.BottomSheetColorPicker
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
import com.tommasoberlose.anotherwidget.databinding.FragmentTabTypographyBinding
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.ui.activities.tabs.CustomFontActivity
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.activities.tabs.CustomDateActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
class TypographyFragment : Fragment() {
companion object {
fun newInstance() = TypographyFragment()
}
private lateinit var viewModel: MainViewModel
private lateinit var colors: IntArray
private lateinit var binding: FragmentTabTypographyBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
binding = FragmentTabTypographyBinding.inflate(inflater)
subscribeUi(viewModel)
binding.lifecycleOwner = this
binding.viewModel = viewModel
binding.isDarkModeEnabled = activity?.isDarkTheme() == true
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setupListener()
lifecycleScope.launch(Dispatchers.IO) {
val lazyColors = requireContext().resources.getIntArray(R.array.material_colors)
withContext(Dispatchers.Main) {
colors = lazyColors
}
}
binding.scrollView.viewTreeObserver.addOnScrollChangedListener {
viewModel.fragmentScrollY.value = binding.scrollView.scrollY
}
}
@SuppressLint("DefaultLocale")
private fun subscribeUi(
viewModel: MainViewModel
) {
viewModel.textMainSize.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.mainTextSizeLabel.text = String.format("%.0fsp", it)
}
}
viewModel.textSecondSize.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.secondTextSizeLabel.text = String.format("%.0fsp", it)
}
}
viewModel.textGlobalColor.observe(viewLifecycleOwner) {
maintainScrollPosition {
if (Preferences.textGlobalAlpha == "00") {
binding.fontColorLabel.text = getString(R.string.transparent)
} else {
binding.fontColorLabel.text =
"#%s".format(Integer.toHexString(ColorHelper.getFontColor(requireActivity().isDarkTheme()))).toUpperCase()
}
}
}
viewModel.textSecondaryColor.observe(viewLifecycleOwner) {
maintainScrollPosition {
if (Preferences.textSecondaryAlpha == "00") {
binding.secondaryFontColorLabel.text = getString(R.string.transparent)
} else {
binding.secondaryFontColorLabel.text =
"#%s".format(Integer.toHexString(ColorHelper.getSecondaryFontColor(requireActivity().isDarkTheme()))).toUpperCase()
}
}
}
viewModel.textShadow.observe(viewLifecycleOwner) {
maintainScrollPosition {
if (requireActivity().isDarkTheme()) {
binding.textShadowLabel.text =
getString(SettingsStringHelper.getTextShadowString(it))
}
}
}
viewModel.textShadow.observe(viewLifecycleOwner) {
maintainScrollPosition {
if (!requireActivity().isDarkTheme()) {
binding.textShadowLabel.text =
getString(SettingsStringHelper.getTextShadowString(it))
}
}
}
viewModel.textShadowDark.observe(viewLifecycleOwner) {
maintainScrollPosition {
if (requireActivity().isDarkTheme()) {
binding.textShadowLabel.text =
getString(SettingsStringHelper.getTextShadowString(it))
}
}
}
viewModel.font.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.customFontLabel.text = SettingsStringHelper.getCustomFontLabel(requireContext(), Preferences.customFont)
}
}
viewModel.dateFormat.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.dateFormatLabel.text = DateHelper.getDateText(requireContext(), Calendar.getInstance())
}
}
}
private fun setupListener() {
binding.actionMainTextSize.setOnClickListener {
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.title_main_text_size)).setSelectedValue(
Preferences.textMainSize)
(40 downTo 10).filter { it % 2 == 0 }.forEach {
dialog.addItem("${it}sp", it.toFloat())
}
dialog.addOnSelectItemListener { value ->
Preferences.textMainSize = value
}.show()
}
binding.actionSecondTextSize.setOnClickListener {
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.title_second_text_size)).setSelectedValue(
Preferences.textSecondSize)
(40 downTo 10).filter { it % 2 == 0 }.forEach {
dialog.addItem("${it}sp", it.toFloat())
}
dialog.addOnSelectItemListener { value ->
Preferences.textSecondSize = value
}.show()
}
binding.actionFontColor.setOnClickListener {
BottomSheetColorPicker(requireContext(),
colors = colors,
header = getString(R.string.settings_font_color_title),
getSelected = { ColorHelper.getFontColorRgb(requireActivity().isDarkTheme()) },
onColorSelected = { color: Int ->
val colorString = Integer.toHexString(color)
if (requireActivity().isDarkTheme()) {
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 = if (requireActivity().isDarkTheme()) Preferences.textGlobalAlphaDark.toIntValue() else Preferences.textGlobalAlpha.toIntValue(),
onAlphaChangeListener = { alpha ->
if (requireActivity().isDarkTheme()) {
Preferences.textGlobalAlphaDark = alpha.toHexValue()
} else {
Preferences.textGlobalAlpha = alpha.toHexValue()
}
}
).show()
}
binding.actionSecondaryFontColor.setOnClickListener {
BottomSheetColorPicker(requireContext(),
colors = colors,
header = getString(R.string.settings_secondary_font_color_title),
getSelected = { ColorHelper.getSecondaryFontColorRgb(requireActivity().isDarkTheme()) },
onColorSelected = { color: Int ->
val colorString = Integer.toHexString(color)
if (requireActivity().isDarkTheme()) {
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 (requireActivity().isDarkTheme()) Preferences.textSecondaryAlphaDark.toIntValue() else Preferences.textSecondaryAlpha.toIntValue(),
onAlphaChangeListener = { alpha ->
if (requireActivity().isDarkTheme()) {
Preferences.textSecondaryAlphaDark = alpha.toHexValue()
} else {
Preferences.textSecondaryAlpha = alpha.toHexValue()
}
}
).show()
}
binding.actionTextShadow.setOnClickListener {
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.title_text_shadow)).setSelectedValue(if (requireActivity().isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow)
(2 downTo 0).forEach {
dialog.addItem(getString(SettingsStringHelper.getTextShadowString(it)), it)
}
dialog.addOnSelectItemListener { value ->
if (requireActivity().isDarkTheme()) {
Preferences.textShadowDark = value
} else {
Preferences.textShadow = value
}
}.show()
}
binding.actionCustomFont.setOnClickListener {
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_custom_font_title)).setSelectedValue(
Preferences.customFont)
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 ->
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()
}
binding.actionDateFormat.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()
}
}
private fun maintainScrollPosition(callback: () -> Unit) {
binding.scrollView.isScrollable = false
callback.invoke()
lifecycleScope.launch {
delay(200)
binding.scrollView.isScrollable = true
}
}
}

View File

@ -0,0 +1,252 @@
package com.tommasoberlose.anotherwidget.ui.fragments.tabs
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
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 com.chibatching.kotpref.bulk
import com.google.android.material.transition.MaterialSharedAxis
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.IconPackSelector
import com.tommasoberlose.anotherwidget.components.MaterialBottomSheetDialog
import com.tommasoberlose.anotherwidget.databinding.FragmentTabWeatherBinding
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.global.RequestCode
import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
import com.tommasoberlose.anotherwidget.receivers.WeatherReceiver
import com.tommasoberlose.anotherwidget.ui.activities.tabs.ChooseApplicationActivity
import com.tommasoberlose.anotherwidget.ui.activities.tabs.CustomLocationActivity
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.activities.tabs.WeatherProviderActivity
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.collapse
import com.tommasoberlose.anotherwidget.utils.expand
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class WeatherFragment : Fragment() {
companion object {
fun newInstance() = WeatherFragment()
}
private lateinit var viewModel: MainViewModel
private lateinit var binding: FragmentTabWeatherBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
binding = FragmentTabWeatherBinding.inflate(inflater)
subscribeUi(viewModel)
binding.lifecycleOwner = this
binding.viewModel = viewModel
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setupListener()
binding.scrollView.viewTreeObserver.addOnScrollChangedListener {
viewModel.fragmentScrollY.value = binding.scrollView.scrollY
}
}
private fun subscribeUi(
viewModel: MainViewModel
) {
binding.isWeatherVisible = Preferences.showWeather
viewModel.weatherProvider.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.labelWeatherProvider.text = WeatherHelper.getProviderName(requireContext(), Constants.WeatherProvider.fromInt(it)!!)
checkWeatherProviderConfig()
}
}
viewModel.weatherProviderError.observe(viewLifecycleOwner) {
checkWeatherProviderConfig()
}
viewModel.weatherProviderLocationError.observe(viewLifecycleOwner) {
checkWeatherProviderConfig()
}
viewModel.customLocationAdd.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.labelCustomLocation.text =
if (it == "") getString(R.string.custom_location_gps) else it
}
checkLocationPermission()
}
viewModel.weatherTempUnit.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.tempUnit.text =
if (it == "F") getString(R.string.fahrenheit) else getString(R.string.celsius)
}
checkLocationPermission()
}
viewModel.weatherRefreshPeriod.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.labelWeatherRefreshPeriod.text = getString(SettingsStringHelper.getRefreshPeriodString(it))
}
checkLocationPermission()
}
viewModel.weatherIconPack.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.labelWeatherIconPack.text = getString(R.string.settings_weather_icon_pack_default).format((it + 1))
}
checkLocationPermission()
}
}
private fun checkLocationPermission() {
if (requireActivity().checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
binding.locationPermissionAlert.isVisible = false
WeatherReceiver.setUpdates(requireContext())
} else if (Preferences.showWeather && Preferences.customLocationAdd == "") {
binding.locationPermissionAlert.isVisible = true
binding.locationPermissionAlert.setOnClickListener {
requirePermission()
}
} else {
binding.locationPermissionAlert.isVisible = false
}
}
private fun checkWeatherProviderConfig() {
binding.weatherProviderError.isVisible = Preferences.showWeather && Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-"
binding.weatherProviderError.text = Preferences.weatherProviderError
binding.weatherProviderLocationError.isVisible = Preferences.showWeather && Preferences.weatherProviderLocationError != ""
binding.weatherProviderLocationError.text = Preferences.weatherProviderLocationError
}
private fun setupListener() {
binding.actionWeatherProvider.setOnClickListener {
startActivityForResult(
Intent(requireContext(), WeatherProviderActivity::class.java),
RequestCode.WEATHER_PROVIDER_REQUEST_CODE.code
)
}
binding.actionCustomLocation.setOnClickListener {
startActivityForResult(
Intent(requireContext(), CustomLocationActivity::class.java),
Constants.RESULT_CODE_CUSTOM_LOCATION
)
}
binding.actionChangeUnit.setOnClickListener {
BottomSheetMenu<String>(requireContext(), header = getString(R.string.settings_unit_title)).setSelectedValue(Preferences.weatherTempUnit)
.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()
}
binding.actionWeatherRefreshPeriod.setOnClickListener {
val dialog =
BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_weather_refresh_period_title)).setSelectedValue(Preferences.weatherRefreshPeriod)
(5 downTo 0).forEach {
dialog.addItem(getString(SettingsStringHelper.getRefreshPeriodString(it)), it)
}
dialog
.addOnSelectItemListener { value ->
Preferences.weatherRefreshPeriod = value
}.show()
}
binding.actionWeatherIconPack.setOnClickListener {
IconPackSelector(requireContext(), header = getString(R.string.settings_weather_icon_pack_title)).show()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
Constants.RESULT_CODE_CUSTOM_LOCATION -> {
WeatherReceiver.setUpdates(requireContext())
checkLocationPermission()
}
RequestCode.WEATHER_PROVIDER_REQUEST_CODE.code -> {
checkLocationPermission()
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
private fun requirePermission() {
Dexter.withContext(requireContext())
.withPermissions(
Manifest.permission.ACCESS_FINE_LOCATION
).withListener(object: MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
report?.let {
if (report.areAllPermissionsGranted()){
checkLocationPermission()
}
}
}
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) {
binding.scrollView.isScrollable = false
callback.invoke()
lifecycleScope.launch {
delay(200)
binding.scrollView.isScrollable = true
}
}
}

View File

@ -1,32 +1,51 @@
package com.tommasoberlose.anotherwidget.ui.viewmodels
import android.app.Application
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import androidx.core.os.ConfigurationCompat
import androidx.lifecycle.*
import com.chibatching.kotpref.livedata.asLiveData
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColor
import com.tommasoberlose.anotherwidget.utils.isMetric
class MainViewModel : ViewModel() {
class MainViewModel(context: Application) : AndroidViewModel(context) {
// 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 textGlobalColor = MediatorLiveData<Boolean>().apply {
addSource(Preferences.asLiveData(Preferences::textGlobalColor)) { value = true }
addSource(Preferences.asLiveData(Preferences::textGlobalAlpha)) { value = true }
addSource(Preferences.asLiveData(Preferences::textGlobalColorDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::textGlobalAlphaDark)) { value = true }
}
val textSecondaryColor = MediatorLiveData<Boolean>().apply {
addSource(Preferences.asLiveData(Preferences::textSecondaryColor)) { value = true }
addSource(Preferences.asLiveData(Preferences::textSecondaryAlpha)) { value = true }
addSource(Preferences.asLiveData(Preferences::textSecondaryColorDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::textSecondaryAlphaDark)) { value = true }
}
val backgroundCardColor = MediatorLiveData<Boolean>().apply {
addSource(Preferences.asLiveData(Preferences::backgroundCardColor)) { value = true }
addSource(Preferences.asLiveData(Preferences::backgroundCardAlpha)) { value = true }
addSource(Preferences.asLiveData(Preferences::backgroundCardColorDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::backgroundCardAlphaDark)) { value = true }
}
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 font = MediatorLiveData<Boolean>().apply {
addSource(Preferences.asLiveData(Preferences::customFont)) { value = true }
addSource(Preferences.asLiveData(Preferences::customFontFile)) { value = true }
addSource(Preferences.asLiveData(Preferences::customFontName)) { value = true }
addSource(Preferences.asLiveData(Preferences::customFontVariant)) { value = true }
}
val secondRowInformation = Preferences.asLiveData(Preferences::secondRowInformation)
val showDividers = Preferences.asLiveData(Preferences::showDividers)
val secondRowTopMargin = Preferences.asLiveData(Preferences::secondRowTopMargin)
@ -36,56 +55,117 @@ class MainViewModel : ViewModel() {
val calendarAllDay = Preferences.asLiveData(Preferences::calendarAllDay)
val showUntil = Preferences.asLiveData(Preferences::showUntil)
val showDiffTime = Preferences.asLiveData(Preferences::showDiffTime)
val showDeclinedEvents = Preferences.asLiveData(Preferences::showDeclinedEvents)
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)
val dateFormat = Preferences.asLiveData(Preferences::dateFormat)
// 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 clockTextColor = MediatorLiveData<Boolean>().apply {
addSource(Preferences.asLiveData(Preferences::clockTextColor)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockTextAlpha)) { value = true }
}
val clockTextColorDark = MediatorLiveData<Boolean>().apply {
addSource(Preferences.asLiveData(Preferences::clockTextColorDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockTextAlphaDark)) { value = true }
}
val showAMPMIndicator = Preferences.asLiveData(Preferences::showAMPMIndicator)
val clockAppName = Preferences.asLiveData(Preferences::clockAppName)
val dateFormat = Preferences.asLiveData(Preferences::dateFormat)
val clockBottomMargin = Preferences.asLiveData(Preferences::clockBottomMargin)
val showBigClockWarning = Preferences.asLiveData(Preferences::showBigClockWarning)
// Weather Settings
val showWeather = Preferences.asLiveData(Preferences::showWeather)
val weatherTempUnit = Preferences.asLiveData(Preferences::weatherTempUnit)
val weatherRefreshPeriod = Preferences.asLiveData(Preferences::weatherRefreshPeriod)
val weatherAppName = Preferences.asLiveData(Preferences::weatherAppName)
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)
val showWallpaper = Preferences.asLiveData(Preferences::showWallpaper)
val showPreview = Preferences.asLiveData(Preferences::showPreview)
val installedIntegrations = Preferences.asLiveData(Preferences::installedIntegrations)
// UI
val fragmentScrollY = MutableLiveData<Int>()
val clockPreferencesUpdate = MediatorLiveData<Boolean>().apply {
addSource(Preferences.asLiveData(Preferences::clockTextSize)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockTextColor)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockTextAlpha)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockTextColorDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockTextAlphaDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::showAMPMIndicator)) { value = true }
addSource(Preferences.asLiveData(Preferences::clockBottomMargin)) { value = true }
}
val widgetPreferencesUpdate = MediatorLiveData<Boolean>().apply {
addSource(Preferences.asLiveData(Preferences::textGlobalColor)) { value = true }
addSource(Preferences.asLiveData(Preferences::textGlobalAlpha)) { value = true }
addSource(Preferences.asLiveData(Preferences::textSecondaryColor)) { value = true }
addSource(Preferences.asLiveData(Preferences::textSecondaryAlpha)) { value = true }
addSource(Preferences.asLiveData(Preferences::backgroundCardColor)) { value = true }
addSource(Preferences.asLiveData(Preferences::backgroundCardAlpha)) { value = true }
addSource(Preferences.asLiveData(Preferences::textGlobalColorDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::textGlobalAlphaDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::dateFormat)) { value = true }
addSource(Preferences.asLiveData(Preferences::textSecondaryColorDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::textSecondaryAlphaDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::backgroundCardColorDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::backgroundCardAlphaDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::textMainSize)) { value = true }
addSource(Preferences.asLiveData(Preferences::textSecondSize)) { value = true }
addSource(Preferences.asLiveData(Preferences::textShadow)) { value = true }
addSource(Preferences.asLiveData(Preferences::textShadowDark)) { value = true }
addSource(Preferences.asLiveData(Preferences::customFont)) { value = true }
addSource(Preferences.asLiveData(Preferences::customFontFile)) { value = true }
addSource(Preferences.asLiveData(Preferences::customFontName)) { value = true }
addSource(Preferences.asLiveData(Preferences::customFontVariant)) { value = true }
addSource(Preferences.asLiveData(Preferences::secondRowInformation)) { value = true }
addSource(Preferences.asLiveData(Preferences::showDividers)) { value = true }
addSource(Preferences.asLiveData(Preferences::secondRowTopMargin)) { value = true }
addSource(Preferences.asLiveData(Preferences::isDateCapitalize)) { value = true }
addSource(Preferences.asLiveData(Preferences::isDateUppercase)) { value = true }
addSource(Preferences.asLiveData(Preferences::showEvents)) { value = true }
addSource(Preferences.asLiveData(Preferences::calendarAllDay)) { value = true }
addSource(Preferences.asLiveData(Preferences::showDiffTime)) { value = true }
addSource(Preferences.asLiveData(Preferences::showNextEvent)) { value = true }
addSource(Preferences.asLiveData(Preferences::showDeclinedEvents)) { value = true }
addSource(Preferences.asLiveData(Preferences::showInvitedEvents)) { value = true }
addSource(Preferences.asLiveData(Preferences::showAcceptedEvents)) { value = true }
addSource(Preferences.asLiveData(Preferences::showOnlyBusyEvents)) { value = true }
addSource(Preferences.asLiveData(Preferences::secondRowInformation)) { value = true }
addSource(Preferences.asLiveData(Preferences::showWeather)) { value = true }
addSource(Preferences.asLiveData(Preferences::weatherTempUnit)) { value = true }
addSource(Preferences.asLiveData(Preferences::weatherIconPack)) { value = true }
addSource(Preferences.asLiveData(Preferences::customLocationLat)) { value = true }
addSource(Preferences.asLiveData(Preferences::customLocationLon)) { value = true }
addSource(Preferences.asLiveData(Preferences::customLocationAdd)) { value = true }
addSource(Preferences.asLiveData(Preferences::enabledGlanceProviderOrder)) { value = true }
addSource(Preferences.asLiveData(Preferences::customNotes)) { value = true }
addSource(Preferences.asLiveData(Preferences::showNextAlarm)) { value = true }
addSource(Preferences.asLiveData(Preferences::showBatteryCharging)) { value = true }
addSource(Preferences.asLiveData(Preferences::showDailySteps)) { value = true }
addSource(Preferences.asLiveData(Preferences::showGreetings)) { value = true }
addSource(Preferences.asLiveData(Preferences::showNotifications)) { value = true }
addSource(Preferences.asLiveData(Preferences::showMusic)) { value = true }
addSource(Preferences.asLiveData(Preferences::mediaInfoFormat)) { value = true }
addSource(Preferences.asLiveData(Preferences::musicPlayersFilter)) { value = true }
addSource(Preferences.asLiveData(Preferences::appNotificationsFilter)) { value = true }
addSource(Preferences.asLiveData(Preferences::showEventsAsGlanceProvider)) { value = true }
addSource(Preferences.asLiveData(Preferences::installedIntegrations)) { value = true }
}
}

View File

@ -1,4 +1,4 @@
package com.tommasoberlose.anotherwidget.ui.viewmodels
package com.tommasoberlose.anotherwidget.ui.viewmodels.settings
import android.app.Application
import androidx.lifecycle.AndroidViewModel

View File

@ -1,4 +1,4 @@
package com.tommasoberlose.anotherwidget.ui.viewmodels
package com.tommasoberlose.anotherwidget.ui.viewmodels.settings
import android.app.Activity
import android.content.Context

View File

@ -1,4 +1,4 @@
package com.tommasoberlose.anotherwidget.ui.viewmodels
package com.tommasoberlose.anotherwidget.ui.viewmodels.tabs
import android.app.Application
import android.content.Intent

View File

@ -1,11 +1,9 @@
package com.tommasoberlose.anotherwidget.ui.viewmodels
package com.tommasoberlose.anotherwidget.ui.viewmodels.tabs
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 kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

View File

@ -1,4 +1,4 @@
package com.tommasoberlose.anotherwidget.ui.viewmodels
package com.tommasoberlose.anotherwidget.ui.viewmodels.tabs
import android.app.Application
import androidx.lifecycle.AndroidViewModel

View File

@ -1,4 +1,4 @@
package com.tommasoberlose.anotherwidget.ui.viewmodels
package com.tommasoberlose.anotherwidget.ui.viewmodels.tabs
import android.app.Application
import android.content.Intent

View File

@ -1,4 +1,4 @@
package com.tommasoberlose.anotherwidget.ui.viewmodels
package com.tommasoberlose.anotherwidget.ui.viewmodels.tabs
import android.app.Application
import android.content.pm.ApplicationInfo

View File

@ -0,0 +1,11 @@
package com.tommasoberlose.anotherwidget.ui.viewmodels.tabs
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
class MediaInfoFormatViewModel(application: Application) : AndroidViewModel(application) {
val mediaInfoFormatInput: MutableLiveData<String> = MutableLiveData(if (Preferences.mediaInfoFormat == "") MediaPlayerHelper.DEFAULT_MEDIA_INFO_FORMAT else Preferences.mediaInfoFormat)
}

View File

@ -1,11 +1,9 @@
package com.tommasoberlose.anotherwidget.ui.viewmodels
package com.tommasoberlose.anotherwidget.ui.viewmodels.tabs
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

View File

@ -1,4 +1,4 @@
package com.tommasoberlose.anotherwidget.ui.viewmodels
package com.tommasoberlose.anotherwidget.ui.viewmodels.tabs
import android.app.Application
import androidx.lifecycle.AndroidViewModel

View File

@ -15,7 +15,9 @@ import android.os.Bundle
import android.text.format.DateUtils
import android.util.Log
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.RemoteViews
import android.widget.TextView
@ -23,6 +25,7 @@ import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import com.google.gson.Gson
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.TheWidgetBinding
import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.global.Actions
import com.tommasoberlose.anotherwidget.global.Constants
@ -32,9 +35,9 @@ import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toIntValue
import com.tommasoberlose.anotherwidget.helpers.ImageHelper.applyShadow
import com.tommasoberlose.anotherwidget.receivers.*
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.convertDpToPixel
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
import com.tommasoberlose.anotherwidget.utils.toPixel
import kotlinx.android.synthetic.main.the_widget.view.*
import java.text.DateFormat
import java.util.*
import java.util.concurrent.TimeUnit
@ -133,13 +136,13 @@ class MainWidget : AppWidgetProvider() {
// Setup listener
try {
val generatedView = generateWidgetView(context, typeface)
val generatedBinding = generateWidgetView(context, typeface)
views.setImageViewBitmap(
R.id.bitmap_container,
BitmapHelper.getBitmapFromView(generatedView, width = w)
BitmapHelper.getBitmapFromView(generatedBinding.root, width = w)
)
views = updateCalendarView(context, generatedView, views, appWidgetId)
views = updateWeatherView(context, generatedView, views, appWidgetId)
views = updateCalendarView(context, generatedBinding, views, appWidgetId)
views = updateWeatherView(context, generatedBinding, views, appWidgetId)
} catch (ex: Exception) {
ex.printStackTrace()
CrashlyticsReceiver.sendCrash(context, ex)
@ -148,12 +151,12 @@ class MainWidget : AppWidgetProvider() {
appWidgetManager.updateAppWidget(appWidgetId, views)
}
private fun updateCalendarView(context: Context, v: View, views: RemoteViews, widgetID: Int): RemoteViews {
private fun updateCalendarView(context: Context, bindingView: TheWidgetBinding, views: RemoteViews, widgetID: Int): RemoteViews {
val eventRepository = EventRepository(context)
try {
views.setImageViewBitmap(
R.id.empty_date_rect,
BitmapHelper.getBitmapFromView(v.empty_date, draw = false)
BitmapHelper.getBitmapFromView(bindingView.emptyDate, draw = false)
)
views.setViewVisibility(R.id.empty_layout_rect, View.VISIBLE)
@ -172,11 +175,11 @@ class MainWidget : AppWidgetProvider() {
val nextEvent = eventRepository.getNextEvent()
val nextAlarm = AlarmHelper.getNextAlarm(context)
if (Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null) {
if (Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null && !Preferences.showEventsAsGlanceProvider) {
if (Preferences.showNextEvent && eventRepository.getEventsCount() > 1) {
views.setImageViewBitmap(
R.id.action_next_rect,
BitmapHelper.getBitmapFromView(v.action_next, draw = false)
BitmapHelper.getBitmapFromView(bindingView.actionNext, draw = false)
)
views.setViewVisibility(R.id.action_next_rect, View.VISIBLE)
views.setOnClickPendingIntent(
@ -194,7 +197,7 @@ class MainWidget : AppWidgetProvider() {
views.setImageViewBitmap(
R.id.action_previous_rect,
BitmapHelper.getBitmapFromView(v.action_previous, draw = false)
BitmapHelper.getBitmapFromView(bindingView.actionPrevious, draw = false)
)
views.setViewVisibility(R.id.action_previous_rect, View.VISIBLE)
views.setOnClickPendingIntent(
@ -227,7 +230,7 @@ class MainWidget : AppWidgetProvider() {
views.setImageViewBitmap(
R.id.next_event_difference_time_rect,
BitmapHelper.getBitmapFromView(
v.next_event_difference_time,
bindingView.nextEventDifferenceTime,
draw = false
)
)
@ -260,12 +263,12 @@ class MainWidget : AppWidgetProvider() {
views.setImageViewBitmap(
R.id.next_event_rect,
BitmapHelper.getBitmapFromView(v.next_event, draw = false)
BitmapHelper.getBitmapFromView(bindingView.nextEvent, draw = false)
)
views.setImageViewBitmap(
R.id.second_row_rect,
BitmapHelper.getBitmapFromView(v.second_row, draw = false)
BitmapHelper.getBitmapFromView(bindingView.secondRow, draw = false)
)
views.setViewVisibility(R.id.second_row_rect, View.VISIBLE)
@ -284,7 +287,7 @@ class MainWidget : AppWidgetProvider() {
R.id.second_row_top_margin_large_sans,
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.value) View.VISIBLE else View.GONE
)
} else if (GlanceProviderHelper.showGlanceProviders(context) && v.calendar_layout.isVisible) {
} else if (GlanceProviderHelper.showGlanceProviders(context) && bindingView.calendarLayout.isVisible) {
var showSomething = false
loop@ for (provider:Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(context)) {
when (provider) {
@ -378,6 +381,26 @@ class MainWidget : AppWidgetProvider() {
break@loop
}
}
Constants.GlanceProviderId.EVENTS -> {
if (Preferences.showEventsAsGlanceProvider&& Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null) {
val pIntentDetail = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getEventIntent(
context,
nextEvent,
forceEventDetails = true
),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(
R.id.second_row_rect,
pIntentDetail
)
showSomething = true
break@loop
}
}
}
}
@ -385,12 +408,12 @@ class MainWidget : AppWidgetProvider() {
if (showSomething) {
views.setImageViewBitmap(
R.id.next_event_rect,
BitmapHelper.getBitmapFromView(v.next_event, draw = false)
BitmapHelper.getBitmapFromView(bindingView.nextEvent, draw = false)
)
views.setImageViewBitmap(
R.id.second_row_rect,
BitmapHelper.getBitmapFromView(v.second_row, draw = false)
BitmapHelper.getBitmapFromView(bindingView.secondRow, draw = false)
)
views.setViewVisibility(R.id.second_row_rect, View.VISIBLE)
@ -423,7 +446,7 @@ class MainWidget : AppWidgetProvider() {
return views
}
private fun updateWeatherView(context: Context, v: View, views: RemoteViews, widgetID: Int): RemoteViews {
private fun updateWeatherView(context: Context, bindingView: TheWidgetBinding, views: RemoteViews, widgetID: Int): RemoteViews {
try {
if (Preferences.showWeather && Preferences.weatherIcon != "") {
views.setViewVisibility(R.id.weather_rect, View.VISIBLE)
@ -440,17 +463,17 @@ class MainWidget : AppWidgetProvider() {
views.setImageViewBitmap(
R.id.weather_rect,
BitmapHelper.getBitmapFromView(v.weather, draw = false)
BitmapHelper.getBitmapFromView(bindingView.weather, draw = false)
)
views.setImageViewBitmap(
R.id.calendar_weather_rect,
BitmapHelper.getBitmapFromView(v.calendar_weather, draw = false)
BitmapHelper.getBitmapFromView(bindingView.calendarWeather, draw = false)
)
views.setImageViewBitmap(
R.id.special_weather_rect,
BitmapHelper.getBitmapFromView(v.calendar_weather, draw = false)
BitmapHelper.getBitmapFromView(bindingView.calendarWeather, draw = false)
)
if (GlanceProviderHelper.showGlanceProviders(context)) {
@ -530,39 +553,39 @@ class MainWidget : AppWidgetProvider() {
// Generates the widget bitmap from the view
fun generateWidgetView(context: Context, typeface: Typeface? = null): View {
fun generateWidgetView(context: Context, typeface: Typeface? = null): TheWidgetBinding {
val eventRepository = EventRepository(context)
val v = View.inflate(context, R.layout.the_widget, null)
val bindingView = TheWidgetBinding.inflate(LayoutInflater.from(context))
v.loader.isVisible = false
bindingView.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
v.next_event_difference_time.visibility = View.GONE
v.action_next.isVisible = false
v.action_previous.isVisible = false
bindingView.emptyLayout.visibility = View.VISIBLE
bindingView.calendarLayout.visibility = View.GONE
bindingView.nextEventDifferenceTime.visibility = View.GONE
bindingView.actionNext.isVisible = false
bindingView.actionPrevious.isVisible = false
v.empty_date.text = DateHelper.getDateText(context, now)
bindingView.emptyDate.text = DateHelper.getDateText(context, now)
val nextEvent = eventRepository.getNextEvent()
val nextAlarm = AlarmHelper.getNextAlarm(context)
if (Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null) {
if (Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null && !Preferences.showEventsAsGlanceProvider) {
// Multiple counter
v.action_next.isVisible =
bindingView.actionNext.isVisible =
Preferences.showNextEvent && eventRepository.getEventsCount() > 1
v.action_previous.isVisible =
bindingView.actionPrevious.isVisible =
Preferences.showNextEvent && eventRepository.getEventsCount() > 1
v.next_event.text = nextEvent.title
bindingView.nextEvent.text = nextEvent.title
if (Preferences.showDiffTime && now.timeInMillis < nextEvent.startDate) {
v.next_event_difference_time.text = if (!nextEvent.allDay) {
bindingView.nextEventDifferenceTime.text = if (!nextEvent.allDay) {
SettingsStringHelper.getDifferenceText(
context,
now.timeInMillis,
@ -576,24 +599,24 @@ class MainWidget : AppWidgetProvider() {
nextEvent.startDate
).toLowerCase(Locale.getDefault())
}
v.next_event_difference_time.visibility = View.VISIBLE
bindingView.nextEventDifferenceTime.visibility = View.VISIBLE
} else {
v.next_event_difference_time.visibility = View.GONE
bindingView.nextEventDifferenceTime.visibility = View.GONE
}
if (nextEvent.address != "" && Preferences.secondRowInformation == 1) {
v.second_row_icon.setImageDrawable(
bindingView.secondRowIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_place
R.drawable.round_place_24
)
)
v.next_event_date.text = nextEvent.address
bindingView.nextEventDate.text = nextEvent.address
} else {
v.second_row_icon.setImageDrawable(
bindingView.secondRowIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_today
R.drawable.round_today_24
)
)
if (!nextEvent.allDay) {
@ -631,10 +654,10 @@ class MainWidget : AppWidgetProvider() {
}
if (nextEvent.startDate != nextEvent.endDate) {
v.next_event_date.text =
bindingView.nextEventDate.text =
String.format("%s - %s%s", startHour, endHour, multipleDay)
} else {
v.next_event_date.text =
bindingView.nextEventDate.text =
String.format("%s", startHour)
}
@ -643,7 +666,7 @@ class MainWidget : AppWidgetProvider() {
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_ABBREV_MONTH
val start = Calendar.getInstance().apply { timeInMillis = nextEvent.startDate }
v.next_event_date.text = if (now.get(Calendar.DAY_OF_YEAR) == start.get(Calendar.DAY_OF_YEAR)) {
bindingView.nextEventDate.text = if (now.get(Calendar.DAY_OF_YEAR) == start.get(Calendar.DAY_OF_YEAR)) {
DateUtils.formatDateTime(context, nextEvent.startDate, flags)
} else if (now.get(Calendar.DAY_OF_YEAR) > start.get(Calendar.DAY_OF_YEAR) || now.get(Calendar.YEAR) > start.get(Calendar.YEAR)) {
DateUtils.formatDateTime(context, now.timeInMillis, flags)
@ -653,17 +676,17 @@ class MainWidget : AppWidgetProvider() {
}
}
v.empty_layout.visibility = View.GONE
v.calendar_layout.visibility = View.VISIBLE
bindingView.emptyLayout.visibility = View.GONE
bindingView.calendarLayout.visibility = View.VISIBLE
v.second_row_top_margin_small.visibility =
bindingView.secondRowTopMarginSmall.visibility =
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.SMALL.value) View.VISIBLE else View.GONE
v.second_row_top_margin_medium.visibility =
bindingView.secondRowTopMarginMedium.visibility =
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.MEDIUM.value) View.VISIBLE else View.GONE
v.second_row_top_margin_large.visibility =
bindingView.secondRowTopMarginLarge.visibility =
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.value) View.VISIBLE else View.GONE
} else if (GlanceProviderHelper.showGlanceProviders(context)) {
v.second_row_icon.isVisible = true
bindingView.secondRowIcon.isVisible = true
var showSomething = false
loop@ for (provider: Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(
context
@ -671,26 +694,26 @@ class MainWidget : AppWidgetProvider() {
when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> {
if (MediaPlayerHelper.isSomeonePlaying(context)) {
v.second_row_icon.setImageDrawable(
bindingView.secondRowIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_music_note
R.drawable.round_music_note_24
)
)
v.next_event_date.text = MediaPlayerHelper.getMediaInfo()
bindingView.nextEventDate.text = MediaPlayerHelper.getMediaInfo()
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
if (Preferences.showNextAlarm && nextAlarm != "") {
v.second_row_icon.setImageDrawable(
bindingView.secondRowIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_alarm
R.drawable.round_alarm_24
)
)
v.next_event_date.text = AlarmHelper.getNextAlarm(context)
bindingView.nextEventDate.text = AlarmHelper.getNextAlarm(context)
showSomething = true
break@loop
}
@ -699,19 +722,19 @@ class MainWidget : AppWidgetProvider() {
if (Preferences.showBatteryCharging) {
BatteryHelper.updateBatteryInfo(context)
if (Preferences.isCharging) {
v.second_row_icon.isVisible = false
bindingView.secondRowIcon.isVisible = false
val batteryLevel = BatteryHelper.getBatteryLevel(context)
if (batteryLevel != 100) {
v.next_event_date.text = context.getString(R.string.charging)
bindingView.nextEventDate.text = context.getString(R.string.charging)
} else {
v.next_event_date.text =
bindingView.nextEventDate.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 =
bindingView.secondRowIcon.isVisible = false
bindingView.nextEventDate.text =
context.getString(R.string.battery_low_warning)
showSomething = true
break@loop
@ -720,17 +743,17 @@ class MainWidget : AppWidgetProvider() {
}
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.maxLines = 2
bindingView.secondRowIcon.isVisible = false
bindingView.nextEventDate.text = Preferences.customNotes
bindingView.nextEventDate.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 =
bindingView.secondRowIcon.isVisible = false
bindingView.nextEventDate.text =
context.getString(R.string.daily_steps_counter)
.format(Preferences.googleFitSteps)
showSomething = true
@ -744,12 +767,12 @@ class MainWidget : AppWidgetProvider() {
val remotePackageContext = context.createPackageContext(Preferences.lastNotificationPackage, 0)
val icon = ContextCompat.getDrawable(remotePackageContext,
Preferences.lastNotificationIcon)
v.second_row_icon.isVisible = true
v.second_row_icon.setImageDrawable(icon)
bindingView.secondRowIcon.isVisible = true
bindingView.secondRowIcon.setImageDrawable(icon)
} else {
v.second_row_icon.isVisible = false
bindingView.secondRowIcon.isVisible = false
}
v.next_event_date.text = Preferences.lastNotificationTitle
bindingView.nextEventDate.text = Preferences.lastNotificationTitle
showSomething = true
break@loop
} catch (ex: Exception) {}
@ -758,9 +781,38 @@ class MainWidget : AppWidgetProvider() {
Constants.GlanceProviderId.GREETINGS -> {
val greetingsText = GreetingsHelper.getRandomString(context)
if (Preferences.showGreetings && GreetingsHelper.showGreetings() && greetingsText.isNotBlank()) {
v.next_event_date.text = greetingsText
v.next_event_date.maxLines = 2
v.second_row_icon.isVisible = false
bindingView.nextEventDate.text = greetingsText
bindingView.nextEventDate.maxLines = 2
bindingView.secondRowIcon.isVisible = false
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.EVENTS -> {
if (Preferences.showEventsAsGlanceProvider && Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null) {
bindingView.nextEventDate.text = context.getString(R.string.events_glance_provider_format).format(nextEvent.title, if (Preferences.showDiffTime && now.timeInMillis < nextEvent.startDate) {
if (!nextEvent.allDay) {
SettingsStringHelper.getDifferenceText(
context,
now.timeInMillis,
nextEvent.startDate
)
.toLowerCase(Locale.getDefault())
} else {
SettingsStringHelper.getAllDayEventDifferenceText(
context,
now.timeInMillis,
nextEvent.startDate
).toLowerCase(Locale.getDefault())
}
} else "").trimEnd()
bindingView.secondRowIcon.isVisible = true
bindingView.secondRowIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_today_24
)
)
showSomething = true
break@loop
}
@ -769,43 +821,43 @@ class MainWidget : AppWidgetProvider() {
}
if (showSomething) {
v.next_event.text = DateHelper.getDateText(context, now)
v.empty_layout.visibility = View.GONE
v.calendar_layout.visibility = View.VISIBLE
bindingView.nextEvent.text = DateHelper.getDateText(context, now)
bindingView.emptyLayout.visibility = View.GONE
bindingView.calendarLayout.visibility = View.VISIBLE
v.second_row_top_margin_small.visibility =
bindingView.secondRowTopMarginSmall.visibility =
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.SMALL.value) View.VISIBLE else View.GONE
v.second_row_top_margin_medium.visibility =
bindingView.secondRowTopMarginMedium.visibility =
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.MEDIUM.value) View.VISIBLE else View.GONE
v.second_row_top_margin_large.visibility =
bindingView.secondRowTopMarginLarge.visibility =
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.value) View.VISIBLE else View.GONE
} else {
v.second_row_icon.isVisible = false
bindingView.secondRowIcon.isVisible = false
}
}
// Color
listOf<TextView>(
v.empty_date,
v.divider1,
v.temp,
v.next_event,
v.next_event_difference_time,
v.divider3,
v.special_temp
bindingView.emptyDate,
bindingView.divider1,
bindingView.temp,
bindingView.nextEvent,
bindingView.nextEventDifferenceTime,
bindingView.divider3,
bindingView.specialTemp
).forEach {
it.setTextColor(ColorHelper.getFontColor(context.applicationContext.isDarkTheme()))
}
if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.value) {
listOf<ImageView>(v.action_next, v.action_previous)
listOf<ImageView>(bindingView.actionNext, bindingView.actionPrevious)
} else {
listOf<ImageView>(
v.action_next,
v.action_previous,
v.empty_weather_icon,
v.special_weather_icon
bindingView.actionNext,
bindingView.actionPrevious,
bindingView.emptyWeatherIcon,
bindingView.specialWeatherIcon
)
}.forEach {
it.setColorFilter(ColorHelper.getFontColorRgb(context.applicationContext.isDarkTheme()))
@ -815,14 +867,14 @@ class MainWidget : AppWidgetProvider() {
.toFloat()) / 100
}
listOf<TextView>(v.next_event_date, v.divider2, v.calendar_temp).forEach {
listOf<TextView>(bindingView.nextEventDate, bindingView.divider2, bindingView.calendarTemp).forEach {
it.setTextColor(ColorHelper.getSecondaryFontColor(context.applicationContext.isDarkTheme()))
}
if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.value) {
listOf<ImageView>(v.second_row_icon, v.second_row_icon_shadow)
listOf<ImageView>(bindingView.secondRowIcon, bindingView.secondRowIconShadow)
} else {
listOf<ImageView>(v.second_row_icon, v.weather_icon, v.second_row_icon_shadow)
listOf<ImageView>(bindingView.secondRowIcon, bindingView.weatherIcon, bindingView.secondRowIconShadow)
}.forEach {
it.setColorFilter(ColorHelper.getSecondaryFontColorRgb(context.applicationContext.isDarkTheme()))
it.alpha =
@ -833,38 +885,38 @@ class MainWidget : AppWidgetProvider() {
// Text Size
listOf<Pair<TextView, Float>>(
v.empty_date to Preferences.textMainSize,
v.divider1 to (Preferences.textMainSize - 2),
v.temp to Preferences.textMainSize,
v.next_event to Preferences.textMainSize,
v.next_event_difference_time to Preferences.textMainSize,
v.next_event_date to Preferences.textSecondSize,
v.divider2 to (Preferences.textSecondSize - 2),
v.calendar_temp to Preferences.textSecondSize,
v.divider3 to (Preferences.textMainSize - 2),
v.special_temp to Preferences.textMainSize
bindingView.emptyDate to Preferences.textMainSize,
bindingView.divider1 to (Preferences.textMainSize - 2),
bindingView.temp to Preferences.textMainSize,
bindingView.nextEvent to Preferences.textMainSize,
bindingView.nextEventDifferenceTime to Preferences.textMainSize,
bindingView.nextEventDate to Preferences.textSecondSize,
bindingView.divider2 to (Preferences.textSecondSize - 2),
bindingView.calendarTemp to Preferences.textSecondSize,
bindingView.divider3 to (Preferences.textMainSize - 2),
bindingView.specialTemp to Preferences.textMainSize
).forEach {
it.first.setTextSize(TypedValue.COMPLEX_UNIT_SP, it.second)
}
// Icons scale
v.second_row_icon.scaleX = Preferences.textSecondSize / 18f
v.second_row_icon.scaleY = Preferences.textSecondSize / 18f
bindingView.secondRowIcon.scaleX = Preferences.textSecondSize / 18f
bindingView.secondRowIcon.scaleY = Preferences.textSecondSize / 18f
v.weather_icon.scaleX = Preferences.textSecondSize / 14f
v.weather_icon.scaleY = Preferences.textSecondSize / 14f
bindingView.weatherIcon.scaleX = Preferences.textSecondSize / 14f
bindingView.weatherIcon.scaleY = Preferences.textSecondSize / 14f
v.empty_weather_icon.scaleX = Preferences.textMainSize / 18f
v.empty_weather_icon.scaleY = Preferences.textMainSize / 18f
bindingView.emptyWeatherIcon.scaleX = Preferences.textMainSize / 18f
bindingView.emptyWeatherIcon.scaleY = Preferences.textMainSize / 18f
v.action_next.scaleX = Preferences.textMainSize / 28f
v.action_next.scaleY = Preferences.textMainSize / 28f
bindingView.actionNext.scaleX = Preferences.textMainSize / 28f
bindingView.actionNext.scaleY = Preferences.textMainSize / 28f
v.action_previous.scaleX = Preferences.textMainSize / 28f
v.action_previous.scaleY = Preferences.textMainSize / 28f
bindingView.actionPrevious.scaleX = Preferences.textMainSize / 28f
bindingView.actionPrevious.scaleY = Preferences.textMainSize / 28f
v.special_weather_icon.scaleX = Preferences.textMainSize / 18f
v.special_weather_icon.scaleY = Preferences.textMainSize / 18f
bindingView.specialWeatherIcon.scaleX = Preferences.textMainSize / 18f
bindingView.specialWeatherIcon.scaleY = Preferences.textMainSize / 18f
// Shadows
@ -891,16 +943,16 @@ class MainWidget : AppWidgetProvider() {
}
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
bindingView.emptyDate,
bindingView.divider1,
bindingView.temp,
bindingView.nextEvent,
bindingView.nextEventDifferenceTime,
bindingView.nextEventDate,
bindingView.divider2,
bindingView.calendarTemp,
bindingView.divider3,
bindingView.specialTemp
).forEach {
it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor)
}
@ -908,7 +960,7 @@ class MainWidget : AppWidgetProvider() {
// Icons shadow
listOf(
Pair(v.second_row_icon, v.second_row_icon_shadow),
Pair(bindingView.secondRowIcon, bindingView.secondRowIconShadow),
).forEach {
if ((if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) == 0) {
it.second.isVisible = false
@ -921,8 +973,8 @@ class MainWidget : AppWidgetProvider() {
}
listOf(
Pair(v.action_next, v.action_next_shadow),
Pair(v.action_previous, v.action_previous_shadow),
Pair(bindingView.actionNext, bindingView.actionNextShadow),
Pair(bindingView.actionPrevious, bindingView.actionPreviousShadow),
).forEach {
if ((if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) == 0) {
it.second.isVisible = false
@ -934,8 +986,8 @@ class MainWidget : AppWidgetProvider() {
}
}
v.action_previous.scaleX = v.action_previous.scaleX * -1
v.action_previous_shadow.scaleX = v.action_previous_shadow.scaleX * -1
bindingView.actionPrevious.scaleX = bindingView.actionPrevious.scaleX * -1
bindingView.actionPreviousShadow.scaleX = bindingView.actionPreviousShadow.scaleX * -1
// Custom Font
if (Preferences.customFont == Constants.CUSTOM_FONT_GOOGLE_SANS) {
@ -949,31 +1001,31 @@ class MainWidget : AppWidgetProvider() {
}
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
bindingView.emptyDate,
bindingView.divider1,
bindingView.temp,
bindingView.nextEvent,
bindingView.nextEventDifferenceTime,
bindingView.nextEventDate,
bindingView.divider2,
bindingView.calendarTemp,
bindingView.divider3,
bindingView.specialTemp
).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
bindingView.emptyDate,
bindingView.divider1,
bindingView.temp,
bindingView.nextEvent,
bindingView.nextEventDifferenceTime,
bindingView.nextEventDate,
bindingView.divider2,
bindingView.calendarTemp,
bindingView.divider3,
bindingView.specialTemp
).forEach {
it.typeface = typeface
}
@ -981,9 +1033,9 @@ class MainWidget : AppWidgetProvider() {
// Weather
if (Preferences.showWeather && Preferences.weatherIcon != "") {
v.weather.visibility = View.VISIBLE
v.calendar_weather.visibility = View.VISIBLE
v.special_weather.visibility = View.VISIBLE
bindingView.weather.visibility = View.VISIBLE
bindingView.calendarWeather.visibility = View.VISIBLE
bindingView.specialWeather.visibility = View.VISIBLE
val currentTemp = String.format(
Locale.getDefault(),
"%d °%s",
@ -993,41 +1045,44 @@ class MainWidget : AppWidgetProvider() {
val icon: String = Preferences.weatherIcon
if (icon == "") {
v.weather_icon.visibility = View.GONE
v.empty_weather_icon.visibility = View.GONE
v.special_weather_icon.visibility = View.GONE
bindingView.weatherIcon.visibility = View.GONE
bindingView.emptyWeatherIcon.visibility = View.GONE
bindingView.specialWeatherIcon.visibility = View.GONE
} else {
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
bindingView.weatherIcon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon))
bindingView.emptyWeatherIcon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon))
bindingView.specialWeatherIcon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon))
bindingView.weatherIcon.visibility = View.VISIBLE
bindingView.emptyWeatherIcon.visibility = View.VISIBLE
bindingView.specialWeatherIcon.visibility = View.VISIBLE
}
v.temp.text = currentTemp
v.calendar_temp.text = currentTemp
v.special_temp.text = currentTemp
bindingView.temp.text = currentTemp
bindingView.calendarTemp.text = currentTemp
bindingView.specialTemp.text = currentTemp
if (GlanceProviderHelper.showGlanceProviders(context)) {
v.calendar_weather.visibility = View.GONE
bindingView.calendarWeather.visibility = View.GONE
} else {
v.special_weather.visibility = View.GONE
bindingView.specialWeather.visibility = View.GONE
}
} else {
v.weather.visibility = View.GONE
v.calendar_weather.visibility = View.GONE
v.special_weather.visibility = View.GONE
bindingView.weather.visibility = View.GONE
bindingView.calendarWeather.visibility = View.GONE
bindingView.specialWeather.visibility = View.GONE
}
// Dividers
arrayOf(v.divider1, v.divider2, v.divider3).forEach {
it.isVisible = Preferences.showDividers
arrayOf(bindingView.divider1, bindingView.divider2, bindingView.divider3).forEach {
it.visibility = if (Preferences.showDividers) View.VISIBLE else View.INVISIBLE
it.layoutParams = (it.layoutParams as ViewGroup.MarginLayoutParams).apply {
this.marginEnd = if (Preferences.showDividers) 8f.convertDpToPixel(context).toInt() else 0
}
}
eventRepository.close()
return v
return bindingView
}
}
}

View File

@ -2,7 +2,6 @@ package com.tommasoberlose.anotherwidget.utils
import android.animation.*
import android.content.pm.PackageManager
import android.view.Gravity
import android.view.View
import android.view.ViewAnimationUtils
import android.widget.Toast
@ -22,20 +21,11 @@ import android.content.res.Resources
import android.graphics.drawable.Drawable
import android.util.DisplayMetrics
import android.util.TypedValue
import android.view.ViewPropertyAnimator
import android.view.animation.Animation
import android.view.animation.Transformation
import android.widget.LinearLayout
import android.widget.RelativeLayout
import androidx.browser.customtabs.CustomTabColorSchemeParams
import androidx.core.animation.addListener
import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart
import androidx.core.view.isVisible
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
import kotlinx.android.synthetic.main.fragment_app_main.*
import kotlinx.android.synthetic.main.the_widget_sans.*
import java.util.*
@ -148,7 +138,7 @@ fun View.collapse(duration: Long = 500L) {
fun Context.openURI(url: String) {
try {
val builder: CustomTabsIntent.Builder = CustomTabsIntent.Builder()
builder.setToolbarColor(ContextCompat.getColor(this, R.color.colorPrimary))
builder.setDefaultColorSchemeParams(CustomTabColorSchemeParams.Builder().setToolbarColor(ContextCompat.getColor(this, R.color.colorPrimary)).build())
val customTabsIntent: CustomTabsIntent = builder.build()
customTabsIntent.launchUrl(this, Uri.parse(url))
} catch (e: Exception) {
@ -256,4 +246,11 @@ fun Intent.isDefaultSet(context: Context): Boolean {
} catch (ex: java.lang.Exception) {
false
}
}
fun Locale.isMetric(): Boolean {
return when (country.toUpperCase(this)) {
"US", "LR", "MM", "GB" -> false
else -> true
}
}

View File

@ -0,0 +1,26 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="400">
<translate
android:fromYDelta="-20%"
android:toYDelta="0"
android:interpolator="@android:anim/decelerate_interpolator"
/>
<alpha
android:fromAlpha="0"
android:toAlpha="1"
android:interpolator="@android:anim/decelerate_interpolator"
/>
<scale
android:fromXScale="105%"
android:fromYScale="105%"
android:toXScale="100%"
android:toYScale="100%"
android:pivotX="50%"
android:pivotY="50%"
android:interpolator="@android:anim/decelerate_interpolator"
/>
</set>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation
xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/item_animation_fall_down"
android:delay="15%"
android:animationOrder="normal"
/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 751 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 953 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 868 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 465 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 804 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1013 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 439 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

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