Compare commits

...

25 Commits

Author SHA1 Message Date
24bb811f93 Fix update calendar service 2021-01-10 15:54:54 +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
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
6b6ec633ee Update main design 2021-01-05 10:46:12 +01:00
406 changed files with 4997 additions and 4114 deletions

BIN
.idea/caches/build_file_checksums.ser generated Normal file

Binary file not shown.

View File

@ -17,7 +17,6 @@
<package name="" alias="true" withSubpackages="true" /> <package name="" alias="true" withSubpackages="true" />
</value> </value>
</option> </option>
<option name="ALLOW_TRAILING_COMMA" value="true" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>
<codeStyleSettings language="XML"> <codeStyleSettings language="XML">

140
.idea/navEditor.xml generated
View File

@ -46,6 +46,146 @@
</LayoutPositions> </LayoutPositions>
</value> </value>
</entry> </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> </map>
</option> </option>
</component> </component>

View File

@ -6,7 +6,6 @@ apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'realm-android' apply plugin: 'realm-android'
@ -23,8 +22,8 @@ android {
applicationId "com.tommasoberlose.anotherwidget" applicationId "com.tommasoberlose.anotherwidget"
minSdkVersion 23 minSdkVersion 23
targetSdkVersion 30 targetSdkVersion 30
versionCode 115 versionCode 120
versionName "2.1.1" versionName "2.2.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("String", "GOOGLE_API_KEY", apikeyProperties['GOOGLE_API_KEY']) buildConfigField("String", "GOOGLE_API_KEY", apikeyProperties['GOOGLE_API_KEY'])
@ -55,11 +54,11 @@ android {
packagingOptions { packagingOptions {
exclude 'META-INF/DEPENDENCIES' exclude 'META-INF/DEPENDENCIES'
} }
dataBinding {
enabled = true
}
viewBinding.enabled = true buildFeatures {
dataBinding = true
viewBinding = true
}
} }
dependencies { dependencies {
@ -77,6 +76,7 @@ dependencies {
implementation 'androidx.browser:browser:1.3.0' implementation 'androidx.browser:browser:1.3.0'
implementation 'net.idik:slimadapter:2.1.2' implementation 'net.idik:slimadapter:2.1.2'
implementation 'com.google.android:flexbox:2.0.1' implementation 'com.google.android:flexbox:2.0.1'
implementation 'com.kyleduo.switchbutton:library:2.0.3'
// Lifecycle // Lifecycle
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
@ -94,7 +94,7 @@ dependencies {
// Other // Other
implementation 'androidx.multidex:multidex:2.0.1' 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 'me.everything:providers-android:1.0.1'
implementation 'com.github.warkiz.widget:indicatorseekbar:2.1.2' implementation 'com.github.warkiz.widget:indicatorseekbar:2.1.2'

View File

@ -31,15 +31,15 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".ui.activities.ChooseApplicationActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" /> <activity android:name=".ui.activities.tabs.ChooseApplicationActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.CustomFontActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" /> <activity android:name=".ui.activities.tabs.CustomFontActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.CustomLocationActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" /> <activity android:name=".ui.activities.tabs.CustomLocationActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.WeatherProviderActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" /> <activity android:name=".ui.activities.tabs.WeatherProviderActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.SupportDevActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" /> <activity android:name=".ui.activities.settings.SupportDevActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.CustomDateActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" /> <activity android:name=".ui.activities.tabs.CustomDateActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.IntegrationsActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" /> <activity android:name=".ui.activities.settings.IntegrationsActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.MusicPlayersFilterActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" /> <activity android:name=".ui.activities.tabs.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.AppNotificationsFilterActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<receiver android:name=".ui.widgets.MainWidget"> <receiver android:name=".ui.widgets.MainWidget">
@ -150,9 +150,10 @@
</receiver> </receiver>
<service <service
android:name=".services.UpdateCalendarJob" android:name=".services.UpdateCalendarService"
android:permission="android.permission.BIND_JOB_SERVICE" android:enabled="true"
android:exported="true"/> android:exported="false"
android:foregroundServiceType="dataSync" />
<service <service
android:name=".services.LocationService" android:name=".services.LocationService"
android:enabled="true" android:enabled="true"

View File

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

View File

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

View File

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

View File

@ -5,32 +5,33 @@ import android.view.View
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.WeatherProviderSettingsLayoutBinding
import com.tommasoberlose.anotherwidget.global.Constants import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
import com.tommasoberlose.anotherwidget.utils.openURI 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) { class BottomSheetWeatherProviderSettings(context: Context, callback: () -> Unit) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
private var binding: WeatherProviderSettingsLayoutBinding = WeatherProviderSettingsLayoutBinding.inflate(android.view.LayoutInflater.from(context))
init { init {
val view = View.inflate(context, R.layout.weather_provider_settings_layout, null) binding.apiKeyContainer.isVisible = WeatherHelper.isKeyRequired()
view.api_key_container.isVisible = WeatherHelper.isKeyRequired() binding.actionSaveKey.isVisible = WeatherHelper.isKeyRequired()
view.action_save_key.isVisible = WeatherHelper.isKeyRequired()
WeatherHelper.getProviderInfoTitle(context).let { title -> WeatherHelper.getProviderInfoTitle(context).let { title ->
view.info_title.text = title binding.infoTitle.text = title
view.info_title.isVisible = title != "" binding.infoTitle.isVisible = title != ""
} }
WeatherHelper.getProviderInfoSubtitle(context).let { subtitle -> WeatherHelper.getProviderInfoSubtitle(context).let { subtitle ->
view.info_subtitle.text = subtitle binding.infoSubtitle.text = subtitle
view.info_subtitle.isVisible = 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.OPEN_WEATHER -> Preferences.weatherProviderApiOpen
Constants.WeatherProvider.WEATHER_BIT -> Preferences.weatherProviderApiWeatherBit Constants.WeatherProvider.WEATHER_BIT -> Preferences.weatherProviderApiWeatherBit
Constants.WeatherProvider.WEATHER_API -> Preferences.weatherProviderApiWeatherApi Constants.WeatherProvider.WEATHER_API -> Preferences.weatherProviderApiWeatherApi
@ -41,12 +42,12 @@ class BottomSheetWeatherProviderSettings(context: Context, callback: () -> Unit)
null -> "" null -> ""
}) })
view.action_open_provider.setOnClickListener { binding.actionOpenProvider.setOnClickListener {
context.openURI(WeatherHelper.getProviderLink()) context.openURI(WeatherHelper.getProviderLink())
} }
view.action_save_key.setOnClickListener { binding.actionSaveKey.setOnClickListener {
val key = view.api_key.editText?.text.toString() val key = binding.apiKey.editText?.text.toString()
when (Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) { when (Constants.WeatherProvider.fromInt(Preferences.weatherProvider)) {
Constants.WeatherProvider.OPEN_WEATHER -> Preferences.weatherProviderApiOpen = key Constants.WeatherProvider.OPEN_WEATHER -> Preferences.weatherProviderApiOpen = key
Constants.WeatherProvider.WEATHER_BIT -> Preferences.weatherProviderApiWeatherBit = key Constants.WeatherProvider.WEATHER_BIT -> Preferences.weatherProviderApiWeatherBit = key
@ -59,6 +60,6 @@ class BottomSheetWeatherProviderSettings(context: Context, callback: () -> Unit)
dismiss() dismiss()
} }
setContentView(view) setContentView(binding.root)
} }
} }

View File

@ -1,28 +1,30 @@
package com.tommasoberlose.anotherwidget.components package com.tommasoberlose.anotherwidget.components
import android.content.Context import android.content.Context
import android.view.LayoutInflater
import android.view.View import android.view.View
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.CustomNotesDialogLayoutBinding
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import kotlinx.android.synthetic.main.custom_notes_dialog_layout.view.*
class CustomNotesDialog(context: Context, callback: (() -> Unit)?) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) { class CustomNotesDialog(context: Context, callback: (() -> Unit)?) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
init { private var binding: CustomNotesDialogLayoutBinding = CustomNotesDialogLayoutBinding.inflate(LayoutInflater.from(context))
val view = View.inflate(context, R.layout.custom_notes_dialog_layout, null)
view.notes.setText(Preferences.customNotes)
view.action_positive.setOnClickListener { init {
Preferences.customNotes = view.notes.text.toString() binding.notes.setText(Preferences.customNotes)
binding.actionPositive.setOnClickListener {
Preferences.customNotes = binding.notes.text.toString()
this.dismiss() this.dismiss()
callback?.invoke() callback?.invoke()
} }
view.notes.requestFocus() binding.notes.requestFocus()
setContentView(view) setContentView(binding.root)
} }
} }

View File

@ -7,11 +7,10 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.util.EventLog import android.view.LayoutInflater
import android.util.Log
import android.view.View import android.view.View
import androidx.core.app.NotificationManagerCompat
import androidx.core.view.isVisible 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.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.auth.api.signin.GoogleSignInOptions import com.google.android.gms.auth.api.signin.GoogleSignInOptions
@ -22,6 +21,7 @@ import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionRequest import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.multi.MultiplePermissionsListener import com.karumi.dexter.listener.multi.MultiplePermissionsListener
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.GlanceProviderSettingsLayoutBinding
import com.tommasoberlose.anotherwidget.global.Constants import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper
@ -29,21 +29,21 @@ import com.tommasoberlose.anotherwidget.helpers.AlarmHelper
import com.tommasoberlose.anotherwidget.helpers.GreetingsHelper import com.tommasoberlose.anotherwidget.helpers.GreetingsHelper
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver
import com.tommasoberlose.anotherwidget.ui.activities.AppNotificationsFilterActivity import com.tommasoberlose.anotherwidget.ui.activities.tabs.AppNotificationsFilterActivity
import com.tommasoberlose.anotherwidget.ui.activities.MusicPlayersFilterActivity import com.tommasoberlose.anotherwidget.ui.activities.tabs.MusicPlayersFilterActivity
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import kotlinx.android.synthetic.main.glance_provider_settings_layout.view.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
class GlanceSettingsDialog(val context: Activity, val provider: Constants.GlanceProviderId, private val statusCallback: (() -> Unit)?) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) { 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() { override fun show() {
val view = View.inflate(context, R.layout.glance_provider_settings_layout, null)
/* TITLE */ /* 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.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.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) Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> context.getString(R.string.settings_low_battery_level_title)
@ -55,7 +55,7 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
} }
/* SUBTITLE*/ /* 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.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.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) Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> context.getString(R.string.settings_low_battery_level_subtitle)
@ -67,50 +67,50 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
} }
/* SONG */ /* SONG */
view.action_filter_music_players.isVisible = provider == Constants.GlanceProviderId.PLAYING_SONG binding.actionFilterMusicPlayers.isVisible = provider == Constants.GlanceProviderId.PLAYING_SONG
if (provider == Constants.GlanceProviderId.PLAYING_SONG) { if (provider == Constants.GlanceProviderId.PLAYING_SONG) {
view.action_filter_music_players.setOnClickListener { binding.actionFilterMusicPlayers.setOnClickListener {
dismiss() dismiss()
context.startActivityForResult(Intent(context, MusicPlayersFilterActivity::class.java), 0) context.startActivityForResult(Intent(context, MusicPlayersFilterActivity::class.java), 0)
} }
checkNotificationPermission(view) checkNotificationPermission()
} }
/* ALARM */ /* 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) { if (provider == Constants.GlanceProviderId.NEXT_CLOCK_ALARM) {
view.header.text = context.getString(R.string.information_header) binding.header.text = context.getString(R.string.information_header)
view.warning_container.isVisible = false binding.warningContainer.isVisible = false
checkNextAlarm(view) checkNextAlarm()
} }
/* GOOGLE STEPS */ /* 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) { if (provider == Constants.GlanceProviderId.GOOGLE_FIT_STEPS) {
view.warning_container.isVisible = false binding.warningContainer.isVisible = false
checkFitnessPermission(view) checkFitnessPermission()
checkGoogleFitConnection(view) checkGoogleFitConnection()
} }
/* BATTERY INFO */ /* BATTERY INFO */
if (provider == Constants.GlanceProviderId.BATTERY_LEVEL_LOW) { if (provider == Constants.GlanceProviderId.BATTERY_LEVEL_LOW) {
view.warning_container.isVisible = false binding.warningContainer.isVisible = false
view.header.isVisible = false binding.header.isVisible = false
view.divider.isVisible = false binding.divider.isVisible = false
} }
/* NOTIFICATIONS */ /* NOTIFICATIONS */
view.action_filter_notifications_app.isVisible = provider == Constants.GlanceProviderId.NOTIFICATIONS binding.actionFilterNotificationsApp.isVisible = provider == Constants.GlanceProviderId.NOTIFICATIONS
view.action_change_notification_timer.isVisible = provider == Constants.GlanceProviderId.NOTIFICATIONS binding.actionChangeNotificationTimer.isVisible = provider == Constants.GlanceProviderId.NOTIFICATIONS
if (provider == Constants.GlanceProviderId.NOTIFICATIONS) { if (provider == Constants.GlanceProviderId.NOTIFICATIONS) {
checkLastNotificationsPermission(view) checkLastNotificationsPermission()
val stringArray = context.resources.getStringArray(R.array.glance_notifications_timeout) val stringArray = context.resources.getStringArray(R.array.glance_notifications_timeout)
view.action_filter_notifications_app.setOnClickListener { binding.actionFilterNotificationsApp.setOnClickListener {
dismiss() dismiss()
context.startActivityForResult(Intent(context, AppNotificationsFilterActivity::class.java), 0) context.startActivityForResult(Intent(context, AppNotificationsFilterActivity::class.java), 0)
} }
view.notification_timer_label.text = stringArray[Preferences.hideNotificationAfter] binding.notificationTimerLabel.text = stringArray[Preferences.hideNotificationAfter]
view.action_change_notification_timer.setOnClickListener { binding.actionChangeNotificationTimer.setOnClickListener {
val dialog = BottomSheetMenu<Int>(context, header = context.getString(R.string.glance_notification_hide_timeout_title)).setSelectedValue(Preferences.hideNotificationAfter) val dialog = BottomSheetMenu<Int>(context, header = context.getString(R.string.glance_notification_hide_timeout_title)).setSelectedValue(Preferences.hideNotificationAfter)
Constants.GlanceNotificationTimer.values().forEachIndexed { index, timeout -> Constants.GlanceNotificationTimer.values().forEachIndexed { index, timeout ->
dialog.addItem(stringArray[index], timeout.value) dialog.addItem(stringArray[index], timeout.value)
@ -124,20 +124,20 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
/* GREETINGS */ /* GREETINGS */
if (provider == Constants.GlanceProviderId.GREETINGS) { if (provider == Constants.GlanceProviderId.GREETINGS) {
view.warning_container.isVisible = false binding.warningContainer.isVisible = false
view.header.isVisible = false binding.header.isVisible = false
view.divider.isVisible = false binding.divider.isVisible = false
} }
/* EVENTS */ /* EVENTS */
if (provider == Constants.GlanceProviderId.EVENTS) { if (provider == Constants.GlanceProviderId.EVENTS) {
view.header.isVisible = false binding.header.isVisible = false
view.divider.isVisible = false binding.divider.isVisible = false
checkCalendarConfig(view) checkCalendarConfig()
} }
/* TOGGLE */ /* TOGGLE */
view.provider_switch.isChecked = when (provider) { binding.providerSwitch.setCheckedImmediatelyNoEvent(when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> Preferences.showMusic Constants.GlanceProviderId.PLAYING_SONG -> Preferences.showMusic
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> Preferences.showNextAlarm Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> Preferences.showNextAlarm
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> Preferences.showBatteryCharging Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> Preferences.showBatteryCharging
@ -146,11 +146,11 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
Constants.GlanceProviderId.NOTIFICATIONS -> Preferences.showNotifications Constants.GlanceProviderId.NOTIFICATIONS -> Preferences.showNotifications
Constants.GlanceProviderId.GREETINGS -> Preferences.showGreetings Constants.GlanceProviderId.GREETINGS -> Preferences.showGreetings
Constants.GlanceProviderId.EVENTS -> Preferences.showEventsAsGlanceProvider Constants.GlanceProviderId.EVENTS -> Preferences.showEventsAsGlanceProvider
} })
var job: Job? = null var job: Job? = null
view.provider_switch.setOnCheckedChangeListener { _, isChecked -> binding.providerSwitch.setOnCheckedChangeListener { _, isChecked ->
job?.cancel() job?.cancel()
job = GlobalScope.launch(Dispatchers.IO) { job = GlobalScope.launch(Dispatchers.IO) {
delay(300) delay(300)
@ -158,18 +158,18 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
when (provider) { when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> { Constants.GlanceProviderId.PLAYING_SONG -> {
Preferences.showMusic = isChecked Preferences.showMusic = isChecked
checkNotificationPermission(view) checkNotificationPermission()
} }
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> { Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
Preferences.showNextAlarm = isChecked Preferences.showNextAlarm = isChecked
checkNextAlarm(view) checkNextAlarm()
} }
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> { Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
Preferences.showBatteryCharging = isChecked Preferences.showBatteryCharging = isChecked
} }
Constants.GlanceProviderId.NOTIFICATIONS -> { Constants.GlanceProviderId.NOTIFICATIONS -> {
Preferences.showNotifications = isChecked Preferences.showNotifications = isChecked
checkLastNotificationsPermission(view) checkLastNotificationsPermission()
} }
Constants.GlanceProviderId.GREETINGS -> { Constants.GlanceProviderId.GREETINGS -> {
Preferences.showGreetings = isChecked Preferences.showGreetings = isChecked
@ -197,9 +197,9 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
Preferences.showDailySteps = false Preferences.showDailySteps = false
} }
view.warning_container.isVisible = false binding.warningContainer.isVisible = false
checkFitnessPermission(view) checkFitnessPermission()
checkGoogleFitConnection(view) checkGoogleFitConnection()
} }
Constants.GlanceProviderId.EVENTS -> { Constants.GlanceProviderId.EVENTS -> {
Preferences.showEventsAsGlanceProvider = isChecked Preferences.showEventsAsGlanceProvider = isChecked
@ -212,11 +212,11 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
} }
} }
setContentView(view) setContentView(binding.root)
super.show() super.show()
} }
private fun checkNextAlarm(view: View) { private fun checkNextAlarm() {
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) { with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
val alarm = nextAlarmClock val alarm = nextAlarmClock
if (alarm != null && alarm.showIntent != null) { if (alarm != null && alarm.showIntent != null) {
@ -226,69 +226,71 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
} catch (e: Exception) { } catch (e: Exception) {
alarm.showIntent?.creatorPackage ?: "" alarm.showIntent?.creatorPackage ?: ""
} }
view.alarm_set_by_title.text = context.getString(R.string.settings_show_next_alarm_app_title).format(appNameOrPackage) binding.alarmSetByTitle.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) 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)
view.alarm_set_by_title.isVisible = true binding.alarmSetByContainer.isVisible = true
} else { } else {
view.alarm_set_by_title.isVisible = false binding.alarmSetByContainer.isVisible = false
binding.header.isVisible = false
binding.divider.isVisible = false
} }
} }
statusCallback?.invoke() statusCallback?.invoke()
} }
private fun checkCalendarConfig(view: View) { private fun checkCalendarConfig() {
if (!Preferences.showEvents || !context.checkGrantedPermission(Manifest.permission.READ_CALENDAR)) { if (!Preferences.showEvents || !context.checkGrantedPermission(Manifest.permission.READ_CALENDAR)) {
view.warning_container.isVisible = true binding.warningContainer.isVisible = true
view.warning_title.text = context.getString(R.string.settings_show_events_as_glance_provider_error) binding.warningTitle.text = context.getString(R.string.settings_show_events_as_glance_provider_error)
view.warning_container.setOnClickListener { binding.warningContainer.setOnClickListener {
dismiss() dismiss()
EventBus.getDefault().post(MainFragment.ChangeTabEvent(1)) EventBus.getDefault().post(MainFragment.ChangeTabEvent(1))
} }
} else { } else {
view.warning_container.isVisible = false binding.warningContainer.isVisible = false
} }
} }
private fun checkNotificationPermission(view: View) { private fun checkNotificationPermission() {
when { when {
ActiveNotificationsHelper.checkNotificationAccess(context) -> { ActiveNotificationsHelper.checkNotificationAccess(context) -> {
view.warning_container.isVisible = false binding.warningContainer.isVisible = false
MediaPlayerHelper.updatePlayingMediaInfo(context) MediaPlayerHelper.updatePlayingMediaInfo(context)
} }
Preferences.showMusic -> { Preferences.showMusic -> {
view.warning_container.isVisible = true binding.warningContainer.isVisible = true
view.warning_title.text = context.getString(R.string.settings_request_notification_access) binding.warningTitle.text = context.getString(R.string.settings_request_notification_access)
view.warning_container.setOnClickListener { binding.warningContainer.setOnClickListener {
context.startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS")) context.startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"))
} }
} }
else -> { else -> {
view.warning_container.isVisible = false binding.warningContainer.isVisible = false
} }
} }
statusCallback?.invoke() statusCallback?.invoke()
} }
private fun checkLastNotificationsPermission(view: View) { private fun checkLastNotificationsPermission() {
when { when {
ActiveNotificationsHelper.checkNotificationAccess(context) -> { ActiveNotificationsHelper.checkNotificationAccess(context) -> {
view.warning_container.isVisible = false binding.warningContainer.isVisible = false
} }
Preferences.showNotifications -> { Preferences.showNotifications -> {
view.warning_container.isVisible = true binding.warningContainer.isVisible = true
view.warning_title.text = context.getString(R.string.settings_request_last_notification_access) binding.warningTitle.text = context.getString(R.string.settings_request_last_notification_access)
view.warning_container.setOnClickListener { binding.warningContainer.setOnClickListener {
context.startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS")) context.startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"))
} }
} }
else -> { else -> {
view.warning_container.isVisible = false binding.warningContainer.isVisible = false
} }
} }
statusCallback?.invoke() statusCallback?.invoke()
} }
private fun checkFitnessPermission(view: View) { private fun checkFitnessPermission() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || context.checkGrantedPermission( if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || context.checkGrantedPermission(
Manifest.permission.ACTIVITY_RECOGNITION) Manifest.permission.ACTIVITY_RECOGNITION)
) { ) {
@ -299,10 +301,10 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
} }
} else if (Preferences.showDailySteps) { } else if (Preferences.showDailySteps) {
ActivityDetectionReceiver.unregisterFence(context) ActivityDetectionReceiver.unregisterFence(context)
view.warning_container.isVisible = true binding.warningContainer.isVisible = true
view.warning_title.text = context.getString(R.string.settings_request_fitness_access) binding.warningTitle.text = context.getString(R.string.settings_request_fitness_access)
view.warning_container.setOnClickListener { binding.warningContainer.setOnClickListener {
requireFitnessPermission(view) requireFitnessPermission()
} }
} else { } else {
ActivityDetectionReceiver.unregisterFence(context) ActivityDetectionReceiver.unregisterFence(context)
@ -310,36 +312,36 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
statusCallback?.invoke() statusCallback?.invoke()
} }
private fun checkGoogleFitConnection(view: View) { private fun checkGoogleFitConnection() {
val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(context) val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(context)
if (!GoogleSignIn.hasPermissions(account, if (!GoogleSignIn.hasPermissions(account,
ActivityDetectionReceiver.FITNESS_OPTIONS ActivityDetectionReceiver.FITNESS_OPTIONS
)) { )) {
view.warning_container.isVisible = true binding.warningContainer.isVisible = true
view.warning_title.text = context.getString(R.string.settings_request_fitness_access) binding.warningTitle.text = context.getString(R.string.settings_request_fitness_access)
view.warning_container.setOnClickListener { binding.warningContainer.setOnClickListener {
GoogleSignIn.requestPermissions( GoogleSignIn.requestPermissions(
context, context,
1, 1,
account, account,
ActivityDetectionReceiver.FITNESS_OPTIONS) ActivityDetectionReceiver.FITNESS_OPTIONS)
} }
view.action_connect_to_google_fit.isVisible = true binding.actionConnectToGoogleFit.isVisible = true
view.action_disconnect_to_google_fit.isVisible = false binding.actionDisconnectToGoogleFit.isVisible = false
view.action_connect_to_google_fit.setOnClickListener { binding.actionConnectToGoogleFit.setOnClickListener {
GoogleSignIn.requestPermissions( GoogleSignIn.requestPermissions(
context, context,
1, 1,
account, account,
ActivityDetectionReceiver.FITNESS_OPTIONS) ActivityDetectionReceiver.FITNESS_OPTIONS)
} }
view.action_disconnect_to_google_fit.setOnClickListener(null) binding.actionDisconnectToGoogleFit.setOnClickListener(null)
view.google_fit_status_label.text = context.getString(R.string.google_fit_account_not_connected) binding.googleFitStatusLabel.text = context.getString(R.string.google_fit_account_not_connected)
} else { } else {
view.action_connect_to_google_fit.isVisible = false binding.actionConnectToGoogleFit.isVisible = false
view.action_disconnect_to_google_fit.isVisible = true binding.actionDisconnectToGoogleFit.isVisible = true
view.action_connect_to_google_fit.setOnClickListener(null) binding.actionConnectToGoogleFit.setOnClickListener(null)
view.action_disconnect_to_google_fit.setOnClickListener { binding.actionDisconnectToGoogleFit.setOnClickListener {
GoogleSignIn.getClient(context, GoogleSignInOptions.Builder( GoogleSignIn.getClient(context, GoogleSignInOptions.Builder(
GoogleSignInOptions.DEFAULT_SIGN_IN).addExtension( GoogleSignInOptions.DEFAULT_SIGN_IN).addExtension(
ActivityDetectionReceiver.FITNESS_OPTIONS ActivityDetectionReceiver.FITNESS_OPTIONS
@ -347,11 +349,11 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
show() 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) Dexter.withContext(context)
.withPermissions( .withPermissions(
"com.google.android.gms.permission.ACTIVITY_RECOGNITION", "com.google.android.gms.permission.ACTIVITY_RECOGNITION",
@ -359,7 +361,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" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACTIVITY_RECOGNITION else "com.google.android.gms.permission.ACTIVITY_RECOGNITION"
).withListener(object: MultiplePermissionsListener { ).withListener(object: MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) { override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
checkFitnessPermission(view) checkFitnessPermission()
} }
override fun onPermissionRationaleShouldBeShown( override fun onPermissionRationaleShouldBeShown(
permissions: MutableList<PermissionRequest>?, permissions: MutableList<PermissionRequest>?,

View File

@ -1,43 +1,42 @@
package com.tommasoberlose.anotherwidget.components package com.tommasoberlose.anotherwidget.components
import android.content.Context import android.content.Context
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.tommasoberlose.anotherwidget.R 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.Constants
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper 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) { 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() { override fun show() {
val view = View.inflate(context, R.layout.bottom_sheet_menu, null)
// Header // Header
view.header.isVisible = header != null binding.header.isVisible = header != null
view.header_text.text = header ?: "" binding.headerText.text = header ?: ""
view.warning_text.isVisible = false binding.warningText.isVisible = false
// Menu // Menu
for (item in Constants.WeatherIconPack.values()) { for (item in Constants.WeatherIconPack.values()) {
val itemView = View.inflate(context, R.layout.icon_pack_menu_item, null) val itemBinding = IconPackMenuItemBinding.inflate(LayoutInflater.from(context))
itemView.label.text = context.getString(R.string.settings_weather_icon_pack_default).format(item.value + 1) itemBinding.label.text = context.getString(R.string.settings_weather_icon_pack_default).format(item.value + 1)
itemView.isSelected = item.value == Preferences.weatherIconPack itemBinding.root.isSelected = item.value == Preferences.weatherIconPack
itemView.icon_1.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "01d", item.value))) itemBinding.icon1.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "01d", item.value)))
itemView.icon_2.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "01n", item.value))) itemBinding.icon2.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "01n", item.value)))
itemView.icon_3.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "10d", item.value))) itemBinding.icon3.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "10d", item.value)))
itemView.icon_4.setImageDrawable(ContextCompat.getDrawable(context, WeatherHelper.getWeatherIconResource(context, "09n", 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) { if (item == Constants.WeatherIconPack.MINIMAL) {
it.setColorFilter(ContextCompat.getColor(context, R.color.colorPrimaryText)) it.setColorFilter(ContextCompat.getColor(context, R.color.colorPrimaryText))
} else { } else {
@ -45,13 +44,13 @@ class IconPackSelector(context: Context, private val header: String? = null) : B
} }
} }
itemView.setOnClickListener { itemBinding.root.setOnClickListener {
Preferences.weatherIconPack = item.value Preferences.weatherIconPack = item.value
this.dismiss() this.dismiss()
} }
view.menu.addView(itemView) binding.menu.addView(itemBinding.root)
} }
setContentView(view) setContentView(binding.root)
super.show() super.show()
} }
} }

View File

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

View File

@ -111,16 +111,13 @@ object Preferences : KotprefModel() {
// Settings // Settings
var showWallpaper by booleanPref(default = true) 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 showPreview by booleanPref(default = true)
var showXiaomiWarning by booleanPref(default = true) var showXiaomiWarning by booleanPref(default = true)
// Glance // Glance
var showGlance by booleanPref(default = true)
var enabledGlanceProviderOrder by stringPref(default = "") var enabledGlanceProviderOrder by stringPref(default = "")
var customNotes 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 showBatteryCharging by booleanPref(default = false)
var isBatteryLevelLow by booleanPref(default = false) var isBatteryLevelLow by booleanPref(default = false)
var isCharging by booleanPref(default = false) var isCharging by booleanPref(default = false)

View File

@ -1,29 +1,16 @@
package com.tommasoberlose.anotherwidget.helpers package com.tommasoberlose.anotherwidget.helpers
import android.Manifest 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.Context
import android.content.Intent import android.content.Intent
import android.provider.CalendarContract import android.provider.CalendarContract
import android.util.Log
import com.tommasoberlose.anotherwidget.services.EventListenerJob import com.tommasoberlose.anotherwidget.services.EventListenerJob
import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.models.Event import com.tommasoberlose.anotherwidget.models.Event
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver import com.tommasoberlose.anotherwidget.services.UpdateCalendarService
import com.tommasoberlose.anotherwidget.services.UpdateCalendarJob
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import me.everything.providers.android.calendar.CalendarProvider import me.everything.providers.android.calendar.CalendarProvider
import org.greenrobot.eventbus.EventBus
import java.util.* import java.util.*
import kotlin.Comparator
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
/** /**
@ -32,7 +19,7 @@ import kotlin.collections.ArrayList
object CalendarHelper { object CalendarHelper {
fun updateEventList(context: Context) { fun updateEventList(context: Context) {
UpdateCalendarJob.enqueueWork(context, Intent()) UpdateCalendarService.enqueueWork(context)
} }
fun getCalendarList(context: Context): List<me.everything.providers.android.calendar.Calendar> { 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.annotation.SuppressLint
import android.graphics.Color import android.graphics.Color
import android.util.Log
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import kotlin.math.roundToInt import kotlin.math.roundToInt
object ColorHelper { object ColorHelper {
fun getFontColor(isDark: Boolean): Int { fun getFontColor(isDark: Boolean): Int {
return try { return try {
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) { } catch (e: Exception) {
Color.parseColor("#FFFFFFFF") Color.parseColor("#FFFFFFFF")
} }
@ -33,7 +35,10 @@ object ColorHelper {
fun getSecondaryFontColor(isDark: Boolean): Int { fun getSecondaryFontColor(isDark: Boolean): Int {
return try { 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) { } catch (e: Exception) {
Color.parseColor("#FFFFFFFF") Color.parseColor("#FFFFFFFF")
} }
@ -57,7 +62,10 @@ object ColorHelper {
fun getClockFontColor(isDark: Boolean): Int { fun getClockFontColor(isDark: Boolean): Int {
return try { 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) { } catch (e: Exception) {
Color.parseColor("#FFFFFFFF") Color.parseColor("#FFFFFFFF")
} }
@ -81,7 +89,10 @@ object ColorHelper {
fun getBackgroundColor(isDark: Boolean): Int { fun getBackgroundColor(isDark: Boolean): Int {
return try { 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) { } catch (e: Exception) {
Color.parseColor("#00000000") Color.parseColor("#00000000")
} }
@ -123,4 +134,14 @@ object ColorHelper {
val hexValue = this.toInt(16).toDouble() val hexValue = this.toInt(16).toDouble()
return (hexValue * 100 / 255).roundToInt() return (hexValue * 100 / 255).roundToInt()
} }
fun String.isColor(): Boolean {
return try {
Color.parseColor(this)
true
} catch (iae: IllegalArgumentException) {
iae.printStackTrace()
false
}
}
} }

View File

@ -101,7 +101,7 @@ object GlanceProviderHelper {
val eventRepository = EventRepository(context) val eventRepository = EventRepository(context)
BatteryHelper.updateBatteryInfo(context) BatteryHelper.updateBatteryInfo(context)
val showGlance = Preferences.showGlance && (eventRepository.getEventsCount() == 0 || !Preferences.showEvents || Preferences.showEventsAsGlanceProvider) val showGlance = (eventRepository.getEventsCount() == 0 || !Preferences.showEvents || Preferences.showEventsAsGlanceProvider)
&& ( && (
(Preferences.showNotifications && ActiveNotificationsHelper.showLastNotification()) || (Preferences.showNotifications && ActiveNotificationsHelper.showLastNotification()) ||
(Preferences.showNextAlarm && AlarmHelper.getNextAlarm(context) != "") || (Preferences.showNextAlarm && AlarmHelper.getNextAlarm(context) != "") ||

View File

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

View File

@ -63,10 +63,20 @@ object SettingsStringHelper {
} }
fun getVariantLabel(context: Context, variant: String): String = when { 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("100") -> context.getString(R.string.font_100)
variant.contains("200") -> context.getString(R.string.font_200) variant.contains("200") -> context.getString(R.string.font_200)
variant.contains("300") -> context.getString(R.string.font_300) 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("500") -> context.getString(R.string.font_500)
variant.contains("600") -> context.getString(R.string.font_600) variant.contains("600") -> context.getString(R.string.font_600)
variant.contains("700") -> context.getString(R.string.font_700) variant.contains("700") -> context.getString(R.string.font_700)

View File

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

View File

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

View File

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

View File

@ -41,6 +41,7 @@ class NotificationListener : NotificationListenerService() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
Preferences.lastNotificationIcon = sbn.notification.smallIcon.resId Preferences.lastNotificationIcon = sbn.notification.smallIcon.resId
} else { } else {
@Suppress("DEPRECATION")
Preferences.lastNotificationIcon = sbn.notification.icon Preferences.lastNotificationIcon = sbn.notification.icon
} }
} catch (ex: Exception) { } catch (ex: Exception) {

View File

@ -26,7 +26,7 @@ import kotlin.collections.ArrayList
class LocationService : Service() { class LocationService : Service() {
private val jobs: ArrayList<Job> = ArrayList() private var job: Job? = null
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@ -34,13 +34,13 @@ class LocationService : Service() {
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (ActivityCompat.checkSelfPermission( job?.cancel()
this, job = GlobalScope.launch(Dispatchers.IO) {
Manifest.permission.ACCESS_FINE_LOCATION if (ActivityCompat.checkSelfPermission(
) == PackageManager.PERMISSION_GRANTED this@LocationService,
) { Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
jobs += GlobalScope.launch(Dispatchers.IO) { ) {
LocationServices.getFusedLocationProviderClient(this@LocationService).lastLocation.addOnCompleteListener { task -> LocationServices.getFusedLocationProviderClient(this@LocationService).lastLocation.addOnCompleteListener { task ->
val networkApi = WeatherNetworkApi(this@LocationService) val networkApi = WeatherNetworkApi(this@LocationService)
if (task.isSuccessful) { if (task.isSuccessful) {
@ -67,18 +67,17 @@ class LocationService : Service() {
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent()) EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
} }
} }
} else {
stopSelf()
} }
} else {
stopSelf()
} }
return START_STICKY return START_STICKY
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
jobs.forEach { job?.cancel()
it.cancel() job = null
}
} }
companion object { companion object {
@ -116,7 +115,6 @@ class LocationService : Service() {
val builder = NotificationCompat.Builder(this@LocationService, getString(R.string.location_access_notification_channel_id)) val builder = NotificationCompat.Builder(this@LocationService, getString(R.string.location_access_notification_channel_id))
.setSmallIcon(R.drawable.ic_stat_notification) .setSmallIcon(R.drawable.ic_stat_notification)
.setContentTitle(getString(R.string.location_access_notification_title)) .setContentTitle(getString(R.string.location_access_notification_title))
.setStyle(NotificationCompat.BigTextStyle().bigText(getString(R.string.location_access_notification_subtitle)))
.setOngoing(true) .setOngoing(true)
.setColor(ContextCompat.getColor(this@LocationService, R.color.colorAccent)) .setColor(ContextCompat.getColor(this@LocationService, R.color.colorAccent))

View File

@ -1,153 +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)
}
// 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)
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 = 28466
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,93 +1,74 @@
package com.tommasoberlose.anotherwidget.ui.activities package com.tommasoberlose.anotherwidget.ui.activities
import android.Manifest import android.Manifest
import android.animation.ValueAnimator
import android.app.Activity import android.app.Activity
import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetManager
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences 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.os.Bundle
import android.provider.Settings
import android.util.DisplayMetrics
import android.util.Log
import android.util.TypedValue
import android.view.View import android.view.View
import android.widget.RelativeLayout
import androidx.appcompat.app.AppCompatActivity 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.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.Navigation 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.Dexter
import com.karumi.dexter.MultiplePermissionsReport import com.karumi.dexter.MultiplePermissionsReport
import com.karumi.dexter.PermissionToken import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionRequest import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.multi.MultiplePermissionsListener import com.karumi.dexter.listener.multi.MultiplePermissionsListener
import com.tommasoberlose.anotherwidget.R 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.Actions
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.global.RequestCode import com.tommasoberlose.anotherwidget.global.RequestCode
import com.tommasoberlose.anotherwidget.helpers.BitmapHelper import com.tommasoberlose.anotherwidget.ui.activities.tabs.WeatherProviderActivity
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.viewmodels.MainViewModel import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission 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(), SharedPreferences.OnSharedPreferenceChangeListener {
class MainActivity : AppCompatActivity() {
private var mAppWidgetId: Int = -1 private var mAppWidgetId: Int = -1
private lateinit var viewModel: MainViewModel private lateinit var viewModel: MainViewModel
private lateinit var binding: ActivityMainBinding
private val mainNavController: NavController? by lazy { private val mainNavController: NavController? by lazy {
Navigation.findNavController( Navigation.findNavController(
this, this,
R.id.content_fragment R.id.content_fragment
) )
} }
private val settingsNavController: NavController? by lazy {
Navigation.findNavController(
this,
R.id.settings_fragment
)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProvider(this).get(MainViewModel::class.java) viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
binding = ActivityMainBinding.inflate(layoutInflater)
controlExtras(intent) controlExtras(intent)
if (Preferences.showWallpaper) { if (Preferences.showWallpaper) {
requirePermission() requirePermission()
} }
setContentView(binding.root)
} }
override fun onBackPressed() { override fun onBackPressed() {
if (mainNavController?.currentDestination?.id == R.id.appMainFragment) { if (mainNavController?.currentDestination?.id == R.id.appMainFragment) {
if (mAppWidgetId > 0) { if (settingsNavController?.navigateUp() == false) {
addNewWidget() if (mAppWidgetId > 0) {
addNewWidget()
} else {
setResult(Activity.RESULT_OK)
finish()
}
} else { } else {
setResult(Activity.RESULT_OK) viewModel.fragmentScrollY.value = 0
finish()
} }
} else { } else {
super.onBackPressed() super.onBackPressed()
@ -110,8 +91,8 @@ class MainActivity : AppCompatActivity() {
AppWidgetManager.INVALID_APPWIDGET_ID) AppWidgetManager.INVALID_APPWIDGET_ID)
if (mAppWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) { if (mAppWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
action_add_widget.visibility = View.VISIBLE binding.actionAddWidget.visibility = View.VISIBLE
action_add_widget.setOnClickListener { binding.actionAddWidget.setOnClickListener {
addNewWidget() addNewWidget()
} }
} }
@ -153,4 +134,26 @@ class MainActivity : AppCompatActivity() {
}) })
.check() .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 android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.ActivityIntegrationsBinding import com.tommasoberlose.anotherwidget.databinding.ActivityIntegrationsBinding
import com.tommasoberlose.anotherwidget.ui.viewmodels.IntegrationsViewModel import com.tommasoberlose.anotherwidget.ui.viewmodels.settings.IntegrationsViewModel
import kotlinx.android.synthetic.main.activity_integrations.*
import net.idik.lib.slimadapter.SlimAdapter import net.idik.lib.slimadapter.SlimAdapter
class IntegrationsActivity : AppCompatActivity() { class IntegrationsActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter private lateinit var adapter: SlimAdapter
private lateinit var viewModel: IntegrationsViewModel private lateinit var viewModel: IntegrationsViewModel
private lateinit var binding: ActivityIntegrationsBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(IntegrationsViewModel::class.java) 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) val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapter.create() adapter = SlimAdapter.create()
adapter adapter
@ -33,10 +32,12 @@ class IntegrationsActivity : AppCompatActivity() {
.text(R.id.text, getString(R.string.default_name)) .text(R.id.text, getString(R.string.default_name))
} }
.attachTo(list_view) .attachTo(binding.listView)
setupListener() setupListener()
subscribeUi(binding, viewModel) subscribeUi(binding, viewModel)
setContentView(binding.root)
} }
private fun subscribeUi(binding: ActivityIntegrationsBinding, viewModel: IntegrationsViewModel) { private fun subscribeUi(binding: ActivityIntegrationsBinding, viewModel: IntegrationsViewModel) {
@ -45,7 +46,7 @@ class IntegrationsActivity : AppCompatActivity() {
} }
private fun setupListener() { private fun setupListener() {
action_back.setOnClickListener { binding.actionBack.setOnClickListener {
onBackPressed() 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.os.Bundle
import android.util.Log
import android.view.View
import android.widget.TextView import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.android.billingclient.api.* import com.android.billingclient.api.*
import com.android.billingclient.api.BillingClient.BillingResponseCode.OK import com.android.billingclient.api.BillingClient.BillingResponseCode.OK
import com.android.billingclient.api.BillingClient.BillingResponseCode.USER_CANCELED import com.android.billingclient.api.BillingClient.BillingResponseCode.USER_CANCELED
import com.chibatching.kotpref.bulk
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.ActivitySupportDevBinding import com.tommasoberlose.anotherwidget.databinding.ActivitySupportDevBinding
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.ui.viewmodels.settings.SupportDevViewModel
import com.tommasoberlose.anotherwidget.ui.viewmodels.SupportDevViewModel
import com.tommasoberlose.anotherwidget.utils.toast import com.tommasoberlose.anotherwidget.utils.toast
import kotlinx.android.synthetic.main.activity_support_dev.*
import net.idik.lib.slimadapter.SlimAdapter import net.idik.lib.slimadapter.SlimAdapter
class SupportDevActivity : AppCompatActivity(), PurchasesUpdatedListener { class SupportDevActivity : AppCompatActivity(), PurchasesUpdatedListener {
private lateinit var viewModel: SupportDevViewModel private lateinit var viewModel: SupportDevViewModel
private lateinit var adapter: SlimAdapter private lateinit var adapter: SlimAdapter
private lateinit var binding: ActivitySupportDevBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(SupportDevViewModel::class.java) viewModel = ViewModelProvider(this).get(SupportDevViewModel::class.java)
viewModel.billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build() 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) val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapter.create() adapter = SlimAdapter.create()
adapter adapter
@ -62,20 +53,22 @@ class SupportDevActivity : AppCompatActivity(), PurchasesUpdatedListener {
viewModel.purchase(this, item) viewModel.purchase(this, item)
} }
} }
.attachTo(list_view) .attachTo(binding.listView)
viewModel.openConnection() viewModel.openConnection()
subscribeUi(viewModel) subscribeUi(viewModel)
action_back.setOnClickListener { binding.actionBack.setOnClickListener {
onBackPressed() onBackPressed()
} }
setContentView(binding.root)
} }
private fun subscribeUi(viewModel: SupportDevViewModel) { private fun subscribeUi(viewModel: SupportDevViewModel) {
viewModel.products.observe(this, Observer { viewModel.products.observe(this, Observer {
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
loader.isVisible = false binding.loader.isVisible = false
} }
adapter.updateData(it.sortedWith(compareBy(SkuDetails::getPriceAmountMicros))) 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.content.pm.ResolveInfo
import android.os.Bundle import android.os.Bundle
@ -6,7 +6,6 @@ import android.view.View
import android.widget.ImageView import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -16,8 +15,7 @@ import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.ActivityAppNotificationsFilterBinding import com.tommasoberlose.anotherwidget.databinding.ActivityAppNotificationsFilterBinding
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper
import com.tommasoberlose.anotherwidget.ui.viewmodels.AppNotificationsViewModel import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.AppNotificationsViewModel
import kotlinx.android.synthetic.main.activity_app_notifications_filter.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter import net.idik.lib.slimadapter.SlimAdapter
@ -26,16 +24,17 @@ class AppNotificationsFilterActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter private lateinit var adapter: SlimAdapter
private lateinit var viewModel: AppNotificationsViewModel private lateinit var viewModel: AppNotificationsViewModel
private lateinit var binding: ActivityAppNotificationsFilterBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(AppNotificationsViewModel::class.java) 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) val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapter.create() adapter = SlimAdapter.create()
adapter adapter
@ -60,12 +59,14 @@ class AppNotificationsFilterActivity : AppCompatActivity() {
} }
.checked(R.id.checkBox, ActiveNotificationsHelper.isAppAccepted(item.activityInfo.packageName)) .checked(R.id.checkBox, ActiveNotificationsHelper.isAppAccepted(item.activityInfo.packageName))
} }
.attachTo(list_view) .attachTo(binding.listView)
setupListener() setupListener()
subscribeUi(binding, viewModel) subscribeUi(binding, viewModel)
search.requestFocus() binding.search.requestFocus()
setContentView(binding.root)
} }
private var filterJob: Job? = null private var filterJob: Job? = null
@ -76,22 +77,22 @@ class AppNotificationsFilterActivity : AppCompatActivity() {
viewModel.appList.observe(this, Observer { viewModel.appList.observe(this, Observer {
updateList(list = it) updateList(list = it)
loader.visibility = View.INVISIBLE binding.loader.visibility = View.INVISIBLE
}) })
viewModel.searchInput.observe(this, Observer { search -> viewModel.searchInput.observe(this, Observer { search ->
updateList(search = search) updateList(search = search)
clear_search.isVisible = search.isNotBlank() binding.clearSearch.isVisible = search.isNotBlank()
}) })
viewModel.appNotificationsFilter.observe(this, { viewModel.appNotificationsFilter.observe(this, {
updateList() 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) { 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?.cancel()
filterJob = lifecycleScope.launch(Dispatchers.IO) { filterJob = lifecycleScope.launch(Dispatchers.IO) {
if (list != null && list.isNotEmpty()) { if (list != null && list.isNotEmpty()) {
@ -117,22 +118,22 @@ class AppNotificationsFilterActivity : AppCompatActivity() {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
adapter.updateData(filteredList) adapter.updateData(filteredList)
loader.visibility = View.INVISIBLE binding.loader.visibility = View.INVISIBLE
} }
} }
} }
} }
private fun setupListener() { private fun setupListener() {
action_back.setOnClickListener { binding.actionBack.setOnClickListener {
onBackPressed() onBackPressed()
} }
clear_search.setOnClickListener { binding.clearSearch.setOnClickListener {
viewModel.searchInput.value = "" viewModel.searchInput.value = ""
} }
clear_selection.setOnClickListener { binding.clearSelection.setOnClickListener {
Preferences.appNotificationsFilter = "" Preferences.appNotificationsFilter = ""
} }
} }

View File

@ -1,18 +1,15 @@
package com.tommasoberlose.anotherwidget.ui.activities package com.tommasoberlose.anotherwidget.ui.activities.tabs
import android.app.Activity import android.app.Activity
import android.os.Bundle import android.os.Bundle
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import android.content.Intent import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.ResolveInfo import android.content.pm.ResolveInfo
import android.util.Log
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -20,14 +17,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.tommasoberlose.anotherwidget.databinding.ActivityChooseApplicationBinding import com.tommasoberlose.anotherwidget.databinding.ActivityChooseApplicationBinding
import com.tommasoberlose.anotherwidget.global.Constants import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.ui.viewmodels.ChooseApplicationViewModel import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.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 kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter import net.idik.lib.slimadapter.SlimAdapter
import net.idik.lib.slimadapter.SlimAdapterEx import net.idik.lib.slimadapter.SlimAdapterEx
@ -37,16 +27,17 @@ class ChooseApplicationActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter private lateinit var adapter: SlimAdapter
private lateinit var viewModel: ChooseApplicationViewModel private lateinit var viewModel: ChooseApplicationViewModel
private lateinit var binding: ActivityChooseApplicationBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(ChooseApplicationViewModel::class.java) viewModel = ViewModelProvider(this).get(ChooseApplicationViewModel::class.java)
val binding = DataBindingUtil.setContentView<ActivityChooseApplicationBinding>(this, R.layout.activity_choose_application) binding = ActivityChooseApplicationBinding.inflate(layoutInflater)
list_view.setHasFixedSize(true) binding.listView.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this) val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapterEx.create() adapter = SlimAdapterEx.create()
adapter adapter
@ -82,12 +73,14 @@ class ChooseApplicationActivity : AppCompatActivity() {
saveApp(item) saveApp(item)
} }
} }
.attachTo(list_view) .attachTo(binding.listView)
setupListener() setupListener()
subscribeUi(binding, viewModel) subscribeUi(binding, viewModel)
search.requestFocus() binding.search.requestFocus()
setContentView(binding.root)
} }
private var filterJob: Job? = null private var filterJob: Job? = null
@ -96,19 +89,19 @@ class ChooseApplicationActivity : AppCompatActivity() {
binding.viewModel = viewModel binding.viewModel = viewModel
binding.lifecycleOwner = this binding.lifecycleOwner = this
viewModel.appList.observe(this, Observer { viewModel.appList.observe(this) {
updateList(list = it) 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) updateList(search = search)
clear_search.isVisible = search.isNotBlank() binding.clearSearch.isVisible = search.isNotBlank()
}) }
} }
private fun updateList(list: List<ResolveInfo>? = viewModel.appList.value, search: String? = viewModel.searchInput.value) { 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?.cancel()
filterJob = lifecycleScope.launch(Dispatchers.IO) { filterJob = lifecycleScope.launch(Dispatchers.IO) {
if (list != null && list.isNotEmpty()) { if (list != null && list.isNotEmpty()) {
@ -122,18 +115,18 @@ class ChooseApplicationActivity : AppCompatActivity() {
} }
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
adapter.updateData(listOf("Default") + filteredList) adapter.updateData(listOf("Default") + filteredList)
loader.visibility = View.INVISIBLE binding.loader.visibility = View.INVISIBLE
} }
} }
} }
} }
private fun setupListener() { private fun setupListener() {
action_back.setOnClickListener { binding.actionBack.setOnClickListener {
onBackPressed() onBackPressed()
} }
clear_search.setOnClickListener { binding.clearSearch.setOnClickListener {
viewModel.searchInput.value = "" viewModel.searchInput.value = ""
} }
} }

View File

@ -1,31 +1,21 @@
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.os.Bundle
import android.util.Log
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.chibatching.kotpref.blockingBulk import com.chibatching.kotpref.blockingBulk
import com.chibatching.kotpref.bulk
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.ActivityCustomDateBinding import com.tommasoberlose.anotherwidget.databinding.ActivityCustomDateBinding
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.DateHelper import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.CustomDateViewModel
import com.tommasoberlose.anotherwidget.ui.viewmodels.CustomDateViewModel
import com.tommasoberlose.anotherwidget.utils.getCapWordString import com.tommasoberlose.anotherwidget.utils.getCapWordString
import com.tommasoberlose.anotherwidget.utils.openURI import com.tommasoberlose.anotherwidget.utils.openURI
import com.tommasoberlose.anotherwidget.utils.toast 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 kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter import net.idik.lib.slimadapter.SlimAdapter
import java.lang.Exception import java.lang.Exception
@ -36,26 +26,28 @@ class CustomDateActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter private lateinit var adapter: SlimAdapter
private lateinit var viewModel: CustomDateViewModel private lateinit var viewModel: CustomDateViewModel
private lateinit var binding: ActivityCustomDateBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(CustomDateViewModel::class.java) 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) val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapter.create() adapter = SlimAdapter.create()
adapter adapter
.register<String>(R.layout.custom_date_example_item) { item, injector -> .register<String>(R.layout.custom_date_example_item) { item, injector ->
injector injector
.text(R.id.custom_date_example_format, item) .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( adapter.updateData(
listOf( listOf(
@ -66,9 +58,11 @@ class CustomDateActivity : AppCompatActivity() {
setupListener() setupListener()
subscribeUi(binding, viewModel) subscribeUi(binding, viewModel)
date_format.requestFocus() binding.dateFormat.requestFocus()
setContentView(binding.root)
} }
private var formatJob: Job? = null private var formatJob: Job? = null
private fun subscribeUi(binding: ActivityCustomDateBinding, viewModel: CustomDateViewModel) { private fun subscribeUi(binding: ActivityCustomDateBinding, viewModel: CustomDateViewModel) {
@ -79,7 +73,7 @@ class CustomDateActivity : AppCompatActivity() {
formatJob?.cancel() formatJob?.cancel()
formatJob = lifecycleScope.launch(Dispatchers.IO) { formatJob = lifecycleScope.launch(Dispatchers.IO) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
loader.visibility = View.VISIBLE binding.loader.visibility = View.VISIBLE
} }
delay(200) delay(200)
@ -102,8 +96,8 @@ class CustomDateActivity : AppCompatActivity() {
} }
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
loader.visibility = View.INVISIBLE binding.loader.visibility = View.INVISIBLE
date_format_value.text = text binding.dateFormatValue.text = text
} }
} }
@ -123,26 +117,26 @@ class CustomDateActivity : AppCompatActivity() {
private fun updateCapitalizeUi() { private fun updateCapitalizeUi() {
when { when {
viewModel.isDateUppercase.value == true -> { viewModel.isDateUppercase.value == true -> {
action_capitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.round_publish)) binding.actionCapitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.round_publish))
action_capitalize.alpha = 1f binding.actionCapitalize.alpha = 1f
} }
viewModel.isDateCapitalize.value == true -> { viewModel.isDateCapitalize.value == true -> {
action_capitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.ic_capitalize)) binding.actionCapitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.ic_capitalize))
action_capitalize.alpha = 1f binding.actionCapitalize.alpha = 1f
} }
else -> { else -> {
action_capitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.round_publish)) binding.actionCapitalize.setImageDrawable(ContextCompat.getDrawable(this@CustomDateActivity, R.drawable.round_publish))
action_capitalize.alpha = 0.3f binding.actionCapitalize.alpha = 0.3f
} }
} }
} }
private fun setupListener() { private fun setupListener() {
action_back.setOnClickListener { binding.actionBack.setOnClickListener {
onBackPressed() onBackPressed()
} }
action_capitalize.setOnClickListener { binding.actionCapitalize.setOnClickListener {
when { when {
viewModel.isDateUppercase.value == true -> { viewModel.isDateUppercase.value == true -> {
viewModel.isDateCapitalize.value = false 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)) toast(getString(R.string.action_capitalize_the_date))
true true
} }
action_date_format_info.setOnClickListener { binding.actionDateFormatInfo.setOnClickListener {
openURI("https://developer.android.com/reference/java/text/SimpleDateFormat") 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.app.Activity
import android.content.Intent import android.content.Intent
@ -6,6 +6,7 @@ import android.graphics.Typeface
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.HandlerThread import android.os.HandlerThread
import android.util.Log
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
@ -13,28 +14,20 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.provider.FontRequest import androidx.core.provider.FontRequest
import androidx.core.provider.FontsContractCompat import androidx.core.provider.FontsContractCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.chibatching.kotpref.blockingBulk import com.chibatching.kotpref.blockingBulk
import com.google.gson.Gson
import com.koolio.library.Font import com.koolio.library.Font
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
import com.tommasoberlose.anotherwidget.databinding.ActivityCustomFontBinding import com.tommasoberlose.anotherwidget.databinding.ActivityCustomFontBinding
import com.tommasoberlose.anotherwidget.global.Constants import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.DateHelper
import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper
import com.tommasoberlose.anotherwidget.ui.viewmodels.CustomFontViewModel import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.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 kotlinx.coroutines.* import kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter import net.idik.lib.slimadapter.SlimAdapter
import net.idik.lib.slimadapter.diff.DefaultDiffCallback import net.idik.lib.slimadapter.diff.DefaultDiffCallback
@ -44,19 +37,17 @@ class CustomFontActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter private lateinit var adapter: SlimAdapter
private lateinit var viewModel: CustomFontViewModel private lateinit var viewModel: CustomFontViewModel
private lateinit var binding: ActivityCustomFontBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(CustomFontViewModel::class.java) viewModel = ViewModelProvider(this).get(CustomFontViewModel::class.java)
val binding = DataBindingUtil.setContentView<ActivityCustomFontBinding>( binding = ActivityCustomFontBinding.inflate(layoutInflater)
this,
R.layout.activity_custom_font
)
list_view.setHasFixedSize(true) binding.listView.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this) val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapter.create() adapter = SlimAdapter.create()
adapter.enableDiff(object: DefaultDiffCallback() { adapter.enableDiff(object: DefaultDiffCallback() {
@ -86,7 +77,7 @@ class CustomFontActivity : AppCompatActivity() {
injector.clicked(R.id.text) { injector.clicked(R.id.text) {
val dialog = BottomSheetMenu<String>(this, header = item) 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.addItem(SettingsStringHelper.getVariantLabel(this, s), s)
} }
dialog.addOnSelectItemListener { value -> dialog.addOnSelectItemListener { value ->
@ -136,7 +127,7 @@ class CustomFontActivity : AppCompatActivity() {
if (item.fontVariants.isEmpty()) { if (item.fontVariants.isEmpty()) {
dialog.addItem(SettingsStringHelper.getVariantLabel(this, "regular"), -1) dialog.addItem(SettingsStringHelper.getVariantLabel(this, "regular"), -1)
} else { } else {
item.fontVariants.filter { !it.contains("italic") } item.fontVariants
.forEachIndexed { index, s -> .forEachIndexed { index, s ->
dialog.addItem(SettingsStringHelper.getVariantLabel(this, s), index) dialog.addItem(SettingsStringHelper.getVariantLabel(this, s), index)
} }
@ -146,12 +137,14 @@ class CustomFontActivity : AppCompatActivity() {
}.show() }.show()
} }
} }
.attachTo(list_view) .attachTo(binding.listView)
setupListener() setupListener()
subscribeUi(binding, viewModel) subscribeUi(binding, viewModel)
search.requestFocus() binding.search.requestFocus()
setContentView(binding.root)
} }
private var filterJob: Job? = null private var filterJob: Job? = null
@ -162,12 +155,12 @@ class CustomFontActivity : AppCompatActivity() {
viewModel.fontList.observe(this, Observer { viewModel.fontList.observe(this, Observer {
updateList(list = it) updateList(list = it)
loader.visibility = View.INVISIBLE binding.loader.visibility = View.INVISIBLE
}) })
viewModel.searchInput.observe(this, Observer { search -> viewModel.searchInput.observe(this, Observer { search ->
updateList(search = 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, list: ArrayList<Font>? = viewModel.fontList.value,
search: String? = viewModel.searchInput.value search: String? = viewModel.searchInput.value
) { ) {
loader.visibility = View.VISIBLE binding.loader.visibility = View.VISIBLE
filterJob?.cancel() filterJob?.cancel()
filterJob = lifecycleScope.launch(Dispatchers.IO) { filterJob = lifecycleScope.launch(Dispatchers.IO) {
if (list != null && list.isNotEmpty()) { if (list != null && list.isNotEmpty()) {
@ -209,18 +202,18 @@ class CustomFontActivity : AppCompatActivity() {
} }
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
adapter.updateData(filteredList) adapter.updateData(filteredList)
loader.visibility = View.INVISIBLE binding.loader.visibility = View.INVISIBLE
} }
} }
} }
} }
private fun setupListener() { private fun setupListener() {
action_back.setOnClickListener { binding.actionBack.setOnClickListener {
onBackPressed() onBackPressed()
} }
clear_search.setOnClickListener { binding.clearSearch.setOnClickListener {
viewModel.searchInput.value = "" 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.Manifest
import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.location.Address import android.location.Address
import android.location.Geocoder import android.location.Geocoder
import android.os.Build
import android.os.Bundle import android.os.Bundle
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import android.text.Editable
import android.text.TextWatcher
import android.view.View import android.view.View
import android.view.Window
import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.chibatching.kotpref.bulk 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.Dexter
import com.karumi.dexter.MultiplePermissionsReport import com.karumi.dexter.MultiplePermissionsReport
import com.karumi.dexter.PermissionToken import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionRequest import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.multi.MultiplePermissionsListener 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.databinding.ActivityCustomLocationBinding
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.ui.viewmodels.ChooseApplicationViewModel import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.CustomLocationViewModel
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 kotlinx.coroutines.* import kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter import net.idik.lib.slimadapter.SlimAdapter
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.ThreadMode
import org.greenrobot.eventbus.Subscribe
class CustomLocationActivity : AppCompatActivity() { class CustomLocationActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter private lateinit var adapter: SlimAdapter
private lateinit var viewModel: CustomLocationViewModel private lateinit var viewModel: CustomLocationViewModel
private lateinit var binding: ActivityCustomLocationBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(CustomLocationViewModel::class.java) 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) val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapter.create() adapter = SlimAdapter.create()
adapter adapter
@ -74,7 +53,7 @@ class CustomLocationActivity : AppCompatActivity() {
} }
.register<Address>(R.layout.custom_location_item) { item, injector -> .register<Address>(R.layout.custom_location_item) { item, injector ->
injector.text(R.id.text, item.getAddressLine(0)) injector.text(R.id.text, item.getAddressLine(0))
injector.clicked(R.id.text) { injector.clicked(R.id.item) {
Preferences.bulk { Preferences.bulk {
customLocationLat = item.latitude.toString() customLocationLat = item.latitude.toString()
customLocationLon = item.longitude.toString() customLocationLon = item.longitude.toString()
@ -84,7 +63,7 @@ class CustomLocationActivity : AppCompatActivity() {
} }
} }
} }
.attachTo(list_view) .attachTo(binding.listView)
viewModel.addresses.observe(this, Observer { viewModel.addresses.observe(this, Observer {
@ -94,9 +73,11 @@ class CustomLocationActivity : AppCompatActivity() {
setupListener() setupListener()
subscribeUi(binding, viewModel) subscribeUi(binding, viewModel)
location.requestFocus() binding.location.requestFocus()
setContentView(binding.root)
} }
private var searchJob: Job? = null private var searchJob: Job? = null
private fun subscribeUi(binding: ActivityCustomLocationBinding, viewModel: CustomLocationViewModel) { private fun subscribeUi(binding: ActivityCustomLocationBinding, viewModel: CustomLocationViewModel) {
@ -105,11 +86,11 @@ class CustomLocationActivity : AppCompatActivity() {
viewModel.addresses.observe(this, Observer { viewModel.addresses.observe(this, Observer {
adapter.updateData(listOf("Default") + it) adapter.updateData(listOf("Default") + it)
loader.visibility = View.INVISIBLE binding.loader.visibility = View.INVISIBLE
}) })
viewModel.locationInput.observe(this, Observer { location -> viewModel.locationInput.observe(this, Observer { location ->
loader.visibility = View.VISIBLE binding.loader.visibility = View.VISIBLE
searchJob?.cancel() searchJob?.cancel()
searchJob = lifecycleScope.launch(Dispatchers.IO) { searchJob = lifecycleScope.launch(Dispatchers.IO) {
delay(200) delay(200)
@ -125,11 +106,11 @@ class CustomLocationActivity : AppCompatActivity() {
} }
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
viewModel.addresses.value = list viewModel.addresses.value = list
loader.visibility = View.INVISIBLE binding.loader.visibility = View.INVISIBLE
} }
} }
clear_search.isVisible = location.isNotBlank() binding.clearSearch.isVisible = location.isNotBlank()
}) })
} }
@ -164,11 +145,11 @@ class CustomLocationActivity : AppCompatActivity() {
} }
private fun setupListener() { private fun setupListener() {
action_back.setOnClickListener { binding.actionBack.setOnClickListener {
onBackPressed() onBackPressed()
} }
clear_search.setOnClickListener { binding.clearSearch.setOnClickListener {
viewModel.locationInput.value = "" viewModel.locationInput.value = ""
} }
} }

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 android.os.Bundle
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.ResolveInfo import android.content.pm.ResolveInfo
import android.util.Log
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.tommasoberlose.anotherwidget.databinding.ActivityChooseApplicationBinding
import com.tommasoberlose.anotherwidget.databinding.ActivityMusicPlayersFilterBinding import com.tommasoberlose.anotherwidget.databinding.ActivityMusicPlayersFilterBinding
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
import com.tommasoberlose.anotherwidget.ui.viewmodels.ChooseApplicationViewModel import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.MusicPlayersFilterViewModel
import com.tommasoberlose.anotherwidget.ui.viewmodels.MusicPlayersFilterViewModel
import kotlinx.android.synthetic.main.activity_music_players_filter.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter import net.idik.lib.slimadapter.SlimAdapter
import kotlin.Comparator as Comparator1
class MusicPlayersFilterActivity : AppCompatActivity() { class MusicPlayersFilterActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter private lateinit var adapter: SlimAdapter
private lateinit var viewModel: MusicPlayersFilterViewModel private lateinit var viewModel: MusicPlayersFilterViewModel
private lateinit var binding: ActivityMusicPlayersFilterBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(MusicPlayersFilterViewModel::class.java) 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) val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapter.create() adapter = SlimAdapter.create()
adapter adapter
@ -69,12 +59,14 @@ class MusicPlayersFilterActivity : AppCompatActivity() {
} }
.checked(R.id.checkBox, MediaPlayerHelper.isMusicPlayerAccepted(item.activityInfo.packageName)) .checked(R.id.checkBox, MediaPlayerHelper.isMusicPlayerAccepted(item.activityInfo.packageName))
} }
.attachTo(list_view) .attachTo(binding.listView)
setupListener() setupListener()
subscribeUi(binding, viewModel) subscribeUi(binding, viewModel)
search.requestFocus() binding.search.requestFocus()
setContentView(binding.root)
} }
private var filterJob: Job? = null private var filterJob: Job? = null
@ -83,24 +75,24 @@ class MusicPlayersFilterActivity : AppCompatActivity() {
binding.viewModel = viewModel binding.viewModel = viewModel
binding.lifecycleOwner = this binding.lifecycleOwner = this
viewModel.appList.observe(this, Observer { viewModel.appList.observe(this) {
updateList(list = it) 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) updateList(search = search)
clear_search.isVisible = search.isNotBlank() binding.clearSearch.isVisible = search.isNotBlank()
}) }
viewModel.musicPlayersFilter.observe(this, { viewModel.musicPlayersFilter.observe(this) {
updateList() 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) { 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?.cancel()
filterJob = lifecycleScope.launch(Dispatchers.IO) { filterJob = lifecycleScope.launch(Dispatchers.IO) {
if (list != null && list.isNotEmpty()) { if (list != null && list.isNotEmpty()) {
@ -126,22 +118,22 @@ class MusicPlayersFilterActivity : AppCompatActivity() {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
adapter.updateData(filteredList) adapter.updateData(filteredList)
loader.visibility = View.INVISIBLE binding.loader.visibility = View.INVISIBLE
} }
} }
} }
} }
private fun setupListener() { private fun setupListener() {
action_back.setOnClickListener { binding.actionBack.setOnClickListener {
onBackPressed() onBackPressed()
} }
clear_search.setOnClickListener { binding.clearSearch.setOnClickListener {
viewModel.searchInput.value = "" viewModel.searchInput.value = ""
} }
clear_selection.setOnClickListener { binding.clearSelection.setOnClickListener {
Preferences.musicPlayersFilter = "" 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.app.Activity
import android.content.Intent
import android.content.pm.ResolveInfo
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.View import android.view.View
import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
import com.tommasoberlose.anotherwidget.components.BottomSheetWeatherProviderSettings import com.tommasoberlose.anotherwidget.components.BottomSheetWeatherProviderSettings
import com.tommasoberlose.anotherwidget.databinding.ActivityChooseApplicationBinding
import com.tommasoberlose.anotherwidget.databinding.ActivityWeatherProviderBinding import com.tommasoberlose.anotherwidget.databinding.ActivityWeatherProviderBinding
import com.tommasoberlose.anotherwidget.global.Constants import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
import com.tommasoberlose.anotherwidget.ui.viewmodels.ChooseApplicationViewModel import com.tommasoberlose.anotherwidget.ui.viewmodels.tabs.WeatherProviderViewModel
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.ui.viewmodels.WeatherProviderViewModel
import com.tommasoberlose.anotherwidget.utils.collapse
import com.tommasoberlose.anotherwidget.utils.expand
import com.tommasoberlose.anotherwidget.utils.openURI
import com.tommasoberlose.anotherwidget.utils.reveal
import kotlinx.android.synthetic.main.activity_weather_provider.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.idik.lib.slimadapter.SlimAdapter import net.idik.lib.slimadapter.SlimAdapter
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
@ -49,16 +29,17 @@ class WeatherProviderActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter private lateinit var adapter: SlimAdapter
private lateinit var viewModel: WeatherProviderViewModel private lateinit var viewModel: WeatherProviderViewModel
private lateinit var binding: ActivityWeatherProviderBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_weather_provider)
viewModel = ViewModelProvider(this).get(WeatherProviderViewModel::class.java) viewModel = ViewModelProvider(this).get(WeatherProviderViewModel::class.java)
binding = ActivityWeatherProviderBinding.inflate(layoutInflater)
list_view.setHasFixedSize(true) binding.listView.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this) val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager binding.listView.layoutManager = mLayoutManager
adapter = SlimAdapter.create() adapter = SlimAdapter.create()
adapter adapter
@ -74,7 +55,7 @@ class WeatherProviderActivity : AppCompatActivity() {
Preferences.weatherProvider = provider.value Preferences.weatherProvider = provider.value
updateListItem(oldValue) updateListItem(oldValue)
updateListItem() updateListItem()
loader.isVisible = true binding.loader.isVisible = true
lifecycleScope.launch { lifecycleScope.launch {
WeatherHelper.updateWeather(this@WeatherProviderActivity) WeatherHelper.updateWeather(this@WeatherProviderActivity)
@ -89,7 +70,7 @@ class WeatherProviderActivity : AppCompatActivity() {
Preferences.weatherProvider = provider.value Preferences.weatherProvider = provider.value
updateListItem(oldValue) updateListItem(oldValue)
updateListItem() updateListItem()
loader.isVisible = true binding.loader.isVisible = true
lifecycleScope.launch { lifecycleScope.launch {
WeatherHelper.updateWeather(this@WeatherProviderActivity) WeatherHelper.updateWeather(this@WeatherProviderActivity)
@ -112,7 +93,7 @@ class WeatherProviderActivity : AppCompatActivity() {
.clicked(R.id.action_configure) { .clicked(R.id.action_configure) {
BottomSheetWeatherProviderSettings(this) { BottomSheetWeatherProviderSettings(this) {
lifecycleScope.launch { lifecycleScope.launch {
loader.isVisible = true binding.loader.isVisible = true
WeatherHelper.updateWeather(this@WeatherProviderActivity) WeatherHelper.updateWeather(this@WeatherProviderActivity)
} }
}.show() }.show()
@ -130,7 +111,7 @@ class WeatherProviderActivity : AppCompatActivity() {
} }
} }
.image(R.id.action_configure, ContextCompat.getDrawable(this, if (WeatherHelper.isKeyRequired(provider)) R.drawable.round_settings_24 else R.drawable.outline_info_24)) .image(R.id.action_configure, ContextCompat.getDrawable(this, if (WeatherHelper.isKeyRequired(provider)) R.drawable.round_settings_24 else R.drawable.outline_info_24))
}.attachTo(list_view) }.attachTo(binding.listView)
adapter.updateData( adapter.updateData(
Constants.WeatherProvider.values().asList() Constants.WeatherProvider.values().asList()
@ -140,6 +121,8 @@ class WeatherProviderActivity : AppCompatActivity() {
setupListener() setupListener()
subscribeUi(viewModel) subscribeUi(viewModel)
setContentView(binding.root)
} }
private fun subscribeUi(viewModel: WeatherProviderViewModel) { private fun subscribeUi(viewModel: WeatherProviderViewModel) {
@ -161,7 +144,7 @@ class WeatherProviderActivity : AppCompatActivity() {
} }
private fun setupListener() { private fun setupListener() {
action_back.setOnClickListener { binding.actionBack.setOnClickListener {
onBackPressed() onBackPressed()
} }
} }
@ -183,9 +166,9 @@ class WeatherProviderActivity : AppCompatActivity() {
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(ignore: MainFragment.UpdateUiMessageEvent?) { fun onMessageEvent(ignore: MainFragment.UpdateUiMessageEvent?) {
loader.isVisible = Preferences.weatherProviderError == "-" binding.loader.isVisible = Preferences.weatherProviderError == "-"
if (Preferences.weatherProviderError == "" && Preferences.weatherProviderLocationError == "") { 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.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentStateAdapter
import com.tommasoberlose.anotherwidget.ui.fragments.* import com.tommasoberlose.anotherwidget.ui.fragments.tabs.*
class ViewPagerAdapter(fragmentActivity: FragmentActivity) : class ViewPagerAdapter(fragmentActivity: FragmentActivity) :
FragmentStateAdapter(fragmentActivity) { FragmentStateAdapter(fragmentActivity) {
@ -12,11 +12,11 @@ class ViewPagerAdapter(fragmentActivity: FragmentActivity) :
override fun createFragment(position: Int): Fragment { override fun createFragment(position: Int): Fragment {
return when (position) { return when (position) {
1 -> CalendarTabFragment.newInstance() 1 -> CalendarFragment.newInstance()
2 -> WeatherTabFragment.newInstance() 2 -> WeatherFragment.newInstance()
3 -> ClockTabFragment.newInstance() 3 -> ClockFragment.newInstance()
4 -> GlanceTabFragment.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.os.Bundle
import android.provider.Settings import android.provider.Settings
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.util.Log
import android.util.TypedValue import android.util.TypedValue
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.RelativeLayout import android.widget.RelativeLayout
import androidx.core.animation.addListener import androidx.core.animation.addListener
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
@ -22,12 +24,17 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.Navigation 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.badge.BadgeDrawable
import com.google.android.material.card.MaterialCardView
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.MaterialBottomSheetDialog import com.tommasoberlose.anotherwidget.components.MaterialBottomSheetDialog
import com.tommasoberlose.anotherwidget.databinding.FragmentAppMainBinding
import com.tommasoberlose.anotherwidget.global.Constants import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.* import com.tommasoberlose.anotherwidget.helpers.*
@ -38,14 +45,12 @@ import com.tommasoberlose.anotherwidget.ui.adapters.ViewPagerAdapter
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.* 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 kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeListener { class MainFragment : Fragment() {
companion object { companion object {
fun newInstance() = MainFragment() fun newInstance() = MainFragment()
@ -53,12 +58,13 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
} }
private lateinit var viewModel: MainViewModel private lateinit var viewModel: MainViewModel
private lateinit var binding: FragmentAppMainBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) enterTransition = MaterialSharedAxis(MaterialSharedAxis.Y, true)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Y, false)
} }
override fun onCreateView( override fun onCreateView(
@ -66,41 +72,7 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java) viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
return inflater.inflate(R.layout.fragment_app_main, container, false) binding = FragmentAppMainBinding.inflate(inflater)
}
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)
// Warnings // Warnings
if (getString(R.string.xiaomi_manufacturer).equals(Build.MANUFACTURER, ignoreCase = true) && Preferences.showXiaomiWarning) { if (getString(R.string.xiaomi_manufacturer).equals(Build.MANUFACTURER, ignoreCase = true) && Preferences.showXiaomiWarning) {
@ -117,6 +89,92 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
} }
.show() .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 private var uiJob: Job? = null
@ -124,286 +182,157 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
private fun updateUI() { private fun updateUI() {
uiJob?.cancel() uiJob?.cancel()
preview?.clearAnimation()
time_container?.clearAnimation()
if (Preferences.showPreview) { if (Preferences.showPreview) {
preview?.setCardBackgroundColor( lifecycleScope.launch(Dispatchers.IO) {
ContextCompat.getColor( val bgColor: Int = ContextCompat.getColor(
requireContext(), requireContext(),
if (ColorHelper.getFontColor(activity?.isDarkTheme() == true) if (ColorHelper.getFontColor(requireActivity().isDarkTheme())
.isColorDark() .isColorDark()
) android.R.color.white else R.color.colorAccent ) android.R.color.white else R.color.colorAccent
) )
)
widget_shape_background?.setImageDrawable( val wallpaperDrawable = BitmapHelper.getTintedDrawable(
BitmapHelper.getTintedDrawable(
requireContext(), requireContext(),
R.drawable.card_background, 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 -> WidgetHelper.runWithCustomTypeface(requireContext()) { typeface ->
uiJob = lifecycleScope.launch(Dispatchers.IO) { uiJob = lifecycleScope.launch(Dispatchers.IO) {
val generatedView = MainWidget.generateWidgetView(requireContext(), typeface) val generatedView = MainWidget.generateWidgetView(requireContext(), typeface).root
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
generatedView.measure(0, 0) generatedView.measure(0, 0)
preview?.measure(0, 0) binding.preview.measure(0, 0)
} }
val bitmap = if (preview != null) { val bitmap = BitmapHelper.getBitmapFromView(
BitmapHelper.getBitmapFromView( generatedView,
generatedView, if (binding.preview.width > 0) binding.preview.width else generatedView.measuredWidth,
if (preview.width > 0) preview.width else generatedView.measuredWidth, generatedView.measuredHeight
generatedView.measuredHeight )
)
} else {
null
}
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
// Clock binding.widgetDetail.bitmapContainer.apply {
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 {
setImageBitmap(bitmap) 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) { private fun updateClockVisibility(showClock: Boolean) {
viewModel.showWallpaper.observe(viewLifecycleOwner, Observer { binding.widgetDetail.timeContainer.clearAnimation()
activity?.let { act -> binding.widgetDetail.time.clearAnimation()
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 {
val metrics = DisplayMetrics() updatePreviewVisibility()
act.windowManager.defaultDisplay.getMetrics(metrics)
val dimensions: Pair<Int, Int> = if (wallpaper.intrinsicWidth >= wallpaper.intrinsicHeight) { if (showClock) {
metrics.heightPixels to (wallpaper.intrinsicWidth) * metrics.heightPixels / (wallpaper.intrinsicHeight) binding.widgetDetail.timeContainer.layoutParams = binding.widgetDetail.timeContainer.layoutParams.apply {
} else { height = RelativeLayout.LayoutParams.WRAP_CONTENT
metrics.widthPixels to (wallpaper.intrinsicHeight) * metrics.widthPixels / (wallpaper.intrinsicWidth) }
} binding.widgetDetail.timeContainer.measure(0, 0)
}
setMargins( if ((Preferences.showClock && binding.widgetDetail.time.alpha != 1f) || (!Preferences.showClock && binding.widgetDetail.time.alpha != 0f)) {
if (dimensions.first >= dimensions.second) (-80).toPixel(requireContext()) else 0, val initialHeight = binding.widgetDetail.timeContainer.measuredHeight
(-80).toPixel(requireContext()), 0, 0 ValueAnimator.ofFloat(
) if (showClock) 0f else 1f,
if (showClock) 1f else 0f
width = dimensions.first ).apply {
height = dimensions.second duration = 300L
addUpdateListener {
val animatedValue = animatedValue as Float
binding.widgetDetail.timeContainer.layoutParams =
binding.widgetDetail.timeContainer.layoutParams.apply {
height = (initialHeight * animatedValue).toInt()
} }
binding.widgetDetail.time.alpha = animatedValue
} }
} }.start()
})
logo.setOnClickListener {
// startActivity(Intent(this, SupportDevActivity::class.java))
}
action_settings.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_appMainFragment_to_appSettingsFragment)
} }
} }
private fun showErrorBadge() { private fun updatePreviewVisibility() {
// Calendar error indicator binding.preview.clearAnimation()
tabs?.getTabAt(1)?.orCreateBadge?.apply { if (binding.preview.layoutParams.height != (if (Preferences.showPreview) PREVIEW_BASE_HEIGHT.toPixel(requireContext()) else 0) + (if (Preferences.showClock) 100.toPixel(
backgroundColor = ContextCompat.getColor(requireContext(), R.color.errorColorText) requireContext()
badgeGravity = BadgeDrawable.TOP_END ) else 0)) {
}?.isVisible = Preferences.showEvents && activity?.checkGrantedPermission(Manifest.permission.READ_CALENDAR) != true ValueAnimator.ofInt(
binding.preview.height,
// Weather error indicator (if (Preferences.showPreview) PREVIEW_BASE_HEIGHT.toPixel(requireContext()) else 0) + (if (Preferences.showClock) 100.toPixel(
tabs?.getTabAt(2)?.orCreateBadge?.apply { requireContext()
backgroundColor = ContextCompat.getColor(requireContext(), R.color.errorColorText) ) else 0)
badgeGravity = BadgeDrawable.TOP_END ).apply {
}?.isVisible = if (Preferences.showWeather) { duration = 300L
(WeatherHelper.isKeyRequired() && WeatherHelper.getApiKey() == "") addUpdateListener {
|| (Preferences.customLocationAdd == "" && activity?.checkGrantedPermission( val animatedValue = animatedValue as Int
Manifest.permission.ACCESS_FINE_LOCATION val layoutParams = binding.preview.layoutParams
) != true) layoutParams.height = animatedValue
|| (Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-") binding.preview.layoutParams = layoutParams
|| (Preferences.weatherProviderLocationError != "") }
} else { }.start()
false
} }
// 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())) ||
(Preferences.showEventsAsGlanceProvider && (!Preferences.showEvents || !requireContext().checkGrantedPermission(Manifest.permission.READ_CALENDAR)))
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
Preferences.preferences.registerOnSharedPreferenceChangeListener(this)
EventBus.getDefault().register(this) EventBus.getDefault().register(this)
showErrorBadge()
updateUI() updateUI()
} }
override fun onPause() { override fun onPause() {
Preferences.preferences.unregisterOnSharedPreferenceChangeListener(this)
EventBus.getDefault().unregister(this) EventBus.getDefault().unregister(this)
super.onPause() super.onPause()
} }
private var delayJob: Job? = null 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 UpdateUiMessageEvent
class ChangeTabEvent(val page: Int) class ChangeTabEvent(val page: Int)
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(ignore: UpdateUiMessageEvent?) { fun onUpdateUiEvent(ignore: UpdateUiMessageEvent?) {
delayJob?.cancel() delayJob?.cancel()
delayJob = lifecycleScope.launch(Dispatchers.IO) { delayJob = lifecycleScope.launch(Dispatchers.IO) {
delay(200) delay(300)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
updateUI() updateUI()
} }
@ -411,9 +340,8 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
} }
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(event: ChangeTabEvent?) { fun onChangeTabEvent(ignore: ChangeTabEvent) {
event?.let { val navHost = childFragmentManager.findFragmentById(R.id.settings_fragment) as? NavHostFragment?
pager.setCurrentItem(event.page, true) navHost?.navController?.navigateUp()
}
} }
} }

View File

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

View File

@ -1,323 +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 {
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() {
if (requireActivity().checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
location_permission_alert?.isVisible = false
WeatherReceiver.setUpdates(requireContext())
} else if (Preferences.showWeather && Preferences.customLocationAdd == "") {
location_permission_alert?.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(
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,312 @@
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())
}
}
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) {
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.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 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) {
binding.scrollView.isScrollable = false
callback.invoke()
lifecycleScope.launch {
delay(200)
binding.scrollView.isScrollable = true
}
}
}

View File

@ -0,0 +1,192 @@
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.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.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 colors: IntArray
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 {
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) {
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 {
Preferences.clockAppName != "" -> Preferences.clockAppName
else -> {
if (IntentHelper.getClockIntent(requireContext()).isDefaultSet(requireContext())) {
getString(
R.string.default_clock_app
)
} else {
getString(R.string.nothing)
}
}
}
}
}
viewModel.weatherAppName.observe(viewLifecycleOwner) {
maintainScrollPosition {
binding.weatherAppLabel.text =
if (it != "") 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), RequestCode.CALENDAR_APP_REQUEST_CODE.code)
}
binding.actionClockApp.setOnClickListener {
startActivityForResult(
Intent(requireContext(), ChooseApplicationActivity::class.java),
RequestCode.CLOCK_APP_REQUEST_CODE.code
)
}
binding.actionWeatherApp.setOnClickListener {
startActivityForResult(
Intent(requireContext(), ChooseApplicationActivity::class.java),
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
}
}
}

View File

@ -1,4 +1,4 @@
package com.tommasoberlose.anotherwidget.ui.fragments package com.tommasoberlose.anotherwidget.ui.fragments.tabs
import android.Manifest import android.Manifest
import android.app.Activity import android.app.Activity
@ -10,16 +10,16 @@ import android.content.IntentFilter
import android.graphics.Canvas import android.graphics.Canvas
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.AnimationUtils
import android.view.animation.LayoutAnimationController
import android.widget.ImageView import android.widget.ImageView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.databinding.DataBindingUtil import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
@ -29,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.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.common.api.ApiException import com.google.android.gms.common.api.ApiException
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import com.google.android.material.transition.MaterialSharedAxis
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.CustomNotesDialog import com.tommasoberlose.anotherwidget.components.CustomNotesDialog
import com.tommasoberlose.anotherwidget.components.GlanceSettingsDialog 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.Constants
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper
@ -44,11 +45,11 @@ import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver
import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver.Companion.FITNESS_OPTIONS import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver.Companion.FITNESS_OPTIONS
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission import com.tommasoberlose.anotherwidget.utils.*
import com.tommasoberlose.anotherwidget.utils.convertDpToPixel import kotlinx.coroutines.Dispatchers
import kotlinx.android.synthetic.main.fragment_glance_settings.*
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.idik.lib.slimadapter.SlimAdapter import net.idik.lib.slimadapter.SlimAdapter
@ -61,10 +62,15 @@ class GlanceTabFragment : Fragment() {
private var dialog: GlanceSettingsDialog? = null private var dialog: GlanceSettingsDialog? = null
private lateinit var adapter: SlimAdapter private lateinit var adapter: SlimAdapter
private lateinit var viewModel: MainViewModel private lateinit var viewModel: MainViewModel
private lateinit var list: ArrayList<Constants.GlanceProviderId> private val list: ArrayList<Constants.GlanceProviderId> by lazy {
GlanceProviderHelper.getGlanceProviders(requireContext())
}
private lateinit var binding: FragmentTabGlanceBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
} }
override fun onCreateView( override fun onCreateView(
@ -73,18 +79,11 @@ class GlanceTabFragment : Fragment() {
): View { ): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java) viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
val binding = DataBindingUtil.inflate<FragmentGlanceSettingsBinding>(inflater, binding = FragmentTabGlanceBinding.inflate(inflater)
R.layout.fragment_glance_settings,
container,
false)
subscribeUi(binding, viewModel)
binding.lifecycleOwner = this binding.lifecycleOwner = this
binding.viewModel = viewModel binding.viewModel = viewModel
list = GlanceProviderHelper.getGlanceProviders(requireContext())
return binding.root return binding.root
} }
@ -92,9 +91,10 @@ class GlanceTabFragment : Fragment() {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
// List // List
providers_list.setHasFixedSize(true) binding.providersList.hasFixedSize()
binding.providersList.isNestedScrollingEnabled = false
val mLayoutManager = LinearLayoutManager(context) val mLayoutManager = LinearLayoutManager(context)
providers_list.layoutManager = mLayoutManager binding.providersList.layoutManager = mLayoutManager
adapter = SlimAdapter.create() adapter = SlimAdapter.create()
adapter adapter
@ -106,20 +106,18 @@ class GlanceTabFragment : Fragment() {
it.setImageDrawable(ContextCompat.getDrawable(requireContext(), item.icon)) it.setImageDrawable(ContextCompat.getDrawable(requireContext(), item.icon))
} }
.clicked(R.id.item) { .clicked(R.id.item) {
if (Preferences.showGlance) { if (provider == Constants.GlanceProviderId.CUSTOM_INFO) {
if (provider == Constants.GlanceProviderId.CUSTOM_INFO) { CustomNotesDialog(requireContext()){
CustomNotesDialog(requireContext()){ adapter.notifyItemRangeChanged(0, adapter.data.size)
adapter.notifyItemRangeChanged(0, adapter.data.size) }.show()
}.show() } else {
} else { dialog = GlanceSettingsDialog(requireActivity(), provider) {
dialog = GlanceSettingsDialog(requireActivity(), provider) { adapter.notifyItemRangeChanged(0, adapter.data.size)
adapter.notifyItemRangeChanged(0, adapter.data.size)
}
dialog?.setOnDismissListener {
dialog = null
}
dialog?.show()
} }
dialog?.setOnDismissListener {
dialog = null
}
dialog?.show()
} }
} }
var isVisible = false var isVisible = false
@ -130,9 +128,12 @@ class GlanceTabFragment : Fragment() {
MediaPlayerHelper.updatePlayingMediaInfo(requireContext()) MediaPlayerHelper.updatePlayingMediaInfo(requireContext())
injector.visibility(R.id.error_icon, View.GONE) injector.visibility(R.id.error_icon, View.GONE)
injector.visibility(R.id.info_icon, View.VISIBLE) 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( if (Preferences.showMusic) getString(R.string.settings_visible) else getString(
R.string.settings_not_visible)) R.string.settings_not_visible
)
)
isVisible = Preferences.showMusic isVisible = Preferences.showMusic
} }
Preferences.showMusic -> { Preferences.showMusic -> {
@ -150,26 +151,40 @@ class GlanceTabFragment : Fragment() {
} }
} }
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> { Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
injector.text(R.id.label, injector.text(
R.id.label,
if (Preferences.showNextAlarm && !AlarmHelper.isAlarmProbablyWrong( if (Preferences.showNextAlarm && !AlarmHelper.isAlarmProbablyWrong(
requireContext()) requireContext()
)
) getString(R.string.settings_visible) else getString( ) getString(R.string.settings_visible) else getString(
R.string.settings_not_visible)) R.string.settings_not_visible
injector.visibility(R.id.error_icon, )
)
injector.visibility(
R.id.error_icon,
if (Preferences.showNextAlarm && AlarmHelper.isAlarmProbablyWrong( if (Preferences.showNextAlarm && AlarmHelper.isAlarmProbablyWrong(
requireContext()) requireContext()
) View.VISIBLE else View.GONE) )
injector.visibility(R.id.info_icon, ) View.VISIBLE else View.GONE
)
injector.visibility(
R.id.info_icon,
if (!(Preferences.showNextAlarm && AlarmHelper.isAlarmProbablyWrong( if (!(Preferences.showNextAlarm && AlarmHelper.isAlarmProbablyWrong(
requireContext())) requireContext()
) View.VISIBLE else View.GONE) ))
isVisible = !(Preferences.showNextAlarm && AlarmHelper.isAlarmProbablyWrong( ) View.VISIBLE else View.GONE
requireContext())) )
isVisible = (Preferences.showNextAlarm && !AlarmHelper.isAlarmProbablyWrong(
requireContext()
))
} }
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> { 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( 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.error_icon, View.GONE)
injector.visibility(R.id.info_icon, View.VISIBLE) injector.visibility(R.id.info_icon, View.VISIBLE)
isVisible = Preferences.showBatteryCharging isVisible = Preferences.showBatteryCharging
@ -179,9 +194,12 @@ class GlanceTabFragment : Fragment() {
ActiveNotificationsHelper.checkNotificationAccess(requireContext()) -> { ActiveNotificationsHelper.checkNotificationAccess(requireContext()) -> {
injector.visibility(R.id.error_icon, View.GONE) injector.visibility(R.id.error_icon, View.GONE)
injector.visibility(R.id.info_icon, View.VISIBLE) injector.visibility(R.id.info_icon, View.VISIBLE)
injector.text(R.id.label, injector.text(
R.id.label,
if (Preferences.showNotifications) getString( 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 isVisible = Preferences.showNotifications
} }
Preferences.showNotifications -> { Preferences.showNotifications -> {
@ -199,29 +217,44 @@ class GlanceTabFragment : Fragment() {
} }
} }
Constants.GlanceProviderId.GREETINGS -> { Constants.GlanceProviderId.GREETINGS -> {
injector.text(R.id.label, injector.text(
R.id.label,
if (Preferences.showGreetings) getString(R.string.settings_visible) else getString( 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.error_icon, View.GONE)
injector.visibility(R.id.info_icon, View.VISIBLE) injector.visibility(R.id.info_icon, View.VISIBLE)
isVisible = Preferences.showGreetings isVisible = Preferences.showGreetings
} }
Constants.GlanceProviderId.CUSTOM_INFO -> { Constants.GlanceProviderId.CUSTOM_INFO -> {
injector.text(R.id.label, injector.text(
R.id.label,
if (Preferences.customNotes != "") getString(R.string.settings_visible) else getString( 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.error_icon, View.GONE)
injector.visibility(R.id.info_icon, View.VISIBLE) injector.visibility(R.id.info_icon, View.VISIBLE)
isVisible = Preferences.customNotes != "" isVisible = Preferences.customNotes != ""
} }
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> { Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(context) val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(
if (GoogleSignIn.hasPermissions(account, FITNESS_OPTIONS) && (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || activity?.checkGrantedPermission( context
Manifest.permission.ACTIVITY_RECOGNITION) == true) )
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( 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.error_icon, View.GONE)
injector.visibility(R.id.info_icon, View.VISIBLE) injector.visibility(R.id.info_icon, View.VISIBLE)
isVisible = Preferences.showDailySteps isVisible = Preferences.showDailySteps
@ -240,12 +273,25 @@ class GlanceTabFragment : Fragment() {
} }
} }
Constants.GlanceProviderId.EVENTS -> { Constants.GlanceProviderId.EVENTS -> {
isVisible = Preferences.showEventsAsGlanceProvider && Preferences.showEvents && requireContext().checkGrantedPermission(Manifest.permission.READ_CALENDAR) isVisible =
injector.text(R.id.label, Preferences.showEventsAsGlanceProvider
if (isVisible) getString(R.string.settings_visible) else getString( val hasError = !Preferences.showEvents || !requireContext().checkGrantedPermission(
R.string.settings_not_visible)) Manifest.permission.READ_CALENDAR
injector.visibility(R.id.error_icon, if (isVisible) View.GONE else View.VISIBLE) )
injector.visibility(R.id.info_icon, if (isVisible) View.VISIBLE else View.GONE) 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
)
} }
} }
@ -253,7 +299,7 @@ class GlanceTabFragment : Fragment() {
injector.alpha(R.id.label, 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) injector.alpha(R.id.icon, if (isVisible) 1f else .25f)
} }
.attachTo(providers_list) .attachTo(binding.providersList)
val mIth = ItemTouchHelper( val mIth = ItemTouchHelper(
object : ItemTouchHelper.SimpleCallback( object : ItemTouchHelper.SimpleCallback(
@ -300,7 +346,12 @@ class GlanceTabFragment : Fragment() {
GlanceProviderHelper.saveGlanceProviderOrder( GlanceProviderHelper.saveGlanceProviderOrder(
list list
) )
adapter.updateData(list.mapNotNull { GlanceProviderHelper.getGlanceProviderById(requireContext(), it) }) adapter.updateData(list.mapNotNull {
GlanceProviderHelper.getGlanceProviderById(
requireContext(),
it
)
})
} }
override fun onChildDraw( override fun onChildDraw(
@ -314,24 +365,38 @@ class GlanceTabFragment : Fragment() {
) { ) {
val view = viewHolder.itemView as MaterialCardView val view = viewHolder.itemView as MaterialCardView
if (isCurrentlyActive) { if (isCurrentlyActive) {
ViewCompat.setElevation(view, 2f.convertDpToPixel(requireContext())) ViewCompat.setElevation(view, 8f.convertDpToPixel(requireContext()))
view.setCardBackgroundColor(ContextCompat.getColor(requireContext(), view.setCardBackgroundColor(
R.color.colorPrimary)) ContextCompat.getColor(
requireContext(),
R.color.cardBorder
)
)
} else { } else {
ViewCompat.setElevation(view, 0f) ViewCompat.setElevation(view, 0f)
view.setCardBackgroundColor(ContextCompat.getColor(requireContext(), view.setCardBackgroundColor(
R.color.colorPrimaryDark)) 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, recyclerView,
viewHolder, viewHolder,
dX, dX,
topEdge, topEdge,
actionState, actionState,
isCurrentlyActive) isCurrentlyActive
)
} }
override fun onSwiped( override fun onSwiped(
@ -342,42 +407,34 @@ class GlanceTabFragment : Fragment() {
} }
}) })
mIth.attachToRecyclerView(providers_list) mIth.attachToRecyclerView(binding.providersList)
adapter.updateData(list.mapNotNull { GlanceProviderHelper.getGlanceProviderById(requireContext(), it) })
providers_list.isNestedScrollingEnabled = false
setupListener() setupListener()
}
private fun subscribeUi( binding.scrollView.viewTreeObserver.addOnScrollChangedListener {
binding: FragmentGlanceSettingsBinding, viewModel.fragmentScrollY.value = binding.scrollView.scrollY
viewModel: MainViewModel, }
) {
binding.isGlanceVisible = Preferences.showGlance
viewModel.showGlance.observe(viewLifecycleOwner, Observer { lifecycleScope.launch(Dispatchers.IO) {
maintainScrollPosition { delay(500)
binding.isGlanceVisible = it val l = list.mapNotNull { GlanceProviderHelper.getGlanceProviderById(
show_glance_label.text = requireContext(),
if (it) getString(R.string.description_show_glance_visible) else getString( it
R.string.description_show_glance_not_visible) ) }
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() { private fun setupListener() {
action_show_glance.setOnClickListener {
Preferences.showGlance = !Preferences.showGlance
}
show_glance_switch.setOnCheckedChangeListener { _, enabled: Boolean ->
Preferences.showGlance = enabled
}
action_show_glance.setOnLongClickListener {
Preferences.enabledGlanceProviderOrder = ""
true
}
} }
private val nextAlarmChangeBroadcastReceiver = object : BroadcastReceiver() { private val nextAlarmChangeBroadcastReceiver = object : BroadcastReceiver() {
@ -388,15 +445,17 @@ class GlanceTabFragment : Fragment() {
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
activity?.registerReceiver(nextAlarmChangeBroadcastReceiver, requireActivity().registerReceiver(
IntentFilter(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) nextAlarmChangeBroadcastReceiver,
IntentFilter(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)
)
if (dialog != null) { if (dialog != null) {
dialog?.show() dialog?.show()
} }
} }
override fun onStop() { override fun onStop() {
activity?.unregisterReceiver(nextAlarmChangeBroadcastReceiver) requireActivity().unregisterReceiver(nextAlarmChangeBroadcastReceiver)
super.onStop() super.onStop()
} }
@ -420,13 +479,15 @@ class GlanceTabFragment : Fragment() {
2 -> { 2 -> {
try { try {
val account: GoogleSignInAccount? = GoogleSignIn.getSignedInAccountFromIntent( val account: GoogleSignInAccount? = GoogleSignIn.getSignedInAccountFromIntent(
data).getResult(ApiException::class.java) data
).getResult(ApiException::class.java)
if (!GoogleSignIn.hasPermissions(account, FITNESS_OPTIONS)) { if (!GoogleSignIn.hasPermissions(account, FITNESS_OPTIONS)) {
GoogleSignIn.requestPermissions( GoogleSignIn.requestPermissions(
requireActivity(), requireActivity(),
1, 1,
account, account,
FITNESS_OPTIONS) FITNESS_OPTIONS
)
} else { } else {
adapter.notifyItemRangeChanged(0, adapter.data.size) adapter.notifyItemRangeChanged(0, adapter.data.size)
} }
@ -442,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() { override fun onResume() {
super.onResume() super.onResume()
adapter.notifyItemRangeChanged(0, adapter.data.size) adapter.notifyItemRangeChanged(0, adapter.data?.size ?: 0)
if (dialog != null) { if (dialog != null) {
dialog?.show() 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,259 @@
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_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(
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 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 androidx.lifecycle.*
import com.chibatching.kotpref.livedata.asLiveData 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.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 // General Settings
val textGlobalColor = Preferences.asLiveData(Preferences::textGlobalColor) val textGlobalColor = MediatorLiveData<Boolean>().apply {
val textGlobalAlpha = Preferences.asLiveData(Preferences::textGlobalAlpha) addSource(Preferences.asLiveData(Preferences::textGlobalColor)) { value = true }
val textSecondaryColor = Preferences.asLiveData(Preferences::textSecondaryColor) addSource(Preferences.asLiveData(Preferences::textGlobalAlpha)) { value = true }
val textSecondaryAlpha = Preferences.asLiveData(Preferences::textSecondaryAlpha) addSource(Preferences.asLiveData(Preferences::textGlobalColorDark)) { value = true }
val backgroundCardColor = Preferences.asLiveData(Preferences::backgroundCardColor) addSource(Preferences.asLiveData(Preferences::textGlobalAlphaDark)) { value = true }
val backgroundCardAlpha = Preferences.asLiveData(Preferences::backgroundCardAlpha) }
val textGlobalColorDark = Preferences.asLiveData(Preferences::textGlobalColorDark) val textSecondaryColor = MediatorLiveData<Boolean>().apply {
val textGlobalAlphaDark = Preferences.asLiveData(Preferences::textGlobalAlphaDark) addSource(Preferences.asLiveData(Preferences::textSecondaryColor)) { value = true }
val textSecondaryColorDark = Preferences.asLiveData(Preferences::textSecondaryColorDark) addSource(Preferences.asLiveData(Preferences::textSecondaryAlpha)) { value = true }
val textSecondaryAlphaDark = Preferences.asLiveData(Preferences::textSecondaryAlphaDark) addSource(Preferences.asLiveData(Preferences::textSecondaryColorDark)) { value = true }
val backgroundCardColorDark = Preferences.asLiveData(Preferences::backgroundCardColorDark) addSource(Preferences.asLiveData(Preferences::textSecondaryAlphaDark)) { value = true }
val backgroundCardAlphaDark = Preferences.asLiveData(Preferences::backgroundCardAlphaDark) }
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 textMainSize = Preferences.asLiveData(Preferences::textMainSize)
val textSecondSize = Preferences.asLiveData(Preferences::textSecondSize) val textSecondSize = Preferences.asLiveData(Preferences::textSecondSize)
val textShadow = Preferences.asLiveData(Preferences::textShadow) val textShadow = Preferences.asLiveData(Preferences::textShadow)
val textShadowDark = Preferences.asLiveData(Preferences::textShadowDark) val textShadowDark = Preferences.asLiveData(Preferences::textShadowDark)
val customFont = Preferences.asLiveData(Preferences::customFont) val font = MediatorLiveData<Boolean>().apply {
val customFontFile = Preferences.asLiveData(Preferences::customFontFile) addSource(Preferences.asLiveData(Preferences::customFont)) { value = true }
val customFontName = Preferences.asLiveData(Preferences::customFontName) addSource(Preferences.asLiveData(Preferences::customFontFile)) { value = true }
val customFontVariant = Preferences.asLiveData(Preferences::customFontVariant) addSource(Preferences.asLiveData(Preferences::customFontName)) { value = true }
addSource(Preferences.asLiveData(Preferences::customFontVariant)) { value = true }
}
val secondRowInformation = Preferences.asLiveData(Preferences::secondRowInformation) val secondRowInformation = Preferences.asLiveData(Preferences::secondRowInformation)
val showDividers = Preferences.asLiveData(Preferences::showDividers) val showDividers = Preferences.asLiveData(Preferences::showDividers)
val secondRowTopMargin = Preferences.asLiveData(Preferences::secondRowTopMargin) val secondRowTopMargin = Preferences.asLiveData(Preferences::secondRowTopMargin)
@ -36,56 +55,117 @@ class MainViewModel : ViewModel() {
val calendarAllDay = Preferences.asLiveData(Preferences::calendarAllDay) val calendarAllDay = Preferences.asLiveData(Preferences::calendarAllDay)
val showUntil = Preferences.asLiveData(Preferences::showUntil) val showUntil = Preferences.asLiveData(Preferences::showUntil)
val showDiffTime = Preferences.asLiveData(Preferences::showDiffTime) val showDiffTime = Preferences.asLiveData(Preferences::showDiffTime)
val showDeclinedEvents = Preferences.asLiveData(Preferences::showDeclinedEvents)
val showNextEvent = Preferences.asLiveData(Preferences::showNextEvent) val showNextEvent = Preferences.asLiveData(Preferences::showNextEvent)
val openEventDetails = Preferences.asLiveData(Preferences::openEventDetails) val openEventDetails = Preferences.asLiveData(Preferences::openEventDetails)
val calendarAppName = Preferences.asLiveData(Preferences::calendarAppName) val calendarAppName = Preferences.asLiveData(Preferences::calendarAppName)
val widgetUpdateFrequency = Preferences.asLiveData(Preferences::widgetUpdateFrequency) val widgetUpdateFrequency = Preferences.asLiveData(Preferences::widgetUpdateFrequency)
val showOnlyBusyEvents = Preferences.asLiveData(Preferences::showOnlyBusyEvents) val dateFormat = Preferences.asLiveData(Preferences::dateFormat)
// Clock Settings // Clock Settings
val showClock = Preferences.asLiveData(Preferences::showClock) val showClock = Preferences.asLiveData(Preferences::showClock)
val clockTextSize = Preferences.asLiveData(Preferences::clockTextSize) val clockTextSize = Preferences.asLiveData(Preferences::clockTextSize)
val clockTextColor = Preferences.asLiveData(Preferences::clockTextColor) val clockTextColor = MediatorLiveData<Boolean>().apply {
val clockTextAlpha = Preferences.asLiveData(Preferences::clockTextAlpha) addSource(Preferences.asLiveData(Preferences::clockTextColor)) { value = true }
val clockTextColorDark = Preferences.asLiveData(Preferences::clockTextColorDark) addSource(Preferences.asLiveData(Preferences::clockTextAlpha)) { value = true }
val clockTextAlphaDark = Preferences.asLiveData(Preferences::clockTextAlphaDark) }
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 showAMPMIndicator = Preferences.asLiveData(Preferences::showAMPMIndicator)
val clockAppName = Preferences.asLiveData(Preferences::clockAppName) val clockAppName = Preferences.asLiveData(Preferences::clockAppName)
val dateFormat = Preferences.asLiveData(Preferences::dateFormat)
val clockBottomMargin = Preferences.asLiveData(Preferences::clockBottomMargin) val clockBottomMargin = Preferences.asLiveData(Preferences::clockBottomMargin)
val showBigClockWarning = Preferences.asLiveData(Preferences::showBigClockWarning)
// Weather Settings // Weather Settings
val showWeather = Preferences.asLiveData(Preferences::showWeather) val showWeather = Preferences.asLiveData(Preferences::showWeather)
val weatherTempUnit = Preferences.asLiveData(Preferences::weatherTempUnit) val weatherTempUnit = Preferences.asLiveData(Preferences::weatherTempUnit)
val weatherRefreshPeriod = Preferences.asLiveData(Preferences::weatherRefreshPeriod) val weatherRefreshPeriod = Preferences.asLiveData(Preferences::weatherRefreshPeriod)
val weatherAppName = Preferences.asLiveData(Preferences::weatherAppName) val weatherAppName = Preferences.asLiveData(Preferences::weatherAppName)
val weatherProviderApi = Preferences.asLiveData(Preferences::weatherProviderApiOpen)
val customLocationAdd = Preferences.asLiveData(Preferences::customLocationAdd) val customLocationAdd = Preferences.asLiveData(Preferences::customLocationAdd)
val showWeatherWarning = Preferences.asLiveData(Preferences::showWeatherWarning)
val weatherIconPack = Preferences.asLiveData(Preferences::weatherIconPack) val weatherIconPack = Preferences.asLiveData(Preferences::weatherIconPack)
val weatherProvider = Preferences.asLiveData(Preferences::weatherProvider) val weatherProvider = Preferences.asLiveData(Preferences::weatherProvider)
val weatherProviderError = Preferences.asLiveData(Preferences::weatherProviderError) val weatherProviderError = Preferences.asLiveData(Preferences::weatherProviderError)
val weatherProviderLocationError = Preferences.asLiveData(Preferences::weatherProviderLocationError) 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 // Advanced Settings
val darkThemePreference = Preferences.asLiveData(Preferences::darkThemePreference) val darkThemePreference = Preferences.asLiveData(Preferences::darkThemePreference)
val showWallpaper = Preferences.asLiveData(Preferences::showWallpaper) val showWallpaper = Preferences.asLiveData(Preferences::showWallpaper)
val showPreview = Preferences.asLiveData(Preferences::showPreview) val showPreview = Preferences.asLiveData(Preferences::showPreview)
val installedIntegrations = Preferences.asLiveData(Preferences::installedIntegrations) 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 android.app.Application
import androidx.lifecycle.AndroidViewModel 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.app.Activity
import android.content.Context 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.app.Application
import android.content.Intent 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.app.Application
import android.content.Intent import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.pm.ResolveInfo import android.content.pm.ResolveInfo
import android.util.Log
import androidx.lifecycle.* import androidx.lifecycle.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch 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 android.app.Application
import androidx.lifecycle.AndroidViewModel 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.app.Application
import android.content.Intent 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.app.Application
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo

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.app.Application
import android.content.Intent import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.pm.ResolveInfo import android.content.pm.ResolveInfo
import android.util.Log
import androidx.lifecycle.* import androidx.lifecycle.*
import com.chibatching.kotpref.livedata.asLiveData import com.chibatching.kotpref.livedata.asLiveData
import com.tommasoberlose.anotherwidget.global.Preferences 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 android.app.Application
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel

View File

@ -15,6 +15,7 @@ import android.os.Bundle
import android.text.format.DateUtils import android.text.format.DateUtils
import android.util.Log import android.util.Log
import android.util.TypedValue import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import android.widget.RemoteViews import android.widget.RemoteViews
@ -23,6 +24,7 @@ import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.google.gson.Gson import com.google.gson.Gson
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.TheWidgetBinding
import com.tommasoberlose.anotherwidget.db.EventRepository import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.global.Actions import com.tommasoberlose.anotherwidget.global.Actions
import com.tommasoberlose.anotherwidget.global.Constants import com.tommasoberlose.anotherwidget.global.Constants
@ -34,7 +36,6 @@ import com.tommasoberlose.anotherwidget.receivers.*
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.isDarkTheme import com.tommasoberlose.anotherwidget.utils.isDarkTheme
import com.tommasoberlose.anotherwidget.utils.toPixel import com.tommasoberlose.anotherwidget.utils.toPixel
import kotlinx.android.synthetic.main.the_widget.view.*
import java.text.DateFormat import java.text.DateFormat
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -133,13 +134,13 @@ class MainWidget : AppWidgetProvider() {
// Setup listener // Setup listener
try { try {
val generatedView = generateWidgetView(context, typeface) val generatedBinding = generateWidgetView(context, typeface)
views.setImageViewBitmap( views.setImageViewBitmap(
R.id.bitmap_container, R.id.bitmap_container,
BitmapHelper.getBitmapFromView(generatedView, width = w) BitmapHelper.getBitmapFromView(generatedBinding.root, width = w)
) )
views = updateCalendarView(context, generatedView, views, appWidgetId) views = updateCalendarView(context, generatedBinding, views, appWidgetId)
views = updateWeatherView(context, generatedView, views, appWidgetId) views = updateWeatherView(context, generatedBinding, views, appWidgetId)
} catch (ex: Exception) { } catch (ex: Exception) {
ex.printStackTrace() ex.printStackTrace()
CrashlyticsReceiver.sendCrash(context, ex) CrashlyticsReceiver.sendCrash(context, ex)
@ -148,12 +149,12 @@ class MainWidget : AppWidgetProvider() {
appWidgetManager.updateAppWidget(appWidgetId, views) 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) val eventRepository = EventRepository(context)
try { try {
views.setImageViewBitmap( views.setImageViewBitmap(
R.id.empty_date_rect, 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) views.setViewVisibility(R.id.empty_layout_rect, View.VISIBLE)
@ -176,7 +177,7 @@ class MainWidget : AppWidgetProvider() {
if (Preferences.showNextEvent && eventRepository.getEventsCount() > 1) { if (Preferences.showNextEvent && eventRepository.getEventsCount() > 1) {
views.setImageViewBitmap( views.setImageViewBitmap(
R.id.action_next_rect, 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.setViewVisibility(R.id.action_next_rect, View.VISIBLE)
views.setOnClickPendingIntent( views.setOnClickPendingIntent(
@ -194,7 +195,7 @@ class MainWidget : AppWidgetProvider() {
views.setImageViewBitmap( views.setImageViewBitmap(
R.id.action_previous_rect, 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.setViewVisibility(R.id.action_previous_rect, View.VISIBLE)
views.setOnClickPendingIntent( views.setOnClickPendingIntent(
@ -227,7 +228,7 @@ class MainWidget : AppWidgetProvider() {
views.setImageViewBitmap( views.setImageViewBitmap(
R.id.next_event_difference_time_rect, R.id.next_event_difference_time_rect,
BitmapHelper.getBitmapFromView( BitmapHelper.getBitmapFromView(
v.next_event_difference_time, bindingView.nextEventDifferenceTime,
draw = false draw = false
) )
) )
@ -260,12 +261,12 @@ class MainWidget : AppWidgetProvider() {
views.setImageViewBitmap( views.setImageViewBitmap(
R.id.next_event_rect, R.id.next_event_rect,
BitmapHelper.getBitmapFromView(v.next_event, draw = false) BitmapHelper.getBitmapFromView(bindingView.nextEvent, draw = false)
) )
views.setImageViewBitmap( views.setImageViewBitmap(
R.id.second_row_rect, 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) views.setViewVisibility(R.id.second_row_rect, View.VISIBLE)
@ -284,7 +285,7 @@ class MainWidget : AppWidgetProvider() {
R.id.second_row_top_margin_large_sans, R.id.second_row_top_margin_large_sans,
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.value) View.VISIBLE else View.GONE 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 var showSomething = false
loop@ for (provider:Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(context)) { loop@ for (provider:Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(context)) {
when (provider) { when (provider) {
@ -405,12 +406,12 @@ class MainWidget : AppWidgetProvider() {
if (showSomething) { if (showSomething) {
views.setImageViewBitmap( views.setImageViewBitmap(
R.id.next_event_rect, R.id.next_event_rect,
BitmapHelper.getBitmapFromView(v.next_event, draw = false) BitmapHelper.getBitmapFromView(bindingView.nextEvent, draw = false)
) )
views.setImageViewBitmap( views.setImageViewBitmap(
R.id.second_row_rect, 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) views.setViewVisibility(R.id.second_row_rect, View.VISIBLE)
@ -443,7 +444,7 @@ class MainWidget : AppWidgetProvider() {
return views 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 { try {
if (Preferences.showWeather && Preferences.weatherIcon != "") { if (Preferences.showWeather && Preferences.weatherIcon != "") {
views.setViewVisibility(R.id.weather_rect, View.VISIBLE) views.setViewVisibility(R.id.weather_rect, View.VISIBLE)
@ -460,17 +461,17 @@ class MainWidget : AppWidgetProvider() {
views.setImageViewBitmap( views.setImageViewBitmap(
R.id.weather_rect, R.id.weather_rect,
BitmapHelper.getBitmapFromView(v.weather, draw = false) BitmapHelper.getBitmapFromView(bindingView.weather, draw = false)
) )
views.setImageViewBitmap( views.setImageViewBitmap(
R.id.calendar_weather_rect, R.id.calendar_weather_rect,
BitmapHelper.getBitmapFromView(v.calendar_weather, draw = false) BitmapHelper.getBitmapFromView(bindingView.calendarWeather, draw = false)
) )
views.setImageViewBitmap( views.setImageViewBitmap(
R.id.special_weather_rect, R.id.special_weather_rect,
BitmapHelper.getBitmapFromView(v.calendar_weather, draw = false) BitmapHelper.getBitmapFromView(bindingView.calendarWeather, draw = false)
) )
if (GlanceProviderHelper.showGlanceProviders(context)) { if (GlanceProviderHelper.showGlanceProviders(context)) {
@ -550,39 +551,39 @@ class MainWidget : AppWidgetProvider() {
// Generates the widget bitmap from the view // 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 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 { val now = Calendar.getInstance().apply {
set(Calendar.SECOND, 0) set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0) set(Calendar.MILLISECOND, 0)
} }
v.empty_layout.visibility = View.VISIBLE bindingView.emptyLayout.visibility = View.VISIBLE
v.calendar_layout.visibility = View.GONE bindingView.calendarLayout.visibility = View.GONE
v.next_event_difference_time.visibility = View.GONE bindingView.nextEventDifferenceTime.visibility = View.GONE
v.action_next.isVisible = false bindingView.actionNext.isVisible = false
v.action_previous.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 nextEvent = eventRepository.getNextEvent()
val nextAlarm = AlarmHelper.getNextAlarm(context) val nextAlarm = AlarmHelper.getNextAlarm(context)
if (Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null && !Preferences.showEventsAsGlanceProvider) { if (Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null && !Preferences.showEventsAsGlanceProvider) {
// Multiple counter // Multiple counter
v.action_next.isVisible = bindingView.actionNext.isVisible =
Preferences.showNextEvent && eventRepository.getEventsCount() > 1 Preferences.showNextEvent && eventRepository.getEventsCount() > 1
v.action_previous.isVisible = bindingView.actionPrevious.isVisible =
Preferences.showNextEvent && eventRepository.getEventsCount() > 1 Preferences.showNextEvent && eventRepository.getEventsCount() > 1
v.next_event.text = nextEvent.title bindingView.nextEvent.text = nextEvent.title
if (Preferences.showDiffTime && now.timeInMillis < nextEvent.startDate) { if (Preferences.showDiffTime && now.timeInMillis < nextEvent.startDate) {
v.next_event_difference_time.text = if (!nextEvent.allDay) { bindingView.nextEventDifferenceTime.text = if (!nextEvent.allDay) {
SettingsStringHelper.getDifferenceText( SettingsStringHelper.getDifferenceText(
context, context,
now.timeInMillis, now.timeInMillis,
@ -596,21 +597,21 @@ class MainWidget : AppWidgetProvider() {
nextEvent.startDate nextEvent.startDate
).toLowerCase(Locale.getDefault()) ).toLowerCase(Locale.getDefault())
} }
v.next_event_difference_time.visibility = View.VISIBLE bindingView.nextEventDifferenceTime.visibility = View.VISIBLE
} else { } else {
v.next_event_difference_time.visibility = View.GONE bindingView.nextEventDifferenceTime.visibility = View.GONE
} }
if (nextEvent.address != "" && Preferences.secondRowInformation == 1) { if (nextEvent.address != "" && Preferences.secondRowInformation == 1) {
v.second_row_icon.setImageDrawable( bindingView.secondRowIcon.setImageDrawable(
ContextCompat.getDrawable( ContextCompat.getDrawable(
context, context,
R.drawable.round_place_24 R.drawable.round_place_24
) )
) )
v.next_event_date.text = nextEvent.address bindingView.nextEventDate.text = nextEvent.address
} else { } else {
v.second_row_icon.setImageDrawable( bindingView.secondRowIcon.setImageDrawable(
ContextCompat.getDrawable( ContextCompat.getDrawable(
context, context,
R.drawable.round_today_24 R.drawable.round_today_24
@ -651,10 +652,10 @@ class MainWidget : AppWidgetProvider() {
} }
if (nextEvent.startDate != nextEvent.endDate) { if (nextEvent.startDate != nextEvent.endDate) {
v.next_event_date.text = bindingView.nextEventDate.text =
String.format("%s - %s%s", startHour, endHour, multipleDay) String.format("%s - %s%s", startHour, endHour, multipleDay)
} else { } else {
v.next_event_date.text = bindingView.nextEventDate.text =
String.format("%s", startHour) String.format("%s", startHour)
} }
@ -663,7 +664,7 @@ class MainWidget : AppWidgetProvider() {
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_ABBREV_MONTH DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_ABBREV_MONTH
val start = Calendar.getInstance().apply { timeInMillis = nextEvent.startDate } 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) 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)) { } 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) DateUtils.formatDateTime(context, now.timeInMillis, flags)
@ -673,17 +674,17 @@ class MainWidget : AppWidgetProvider() {
} }
} }
v.empty_layout.visibility = View.GONE bindingView.emptyLayout.visibility = View.GONE
v.calendar_layout.visibility = View.VISIBLE 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 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 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 if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.value) View.VISIBLE else View.GONE
} else if (GlanceProviderHelper.showGlanceProviders(context)) { } else if (GlanceProviderHelper.showGlanceProviders(context)) {
v.second_row_icon.isVisible = true bindingView.secondRowIcon.isVisible = true
var showSomething = false var showSomething = false
loop@ for (provider: Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders( loop@ for (provider: Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(
context context
@ -691,26 +692,26 @@ class MainWidget : AppWidgetProvider() {
when (provider) { when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> { Constants.GlanceProviderId.PLAYING_SONG -> {
if (MediaPlayerHelper.isSomeonePlaying(context)) { if (MediaPlayerHelper.isSomeonePlaying(context)) {
v.second_row_icon.setImageDrawable( bindingView.secondRowIcon.setImageDrawable(
ContextCompat.getDrawable( ContextCompat.getDrawable(
context, context,
R.drawable.round_music_note_24 R.drawable.round_music_note_24
) )
) )
v.next_event_date.text = MediaPlayerHelper.getMediaInfo() bindingView.nextEventDate.text = MediaPlayerHelper.getMediaInfo()
showSomething = true showSomething = true
break@loop break@loop
} }
} }
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> { Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
if (Preferences.showNextAlarm && nextAlarm != "") { if (Preferences.showNextAlarm && nextAlarm != "") {
v.second_row_icon.setImageDrawable( bindingView.secondRowIcon.setImageDrawable(
ContextCompat.getDrawable( ContextCompat.getDrawable(
context, context,
R.drawable.round_alarm_24 R.drawable.round_alarm_24
) )
) )
v.next_event_date.text = AlarmHelper.getNextAlarm(context) bindingView.nextEventDate.text = AlarmHelper.getNextAlarm(context)
showSomething = true showSomething = true
break@loop break@loop
} }
@ -719,19 +720,19 @@ class MainWidget : AppWidgetProvider() {
if (Preferences.showBatteryCharging) { if (Preferences.showBatteryCharging) {
BatteryHelper.updateBatteryInfo(context) BatteryHelper.updateBatteryInfo(context)
if (Preferences.isCharging) { if (Preferences.isCharging) {
v.second_row_icon.isVisible = false bindingView.secondRowIcon.isVisible = false
val batteryLevel = BatteryHelper.getBatteryLevel(context) val batteryLevel = BatteryHelper.getBatteryLevel(context)
if (batteryLevel != 100) { if (batteryLevel != 100) {
v.next_event_date.text = context.getString(R.string.charging) bindingView.nextEventDate.text = context.getString(R.string.charging)
} else { } else {
v.next_event_date.text = bindingView.nextEventDate.text =
context.getString(R.string.charged) context.getString(R.string.charged)
} }
showSomething = true showSomething = true
break@loop break@loop
} else if (Preferences.isBatteryLevelLow) { } else if (Preferences.isBatteryLevelLow) {
v.second_row_icon.isVisible = false bindingView.secondRowIcon.isVisible = false
v.next_event_date.text = bindingView.nextEventDate.text =
context.getString(R.string.battery_low_warning) context.getString(R.string.battery_low_warning)
showSomething = true showSomething = true
break@loop break@loop
@ -740,17 +741,17 @@ class MainWidget : AppWidgetProvider() {
} }
Constants.GlanceProviderId.CUSTOM_INFO -> { Constants.GlanceProviderId.CUSTOM_INFO -> {
if (Preferences.customNotes.isNotEmpty()) { if (Preferences.customNotes.isNotEmpty()) {
v.second_row_icon.isVisible = false bindingView.secondRowIcon.isVisible = false
v.next_event_date.text = Preferences.customNotes bindingView.nextEventDate.text = Preferences.customNotes
v.next_event_date.maxLines = 2 bindingView.nextEventDate.maxLines = 2
showSomething = true showSomething = true
break@loop break@loop
} }
} }
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> { Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
if (Preferences.showDailySteps && Preferences.googleFitSteps > 0) { if (Preferences.showDailySteps && Preferences.googleFitSteps > 0) {
v.second_row_icon.isVisible = false bindingView.secondRowIcon.isVisible = false
v.next_event_date.text = bindingView.nextEventDate.text =
context.getString(R.string.daily_steps_counter) context.getString(R.string.daily_steps_counter)
.format(Preferences.googleFitSteps) .format(Preferences.googleFitSteps)
showSomething = true showSomething = true
@ -764,12 +765,12 @@ class MainWidget : AppWidgetProvider() {
val remotePackageContext = context.createPackageContext(Preferences.lastNotificationPackage, 0) val remotePackageContext = context.createPackageContext(Preferences.lastNotificationPackage, 0)
val icon = ContextCompat.getDrawable(remotePackageContext, val icon = ContextCompat.getDrawable(remotePackageContext,
Preferences.lastNotificationIcon) Preferences.lastNotificationIcon)
v.second_row_icon.isVisible = true bindingView.secondRowIcon.isVisible = true
v.second_row_icon.setImageDrawable(icon) bindingView.secondRowIcon.setImageDrawable(icon)
} else { } 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 showSomething = true
break@loop break@loop
} catch (ex: Exception) {} } catch (ex: Exception) {}
@ -778,16 +779,16 @@ class MainWidget : AppWidgetProvider() {
Constants.GlanceProviderId.GREETINGS -> { Constants.GlanceProviderId.GREETINGS -> {
val greetingsText = GreetingsHelper.getRandomString(context) val greetingsText = GreetingsHelper.getRandomString(context)
if (Preferences.showGreetings && GreetingsHelper.showGreetings() && greetingsText.isNotBlank()) { if (Preferences.showGreetings && GreetingsHelper.showGreetings() && greetingsText.isNotBlank()) {
v.next_event_date.text = greetingsText bindingView.nextEventDate.text = greetingsText
v.next_event_date.maxLines = 2 bindingView.nextEventDate.maxLines = 2
v.second_row_icon.isVisible = false bindingView.secondRowIcon.isVisible = false
showSomething = true showSomething = true
break@loop break@loop
} }
} }
Constants.GlanceProviderId.EVENTS -> { Constants.GlanceProviderId.EVENTS -> {
if (Preferences.showEventsAsGlanceProvider && Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null) { if (Preferences.showEventsAsGlanceProvider && Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null) {
v.next_event_date.text = context.getString(R.string.events_glance_provider_format).format(nextEvent.title, if (Preferences.showDiffTime && now.timeInMillis < nextEvent.startDate) { bindingView.nextEventDate.text = context.getString(R.string.events_glance_provider_format).format(nextEvent.title, if (Preferences.showDiffTime && now.timeInMillis < nextEvent.startDate) {
if (!nextEvent.allDay) { if (!nextEvent.allDay) {
SettingsStringHelper.getDifferenceText( SettingsStringHelper.getDifferenceText(
context, context,
@ -803,8 +804,8 @@ class MainWidget : AppWidgetProvider() {
).toLowerCase(Locale.getDefault()) ).toLowerCase(Locale.getDefault())
} }
} else "").trimEnd() } else "").trimEnd()
v.second_row_icon.isVisible = true bindingView.secondRowIcon.isVisible = true
v.second_row_icon.setImageDrawable( bindingView.secondRowIcon.setImageDrawable(
ContextCompat.getDrawable( ContextCompat.getDrawable(
context, context,
R.drawable.round_today_24 R.drawable.round_today_24
@ -818,43 +819,43 @@ class MainWidget : AppWidgetProvider() {
} }
if (showSomething) { if (showSomething) {
v.next_event.text = DateHelper.getDateText(context, now) bindingView.nextEvent.text = DateHelper.getDateText(context, now)
v.empty_layout.visibility = View.GONE bindingView.emptyLayout.visibility = View.GONE
v.calendar_layout.visibility = View.VISIBLE 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 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 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 if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.value) View.VISIBLE else View.GONE
} else { } else {
v.second_row_icon.isVisible = false bindingView.secondRowIcon.isVisible = false
} }
} }
// Color // Color
listOf<TextView>( listOf<TextView>(
v.empty_date, bindingView.emptyDate,
v.divider1, bindingView.divider1,
v.temp, bindingView.temp,
v.next_event, bindingView.nextEvent,
v.next_event_difference_time, bindingView.nextEventDifferenceTime,
v.divider3, bindingView.divider3,
v.special_temp bindingView.specialTemp
).forEach { ).forEach {
it.setTextColor(ColorHelper.getFontColor(context.applicationContext.isDarkTheme())) it.setTextColor(ColorHelper.getFontColor(context.applicationContext.isDarkTheme()))
} }
if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.value) { if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.value) {
listOf<ImageView>(v.action_next, v.action_previous) listOf<ImageView>(bindingView.actionNext, bindingView.actionPrevious)
} else { } else {
listOf<ImageView>( listOf<ImageView>(
v.action_next, bindingView.actionNext,
v.action_previous, bindingView.actionPrevious,
v.empty_weather_icon, bindingView.emptyWeatherIcon,
v.special_weather_icon bindingView.specialWeatherIcon
) )
}.forEach { }.forEach {
it.setColorFilter(ColorHelper.getFontColorRgb(context.applicationContext.isDarkTheme())) it.setColorFilter(ColorHelper.getFontColorRgb(context.applicationContext.isDarkTheme()))
@ -864,14 +865,14 @@ class MainWidget : AppWidgetProvider() {
.toFloat()) / 100 .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())) it.setTextColor(ColorHelper.getSecondaryFontColor(context.applicationContext.isDarkTheme()))
} }
if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.value) { 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 { } else {
listOf<ImageView>(v.second_row_icon, v.weather_icon, v.second_row_icon_shadow) listOf<ImageView>(bindingView.secondRowIcon, bindingView.weatherIcon, bindingView.secondRowIconShadow)
}.forEach { }.forEach {
it.setColorFilter(ColorHelper.getSecondaryFontColorRgb(context.applicationContext.isDarkTheme())) it.setColorFilter(ColorHelper.getSecondaryFontColorRgb(context.applicationContext.isDarkTheme()))
it.alpha = it.alpha =
@ -882,38 +883,38 @@ class MainWidget : AppWidgetProvider() {
// Text Size // Text Size
listOf<Pair<TextView, Float>>( listOf<Pair<TextView, Float>>(
v.empty_date to Preferences.textMainSize, bindingView.emptyDate to Preferences.textMainSize,
v.divider1 to (Preferences.textMainSize - 2), bindingView.divider1 to (Preferences.textMainSize - 2),
v.temp to Preferences.textMainSize, bindingView.temp to Preferences.textMainSize,
v.next_event to Preferences.textMainSize, bindingView.nextEvent to Preferences.textMainSize,
v.next_event_difference_time to Preferences.textMainSize, bindingView.nextEventDifferenceTime to Preferences.textMainSize,
v.next_event_date to Preferences.textSecondSize, bindingView.nextEventDate to Preferences.textSecondSize,
v.divider2 to (Preferences.textSecondSize - 2), bindingView.divider2 to (Preferences.textSecondSize - 2),
v.calendar_temp to Preferences.textSecondSize, bindingView.calendarTemp to Preferences.textSecondSize,
v.divider3 to (Preferences.textMainSize - 2), bindingView.divider3 to (Preferences.textMainSize - 2),
v.special_temp to Preferences.textMainSize bindingView.specialTemp to Preferences.textMainSize
).forEach { ).forEach {
it.first.setTextSize(TypedValue.COMPLEX_UNIT_SP, it.second) it.first.setTextSize(TypedValue.COMPLEX_UNIT_SP, it.second)
} }
// Icons scale // Icons scale
v.second_row_icon.scaleX = Preferences.textSecondSize / 18f bindingView.secondRowIcon.scaleX = Preferences.textSecondSize / 18f
v.second_row_icon.scaleY = Preferences.textSecondSize / 18f bindingView.secondRowIcon.scaleY = Preferences.textSecondSize / 18f
v.weather_icon.scaleX = Preferences.textSecondSize / 14f bindingView.weatherIcon.scaleX = Preferences.textSecondSize / 14f
v.weather_icon.scaleY = Preferences.textSecondSize / 14f bindingView.weatherIcon.scaleY = Preferences.textSecondSize / 14f
v.empty_weather_icon.scaleX = Preferences.textMainSize / 18f bindingView.emptyWeatherIcon.scaleX = Preferences.textMainSize / 18f
v.empty_weather_icon.scaleY = Preferences.textMainSize / 18f bindingView.emptyWeatherIcon.scaleY = Preferences.textMainSize / 18f
v.action_next.scaleX = Preferences.textMainSize / 28f bindingView.actionNext.scaleX = Preferences.textMainSize / 28f
v.action_next.scaleY = Preferences.textMainSize / 28f bindingView.actionNext.scaleY = Preferences.textMainSize / 28f
v.action_previous.scaleX = Preferences.textMainSize / 28f bindingView.actionPrevious.scaleX = Preferences.textMainSize / 28f
v.action_previous.scaleY = Preferences.textMainSize / 28f bindingView.actionPrevious.scaleY = Preferences.textMainSize / 28f
v.special_weather_icon.scaleX = Preferences.textMainSize / 18f bindingView.specialWeatherIcon.scaleX = Preferences.textMainSize / 18f
v.special_weather_icon.scaleY = Preferences.textMainSize / 18f bindingView.specialWeatherIcon.scaleY = Preferences.textMainSize / 18f
// Shadows // Shadows
@ -940,16 +941,16 @@ class MainWidget : AppWidgetProvider() {
} }
listOf<TextView>( listOf<TextView>(
v.empty_date, bindingView.emptyDate,
v.divider1, bindingView.divider1,
v.temp, bindingView.temp,
v.next_event, bindingView.nextEvent,
v.next_event_difference_time, bindingView.nextEventDifferenceTime,
v.next_event_date, bindingView.nextEventDate,
v.divider2, bindingView.divider2,
v.calendar_temp, bindingView.calendarTemp,
v.divider3, bindingView.divider3,
v.special_temp bindingView.specialTemp
).forEach { ).forEach {
it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor) it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor)
} }
@ -957,7 +958,7 @@ class MainWidget : AppWidgetProvider() {
// Icons shadow // Icons shadow
listOf( listOf(
Pair(v.second_row_icon, v.second_row_icon_shadow), Pair(bindingView.secondRowIcon, bindingView.secondRowIconShadow),
).forEach { ).forEach {
if ((if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) == 0) { if ((if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) == 0) {
it.second.isVisible = false it.second.isVisible = false
@ -970,8 +971,8 @@ class MainWidget : AppWidgetProvider() {
} }
listOf( listOf(
Pair(v.action_next, v.action_next_shadow), Pair(bindingView.actionNext, bindingView.actionNextShadow),
Pair(v.action_previous, v.action_previous_shadow), Pair(bindingView.actionPrevious, bindingView.actionPreviousShadow),
).forEach { ).forEach {
if ((if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) == 0) { if ((if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) == 0) {
it.second.isVisible = false it.second.isVisible = false
@ -983,8 +984,8 @@ class MainWidget : AppWidgetProvider() {
} }
} }
v.action_previous.scaleX = v.action_previous.scaleX * -1 bindingView.actionPrevious.scaleX = bindingView.actionPrevious.scaleX * -1
v.action_previous_shadow.scaleX = v.action_previous_shadow.scaleX * -1 bindingView.actionPreviousShadow.scaleX = bindingView.actionPreviousShadow.scaleX * -1
// Custom Font // Custom Font
if (Preferences.customFont == Constants.CUSTOM_FONT_GOOGLE_SANS) { if (Preferences.customFont == Constants.CUSTOM_FONT_GOOGLE_SANS) {
@ -998,31 +999,31 @@ class MainWidget : AppWidgetProvider() {
} }
listOf<TextView>( listOf<TextView>(
v.empty_date, bindingView.emptyDate,
v.divider1, bindingView.divider1,
v.temp, bindingView.temp,
v.next_event, bindingView.nextEvent,
v.next_event_difference_time, bindingView.nextEventDifferenceTime,
v.next_event_date, bindingView.nextEventDate,
v.divider2, bindingView.divider2,
v.calendar_temp, bindingView.calendarTemp,
v.divider3, bindingView.divider3,
v.special_temp bindingView.specialTemp
).forEach { ).forEach {
it.typeface = googleSans it.typeface = googleSans
} }
} else if (Preferences.customFont == Constants.CUSTOM_FONT_DOWNLOADED && typeface != null) { } else if (Preferences.customFont == Constants.CUSTOM_FONT_DOWNLOADED && typeface != null) {
listOf<TextView>( listOf<TextView>(
v.empty_date, bindingView.emptyDate,
v.divider1, bindingView.divider1,
v.temp, bindingView.temp,
v.next_event, bindingView.nextEvent,
v.next_event_difference_time, bindingView.nextEventDifferenceTime,
v.next_event_date, bindingView.nextEventDate,
v.divider2, bindingView.divider2,
v.calendar_temp, bindingView.calendarTemp,
v.divider3, bindingView.divider3,
v.special_temp bindingView.specialTemp
).forEach { ).forEach {
it.typeface = typeface it.typeface = typeface
} }
@ -1030,9 +1031,9 @@ class MainWidget : AppWidgetProvider() {
// Weather // Weather
if (Preferences.showWeather && Preferences.weatherIcon != "") { if (Preferences.showWeather && Preferences.weatherIcon != "") {
v.weather.visibility = View.VISIBLE bindingView.weather.visibility = View.VISIBLE
v.calendar_weather.visibility = View.VISIBLE bindingView.calendarWeather.visibility = View.VISIBLE
v.special_weather.visibility = View.VISIBLE bindingView.specialWeather.visibility = View.VISIBLE
val currentTemp = String.format( val currentTemp = String.format(
Locale.getDefault(), Locale.getDefault(),
"%d °%s", "%d °%s",
@ -1042,41 +1043,41 @@ class MainWidget : AppWidgetProvider() {
val icon: String = Preferences.weatherIcon val icon: String = Preferences.weatherIcon
if (icon == "") { if (icon == "") {
v.weather_icon.visibility = View.GONE bindingView.weatherIcon.visibility = View.GONE
v.empty_weather_icon.visibility = View.GONE bindingView.emptyWeatherIcon.visibility = View.GONE
v.special_weather_icon.visibility = View.GONE bindingView.specialWeatherIcon.visibility = View.GONE
} else { } else {
v.weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon)) bindingView.weatherIcon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon))
v.empty_weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon)) bindingView.emptyWeatherIcon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon))
v.special_weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon)) bindingView.specialWeatherIcon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon))
v.weather_icon.visibility = View.VISIBLE bindingView.weatherIcon.visibility = View.VISIBLE
v.empty_weather_icon.visibility = View.VISIBLE bindingView.emptyWeatherIcon.visibility = View.VISIBLE
v.special_weather_icon.visibility = View.VISIBLE bindingView.specialWeatherIcon.visibility = View.VISIBLE
} }
v.temp.text = currentTemp bindingView.temp.text = currentTemp
v.calendar_temp.text = currentTemp bindingView.calendarTemp.text = currentTemp
v.special_temp.text = currentTemp bindingView.specialTemp.text = currentTemp
if (GlanceProviderHelper.showGlanceProviders(context)) { if (GlanceProviderHelper.showGlanceProviders(context)) {
v.calendar_weather.visibility = View.GONE bindingView.calendarWeather.visibility = View.GONE
} else { } else {
v.special_weather.visibility = View.GONE bindingView.specialWeather.visibility = View.GONE
} }
} else { } else {
v.weather.visibility = View.GONE bindingView.weather.visibility = View.GONE
v.calendar_weather.visibility = View.GONE bindingView.calendarWeather.visibility = View.GONE
v.special_weather.visibility = View.GONE bindingView.specialWeather.visibility = View.GONE
} }
// Dividers // Dividers
arrayOf(v.divider1, v.divider2, v.divider3).forEach { arrayOf(bindingView.divider1, bindingView.divider2, bindingView.divider3).forEach {
it.isVisible = Preferences.showDividers it.isVisible = Preferences.showDividers
} }
eventRepository.close() eventRepository.close()
return v return bindingView
} }
} }
} }

View File

@ -2,7 +2,6 @@ package com.tommasoberlose.anotherwidget.utils
import android.animation.* import android.animation.*
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewAnimationUtils import android.view.ViewAnimationUtils
import android.widget.Toast import android.widget.Toast
@ -21,22 +20,12 @@ import android.content.Intent
import android.content.res.Resources import android.content.res.Resources
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.util.Log
import android.util.TypedValue 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 android.widget.RelativeLayout
import androidx.browser.customtabs.CustomTabColorSchemeParams
import androidx.core.animation.addListener import androidx.core.animation.addListener
import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.tommasoberlose.anotherwidget.R 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.* import java.util.*
@ -149,7 +138,7 @@ fun View.collapse(duration: Long = 500L) {
fun Context.openURI(url: String) { fun Context.openURI(url: String) {
try { try {
val builder: CustomTabsIntent.Builder = CustomTabsIntent.Builder() 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() val customTabsIntent: CustomTabsIntent = builder.build()
customTabsIntent.launchUrl(this, Uri.parse(url)) customTabsIntent.launchUrl(this, Uri.parse(url))
} catch (e: Exception) { } catch (e: Exception) {

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

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