Compare commits

...

33 Commits

Author SHA1 Message Date
b04b103634 Update the build version 2020-05-13 19:04:17 +02:00
1dc050e77f Update event repository 2020-05-13 18:58:45 +02:00
ff171d4022 Update the events selector. Fix #95 2020-05-13 18:57:44 +02:00
d91471d1ee Fix #113, #111, #79, #107, #109 2020-05-13 17:42:48 +02:00
ac381c8542 Add weather icon pack, updates frequency and fix google fit 2020-05-12 23:54:07 +02:00
ba5a860e75 Update ui 2020-05-11 13:33:37 +02:00
09bc9df22f Update icons 2020-05-11 13:30:36 +02:00
385806413e Fix dark theme toggle issue 2020-05-11 12:40:42 +02:00
7d2ea5a4d8 Bugfixes 2020-05-11 00:38:32 +02:00
ce9f5a6e45 Update google fit integration 2020-05-11 00:00:31 +02:00
8f0f6bc868 Update beta release 2020-05-10 20:42:48 +02:00
34d8fa4b59 update gitignore 2020-05-10 20:32:48 +02:00
9401b89036 Clean project files 2020-05-10 20:31:50 +02:00
842c0d764f Add google fit connection 2020-05-10 20:29:35 +02:00
1754b4045b Add google fit integration 2020-05-10 14:08:40 +02:00
9a63b9bde2 Gix switcher, update glance, add glance order, add ampm toggle 2020-05-09 22:37:59 +02:00
26428b65da Merge crud-device 2020-05-09 17:58:03 +02:00
97d1caeabc Add dividers toggle 2020-05-09 15:11:12 +02:00
f013be5a7a Add integreations activity, removed long time until intervals 2020-05-08 20:16:48 +02:00
4cc55edb15 Merge music 2020-05-08 18:57:14 +02:00
d08ad6171e Change music fragment to at a glance 2020-05-08 18:55:08 +02:00
9e2eacef73 Update build version 2020-05-08 17:43:47 +02:00
f8f8a8f051 Merge calendar fix 2020-05-08 15:15:49 +02:00
60531061d7 Fix #83 2020-05-08 15:14:01 +02:00
654ec3fe66 Add current song 2020-05-08 15:10:19 +02:00
d86d2cadd4 Add clock text color 2020-05-08 11:55:42 +02:00
cb6c2c7764 Move app settings to a different fragment 2020-05-08 01:39:31 +02:00
6a4cfdf22b Update bitmap generator 2020-05-07 18:30:28 +02:00
0b9e9e081e Merge branch 'master' of github.com:tommasoberlose/another-widget 2020-05-07 16:19:53 +02:00
15884bd9b2 Fix helper 2020-05-07 16:19:30 +02:00
0adf192965 Fix #72 2020-05-07 15:51:09 +02:00
d72ba08960 Update issue templates 2020-05-07 14:14:26 +02:00
1848951a74 Update issue templates 2020-05-07 14:13:14 +02:00
1024 changed files with 4887 additions and 1619 deletions

32
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,32 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: tommasoberlose
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Smartphone (please complete the following information):**
- Device: [e.g. OnePlus 6]
- OS Version: [e.g. Android 9.0]
- App Version (that you can find inside the advanced settings) [e.g. v2.0.5 (71)]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: tommasoberlose
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

4
.gitignore vendored
View File

@ -7,4 +7,6 @@
/build /build
/captures /captures
.externalNativeBuild .externalNativeBuild
/tasksintegration/build /tasksintegration/build
apikey.properties
/app/google-services.json

Binary file not shown.

1
.idea/gradle.xml generated
View File

@ -11,7 +11,6 @@
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" /> <option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/tasksintegration" />
</set> </set>
</option> </option>
<option name="resolveModulePerSourceSet" value="false" /> <option name="resolveModulePerSourceSet" value="false" />

1
.idea/modules.xml generated
View File

@ -4,7 +4,6 @@
<modules> <modules>
<module fileurl="file://$PROJECT_DIR$/Another Widget.iml" filepath="$PROJECT_DIR$/Another Widget.iml" group="Another Widget" /> <module fileurl="file://$PROJECT_DIR$/Another Widget.iml" filepath="$PROJECT_DIR$/Another Widget.iml" group="Another Widget" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" group="Another Widget/app" /> <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" group="Another Widget/app" />
<module fileurl="file://$PROJECT_DIR$/tasksintegration/tasksintegration.iml" filepath="$PROJECT_DIR$/tasksintegration/tasksintegration.iml" group="Another Widget/tasksintegration" />
</modules> </modules>
</component> </component>
</project> </project>

52
.idea/navEditor.xml generated Normal file
View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="navEditor-manualLayoutAlgorithm2">
<option name="myPositions">
<map>
<entry key="nav_graph.xml">
<value>
<LayoutPositions>
<option name="myPositions">
<map>
<entry key="appMainFragment">
<value>
<LayoutPositions>
<option name="myPosition">
<Point>
<option name="x" value="242" />
<option name="y" value="352" />
</Point>
</option>
<option name="myPositions">
<map>
<entry key="action_appMainFragment_to_appSettingsFragment">
<value>
<LayoutPositions />
</value>
</entry>
</map>
</option>
</LayoutPositions>
</value>
</entry>
<entry key="appSettingsFragment">
<value>
<LayoutPositions>
<option name="myPosition">
<Point>
<option name="x" value="532" />
<option name="y" value="353" />
</Point>
</option>
</LayoutPositions>
</value>
</entry>
</map>
</option>
</LayoutPositions>
</value>
</entry>
</map>
</option>
</component>
</project>

View File

@ -10,6 +10,10 @@ apply plugin: 'kotlin-android-extensions'
apply plugin: 'realm-android' apply plugin: 'realm-android'
def apiKeyPropertiesFile = rootProject.file("apikey.properties")
def apiKeyProperties = new Properties()
apiKeyProperties.load(new FileInputStream(apiKeyPropertiesFile))
android { android {
compileSdkVersion 29 compileSdkVersion 29
buildToolsVersion "29.0.3" buildToolsVersion "29.0.3"
@ -18,10 +22,12 @@ android {
applicationId "com.tommasoberlose.anotherwidget" applicationId "com.tommasoberlose.anotherwidget"
minSdkVersion 23 minSdkVersion 23
targetSdkVersion 29 targetSdkVersion 29
versionCode 74 versionCode 90
versionName "2.0.5" versionName "2.0.9"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
manifestPlaceholders = [ "AWARENESS_API_KEY": apiKeyProperties['AWARENESS_API_KEY']]
} }
buildTypes { buildTypes {
@ -52,8 +58,6 @@ android {
} }
viewBinding.enabled = true viewBinding.enabled = true
dynamicFeatures = [":tasksintegration"]
} }
dependencies { dependencies {
@ -96,6 +100,10 @@ dependencies {
implementation 'com.github.bumptech.glide:glide:4.11.0' implementation 'com.github.bumptech.glide:glide:4.11.0'
kapt 'com.github.bumptech.glide:compiler:4.11.0' kapt 'com.github.bumptech.glide:compiler:4.11.0'
// Fitness
implementation 'com.google.android.gms:play-services-fitness:18.0.0'
implementation 'com.google.android.gms:play-services-auth:18.0.0'
//Weather //Weather
implementation 'com.github.KwabenBerko:OpenWeatherMap-Android-Library:2.0.2' implementation 'com.github.KwabenBerko:OpenWeatherMap-Android-Library:2.0.2'
implementation 'com.google.android.gms:play-services-location:17.0.0' implementation 'com.google.android.gms:play-services-location:17.0.0'
@ -110,9 +118,6 @@ dependencies {
implementation "androidx.palette:palette-ktx:1.0.0" implementation "androidx.palette:palette-ktx:1.0.0"
implementation 'androidx.core:core-ktx:1.2.0' implementation 'androidx.core:core-ktx:1.2.0'
// Recommended: Add the Firebase SDK for Google Analytics.
implementation 'com.google.firebase:firebase-analytics:17.4.0'
// Add the Firebase SDK for Crashlytics. // Add the Firebase SDK for Crashlytics.
implementation 'com.google.firebase:firebase-crashlytics:17.0.0' implementation 'com.google.firebase:firebase-crashlytics:17.0.0'

View File

@ -1,40 +0,0 @@
{
"project_info": {
"project_number": "791844924473",
"firebase_url": "https://anotherwidget-182008.firebaseio.com",
"project_id": "anotherwidget-182008",
"storage_bucket": "anotherwidget-182008.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:791844924473:android:0ad4f6e3890f1ad320b1e8",
"android_client_info": {
"package_name": "com.tommasoberlose.anotherwidget"
}
},
"oauth_client": [
{
"client_id": "791844924473-73dh46rorjq8vm97dgbn6can2dcpqlf0.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyAeJRXstqnzebibxmm3FRM98nbwE_kC8tA"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "791844924473-73dh46rorjq8vm97dgbn6can2dcpqlf0.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}

Binary file not shown.

View File

@ -10,6 +10,10 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.android.vending.BILLING" /> <uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.gms.permission.ACTIVITY_RECOGNITION"/>
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<application <application
android:allowBackup="true" android:allowBackup="true"
@ -21,7 +25,7 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
tools:ignore="LockedOrientationActivity"> tools:ignore="LockedOrientationActivity">
<activity android:name=".ui.activities.MainActivity" android:launchMode="singleInstance" android:theme="@style/AppTheme.Main" android:screenOrientation="portrait"> <activity android:name=".ui.activities.MainActivity" android:launchMode="singleInstance" android:theme="@style/AppTheme.Main">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -33,6 +37,7 @@
<activity android:name=".ui.activities.WeatherProviderActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" /> <activity android:name=".ui.activities.WeatherProviderActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.SupportDevActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" /> <activity android:name=".ui.activities.SupportDevActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.CustomDateActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" /> <activity android:name=".ui.activities.CustomDateActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.IntegrationsActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<receiver android:name=".ui.widgets.MainWidget"> <receiver android:name=".ui.widgets.MainWidget">
@ -81,44 +86,16 @@
android:enabled="true" android:enabled="true"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="com.tommasoberlose.anotherwidget.action.ACTION_WEATHER_UPDATE" /> <action android:name="com.tommasoberlose.anotherwidget.action.ACTION_WEATHER_UPDATE" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" /> <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.TIME_SET" /> <action android:name="android.intent.action.TIME_SET" />
<action android:name="android.intent.action.TIMEZONE_CHANGED" /> <action android:name="android.intent.action.TIMEZONE_CHANGED" />
<action android:name="android.intent.action.LOCALE_CHANGED" /> <action android:name="android.intent.action.LOCALE_CHANGED" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver
android:name=".receivers.PlayerReceiver"
android:enabled="true"
android:exported="true"
tools:ignore="ExportedReceiver">
<intent-filter>
<action android:name="com.android.music.metachanged" />
<action android:name="com.android.music.playstatechanged" />
<action android:name="com.android.music.playbackcomplete" />
<action android:name="com.android.music.queuechanged" />
<action android:name="com.htc.music.metachanged" />
<action android:name="fm.last.android.metachanged" />
<action android:name="com.sec.android.app.music.metachanged" />
<action android:name="com.nullsoft.winamp.metachanged" />
<action android:name="com.amazon.mp3.metachanged" />
<action android:name="com.miui.player.metachanged" />
<action android:name="com.real.IMP.metachanged" />
<action android:name="com.sonyericsson.music.metachanged" />
<action android:name="com.rdio.android.metachanged" />
<action android:name="com.samsung.sec.android.MusicPlayer.metachanged" />
<action android:name="com.andrew.apollo.metachanged" />
<action android:name="com.spotify.music.playbackstatechanged"/>
<action android:name="com.spotify.music.metadatachanged"/>
<action android:name="com.spotify.music.queuechanged"/>
</intent-filter>
</receiver>
<receiver <receiver
android:name=".receivers.WidgetClickListenerReceiver" android:name=".receivers.WidgetClickListenerReceiver"
android:enabled="true" android:enabled="true"
@ -139,6 +116,33 @@
<service android:name=".services.EventListenerJob" android:permission="android.permission.BIND_JOB_SERVICE" /> <service android:name=".services.EventListenerJob" android:permission="android.permission.BIND_JOB_SERVICE" />
<service android:name=".receivers.MusicNotificationListener"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<receiver android:name=".receivers.BatteryLevelReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
<action android:name="android.intent.action.BATTERY_LOW"/>
<action android:name="android.intent.action.BATTERY_OKAY"/>
</intent-filter>
</receiver>
<receiver android:name=".receivers.ActivityDetectionReceiver"
android:exported="false"
android:permission="com.google.android.gms.permission.ACTIVITY_RECOGNITION">
<intent-filter>
<action android:name="com.mypackage.ACTION_PROCESS_ACTIVITY_TRANSITIONS" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application> </application>
</manifest> </manifest>

View File

@ -1,6 +1,7 @@
package com.tommasoberlose.anotherwidget package com.tommasoberlose.anotherwidget
import android.app.Application import android.app.Application
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

View File

@ -16,6 +16,7 @@ import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
@ -79,61 +80,51 @@ class BottomSheetColorPicker(
// List // List
view.menu.setHasFixedSize(true)
val mLayoutManager = GridLayoutManager(context, 6)
view.menu.layoutManager = mLayoutManager
adapter = SlimAdapter.create() adapter = SlimAdapter.create()
adapter
.register<Int>(R.layout.color_picker_menu_item) { item, injector ->
injector
.with<MaterialCardView>(R.id.color) {
loadingJobs.add(GlobalScope.launch(Dispatchers.IO) {
val colorList = ColorStateList.valueOf(item)
withContext(Dispatchers.Main) {
it.setCardBackgroundColor(colorList)
}
})
}
.with<AppCompatImageView>(R.id.check) {
if (getSelected?.invoke() == item) {
loadingJobs.add(GlobalScope.launch(Dispatchers.IO) {
val colorList = ContextCompat.getColor(
context,
if (item.isColorDark()) android.R.color.white else android.R.color.black
)
withContext(Dispatchers.Main) {
it.setColorFilter(
colorList,
android.graphics.PorterDuff.Mode.MULTIPLY
)
it.isVisible = true
}
})
} else {
it.isVisible = false
}
}
injector.clicked(R.id.color) {
adapter.notifyItemChanged(adapter.data.indexOf(getSelected?.invoke()))
onColorSelected?.invoke(item)
val position = adapter.data.indexOf(item)
adapter.notifyItemChanged(position)
(view.menu.layoutManager as GridLayoutManager).scrollToPositionWithOffset(position,0)
}
}
.attachTo(view.menu)
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
listView.setHasFixedSize(true)
val mLayoutManager = GridLayoutManager(context, 6)
listView.layoutManager = mLayoutManager
adapter
.register<Int>(R.layout.color_picker_menu_item) { item, injector ->
injector
.with<MaterialCardView>(R.id.color) {
it.setCardBackgroundColor(ColorStateList.valueOf(item))
}
.with<AppCompatImageView>(R.id.check) {
if (getSelected?.invoke() == item) {
it.setColorFilter(
ContextCompat.getColor(
context,
if (item.isColorDark()) android.R.color.white else android.R.color.black
),
android.graphics.PorterDuff.Mode.MULTIPLY
)
it.isVisible = true
} else {
it.isVisible = false
}
}
.clicked(R.id.color) {
adapter.notifyItemChanged(adapter.data.indexOf(getSelected?.invoke()))
onColorSelected?.invoke(item)
val position = adapter.data.indexOf(item)
adapter.notifyItemChanged(position)
(listView.layoutManager as GridLayoutManager).scrollToPositionWithOffset(position,0)
}
}
.attachTo(listView)
adapter.updateData(colors.toList()) adapter.updateData(colors.toList())
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
view.color_loader.isVisible = false view.color_loader.isVisible = false
view.list_container.addView(listView)
this@BottomSheetColorPicker.behavior.state = BottomSheetBehavior.STATE_EXPANDED this@BottomSheetColorPicker.behavior.state = BottomSheetBehavior.STATE_EXPANDED
// this@BottomSheetColorPicker.behavior.isFitToContents = false view.list_container.isVisible = true
view.menu.isVisible = true
} }
}) })

View File

@ -0,0 +1,27 @@
package com.tommasoberlose.anotherwidget.components
import android.content.Context
import android.view.View
import androidx.appcompat.app.AlertDialog
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.global.Preferences
import kotlinx.android.synthetic.main.custom_notes_dialog_layout.view.*
class CustomNotesDialog(context: Context) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
init {
val view = View.inflate(context, R.layout.custom_notes_dialog_layout, null)
view.notes.setText(Preferences.customNotes)
view.action_positive.setOnClickListener {
Preferences.customNotes = view.notes.text.toString()
this.dismiss()
}
view.notes.requestFocus()
setContentView(view)
}
}

View File

@ -0,0 +1,98 @@
package com.tommasoberlose.anotherwidget.components
import android.content.Context
import android.content.res.ColorStateList
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.card.MaterialCardView
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark
import com.tommasoberlose.anotherwidget.helpers.GlanceProviderHelper
import com.tommasoberlose.anotherwidget.models.GlanceProvider
import kotlinx.android.synthetic.main.glance_provider_sort_bottom_menu.view.*
import kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter
import java.util.*
import kotlin.collections.ArrayList
class GlanceProviderSortMenu(
context: Context
) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
private lateinit var adapter: SlimAdapter
override fun show() {
val view = View.inflate(context, R.layout.glance_provider_sort_bottom_menu, null)
// Header
view.header_text.text = context.getString(R.string.settings_sort_glance_providers_title)
// List
adapter = SlimAdapter.create()
view.menu.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(context)
view.menu.layoutManager = mLayoutManager
adapter = SlimAdapter.create()
adapter
.register<GlanceProvider>(R.layout.glance_provider_item) { item, injector ->
injector
.text(R.id.title, item.title)
.with<ImageView>(R.id.icon) {
it.setImageDrawable(ContextCompat.getDrawable(context, item.icon))
}
}
.attachTo(view.menu)
val mIth = ItemTouchHelper(
object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
ItemTouchHelper.LEFT
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder
): Boolean {
val fromPos = viewHolder.adapterPosition
val toPos = target.adapterPosition
// move item in `fromPos` to `toPos` in adapter.
adapter.notifyItemMoved(fromPos, toPos)
val list = GlanceProviderHelper.getGlanceProviders(context)
Collections.swap(list, fromPos, toPos)
GlanceProviderHelper.saveGlanceProviderOrder(list)
return true
}
override fun onSwiped(
viewHolder: RecyclerView.ViewHolder,
direction: Int
) {
// remove from adapter
}
})
mIth.attachToRecyclerView(view.menu)
adapter.updateData(
GlanceProviderHelper.getGlanceProviders(context)
.mapNotNull { GlanceProviderHelper.getGlanceProviderById(context, it) }
)
setContentView(view)
super.show()
}
}

View File

@ -1,6 +1,7 @@
package com.tommasoberlose.anotherwidget.db package com.tommasoberlose.anotherwidget.db
import android.content.Context import android.content.Context
import android.util.Log
import com.chibatching.kotpref.bulk import com.chibatching.kotpref.bulk
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.models.Event import com.tommasoberlose.anotherwidget.models.Event
@ -8,22 +9,20 @@ import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import io.realm.Realm import io.realm.Realm
import io.realm.RealmResults import io.realm.RealmResults
import java.util.*
import kotlin.collections.ArrayList
class EventRepository(val context: Context) { class EventRepository(val context: Context) {
private val realm by lazy { Realm.getDefaultInstance() } private val realm by lazy { Realm.getDefaultInstance() }
fun saveEvents(eventList: ArrayList<Event>) { fun saveEvents(eventList: ArrayList<Event>) {
realm.executeTransactionAsync { realm -> realm.executeTransaction { realm ->
realm.where(Event::class.java).findAll().deleteAllFromRealm() realm.where(Event::class.java).findAll().deleteAllFromRealm()
realm.copyToRealm(eventList) realm.copyToRealm(eventList)
} }
} }
fun resetNextEventData() { fun resetNextEventData() {
realm.executeTransactionAsync {
it.where(Event::class.java).findAll().deleteAllFromRealm()
}
Preferences.bulk { Preferences.bulk {
remove(Preferences::nextEventId) remove(Preferences::nextEventId)
remove(Preferences::nextEventName) remove(Preferences::nextEventName)
@ -36,22 +35,48 @@ class EventRepository(val context: Context) {
} }
fun saveNextEventData(event: Event) { fun saveNextEventData(event: Event) {
Preferences.nextEventId = event.id Preferences.nextEventId = event.eventID
} }
fun getNextEvent(): Event? = realm.where(Event::class.java).equalTo("id", Preferences.nextEventId).findFirst() ?: realm.where(Event::class.java).findFirst() fun getNextEvent(): Event? {
val nextEvent = getEventByEventId(Preferences.nextEventId)
val event = if (nextEvent != null && nextEvent.endDate > Calendar.getInstance().timeInMillis) {
nextEvent
} else {
val events = getEvents()
if (events.isNotEmpty()) {
val newNextEvent = events.first()
Preferences.nextEventId = newNextEvent!!.eventID
newNextEvent
} else {
resetNextEventData()
null
}
}
return try {
realm.copyFromRealm(event!!)
} catch (ex: Exception) {
event
}
}
fun getEventByEventId(id: Long): Event? = realm.where(Event::class.java).equalTo("eventID", id).findFirst() fun getEventByEventId(id: Long): Event? {
val event = realm.where(Event::class.java).equalTo("eventID", id).findFirst()
return try {
realm.copyFromRealm(event!!)
} catch (ex: Exception) {
event
}
}
fun goToNextEvent() { fun goToNextEvent() {
val eventList = realm.where(Event::class.java).findAll() val eventList = getEvents()
if (eventList.isNotEmpty()) { if (eventList.isNotEmpty()) {
val index = eventList.indexOfFirst { it.id == Preferences.nextEventId } val index = eventList.indexOfFirst { it.eventID == Preferences.nextEventId }
if (index > -1 && index < eventList.size - 1) { if (index > -1 && index < eventList.size - 1) {
Preferences.nextEventId = eventList[index + 1]!!.id Preferences.nextEventId = eventList[index + 1]!!.eventID
} else { } else {
Preferences.nextEventId = eventList.first()!!.id Preferences.nextEventId = eventList.first()!!.eventID
} }
} else { } else {
resetNextEventData() resetNextEventData()
@ -61,14 +86,13 @@ class EventRepository(val context: Context) {
} }
fun goToPreviousEvent() { fun goToPreviousEvent() {
val eventList = realm.where(Event::class.java).findAll() val eventList = getEvents()
if (eventList.isNotEmpty()) { if (eventList.isNotEmpty()) {
val index = eventList.indexOfFirst { it.id == Preferences.nextEventId } val index = eventList.indexOfFirst { it.eventID == Preferences.nextEventId }
if (index > 0) { if (index > 0) {
Preferences.nextEventId = eventList[index - 1]!!.id Preferences.nextEventId = eventList[index - 1]!!.eventID
} else { } else {
Preferences.nextEventId = eventList.last()!!.id Preferences.nextEventId = eventList.last()!!.eventID
} }
} else { } else {
resetNextEventData() resetNextEventData()
@ -77,7 +101,15 @@ class EventRepository(val context: Context) {
MainWidget.updateWidget(context) MainWidget.updateWidget(context)
} }
fun getEvents(): RealmResults<Event> = realm.where(Event::class.java).findAll() fun getEvents(): RealmResults<Event> {
val now = Calendar.getInstance().timeInMillis
realm.refresh()
return realm.where(Event::class.java).greaterThan("endDate", now).findAll()
}
fun getEventsCount(): Int = realm.where(Event::class.java).findAll().size fun getEventsCount(): Int = getEvents().size
fun close() {
realm.close()
}
} }

View File

@ -14,4 +14,23 @@ object Constants {
MEDIUM(2), MEDIUM(2),
LARGE(3) LARGE(3)
} }
enum class GlanceProviderId(val id: String) {
PLAYING_SONG("PLAYING_SONG"),
NEXT_CLOCK_ALARM("NEXT_CLOCK_ALARM"),
BATTERY_LEVEL_LOW("BATTERY_LEVEL_LOW"),
CUSTOM_INFO("CUSTOM_INFO"),
GOOGLE_FIT_STEPS("GOOGLE_FIT_STEPS")
}
enum class WidgetUpdateFrequency(val value: Int) {
LOW(0),
DEFAULT(1),
HIGH(2)
}
enum class WeatherIconPack(val value: Int) {
DEFAULT(0),
MINIMAL(1)
}
} }

View File

@ -9,6 +9,7 @@ object Preferences : KotprefModel() {
var darkThemePreference by intPref(default = MODE_NIGHT_FOLLOW_SYSTEM) var darkThemePreference by intPref(default = MODE_NIGHT_FOLLOW_SYSTEM)
// Calendar and weather
var showEvents by booleanPref(key = "PREF_SHOW_EVENTS", default = false) var showEvents by booleanPref(key = "PREF_SHOW_EVENTS", default = false)
var showWeather by booleanPref(key = "PREF_SHOW_WEATHER", default = false) var showWeather by booleanPref(key = "PREF_SHOW_WEATHER", default = false)
var weatherIcon by stringPref(key = "PREF_WEATHER_ICON", default = "") var weatherIcon by stringPref(key = "PREF_WEATHER_ICON", default = "")
@ -29,6 +30,7 @@ object Preferences : KotprefModel() {
var customLocationLon by stringPref(key = "PREF_CUSTOM_LOCATION_LON", default = "") var customLocationLon by stringPref(key = "PREF_CUSTOM_LOCATION_LON", default = "")
var customLocationAdd by stringPref(key = "PREF_CUSTOM_LOCATION_ADD", default = "") var customLocationAdd by stringPref(key = "PREF_CUSTOM_LOCATION_ADD", default = "")
var dateFormat by stringPref(default = "") var dateFormat by stringPref(default = "")
var isDateCapitalize by booleanPref(default = true)
var weatherRefreshPeriod by intPref(key = "PREF_WEATHER_REFRESH_PERIOD", default = 1) var weatherRefreshPeriod by intPref(key = "PREF_WEATHER_REFRESH_PERIOD", default = 1)
var showUntil by intPref(key = "PREF_SHOW_UNTIL", default = 1) var showUntil by intPref(key = "PREF_SHOW_UNTIL", default = 1)
var calendarAppName by stringPref(key = "PREF_CALENDAR_APP_NAME", default = "") var calendarAppName by stringPref(key = "PREF_CALENDAR_APP_NAME", default = "")
@ -39,12 +41,25 @@ object Preferences : KotprefModel() {
var eventAppName by stringPref(key = "PREF_EVENT_APP_NAME", default = "") var eventAppName by stringPref(key = "PREF_EVENT_APP_NAME", default = "")
var eventAppPackage by stringPref(key = "PREF_EVENT_APP_PACKAGE", default = "") var eventAppPackage by stringPref(key = "PREF_EVENT_APP_PACKAGE", default = "")
var openEventDetails by booleanPref(default = true) var openEventDetails by booleanPref(default = true)
var widgetUpdateFrequency by intPref(default = Constants.WidgetUpdateFrequency.DEFAULT.value)
var textGlobalColor by stringPref(key = "PREF_TEXT_COLOR", default = "#FFFFFF") var textGlobalColor by stringPref(key = "PREF_TEXT_COLOR", default = "#FFFFFF")
var textGlobalAlpha by stringPref(default = "FF") var textGlobalAlpha by stringPref(default = "FF")
var textSecondaryColor by stringPref(default = "#FFFFFF")
var textSecondaryAlpha by stringPref(default = "FF")
var backgroundCardColor by stringPref(default = "#000000") var backgroundCardColor by stringPref(default = "#000000")
var backgroundCardAlpha by stringPref(default = "00") var backgroundCardAlpha by stringPref(default = "00")
var clockTextColor by stringPref(default = "#FFFFFF")
var clockTextAlpha by stringPref(default = "FF")
var showAMPMIndicator by booleanPref(default = true)
var weatherIconPack by intPref(default = Constants.WeatherIconPack.DEFAULT.value)
// Global
var textMainSize by floatPref(key = "PREF_TEXT_MAIN_SIZE", default = 26f) var textMainSize by floatPref(key = "PREF_TEXT_MAIN_SIZE", default = 26f)
var textSecondSize by floatPref(key = "PREF_TEXT_SECOND_SIZE", default = 18f) var textSecondSize by floatPref(key = "PREF_TEXT_SECOND_SIZE", default = 18f)
var clockTextSize by floatPref(key = "PREF_TEXT_CLOCK_SIZE", default = 90f) var clockTextSize by floatPref(key = "PREF_TEXT_CLOCK_SIZE", default = 90f)
@ -52,7 +67,6 @@ object Preferences : KotprefModel() {
var showClock by booleanPref(key = "PREF_SHOW_CLOCK", default = false) var showClock by booleanPref(key = "PREF_SHOW_CLOCK", default = false)
var clockAppName by stringPref(key = "PREF_CLOCK_APP_NAME", default = "") var clockAppName by stringPref(key = "PREF_CLOCK_APP_NAME", default = "")
var clockAppPackage by stringPref(key = "PREF_CLOCK_APP_PACKAGE", default = "") var clockAppPackage by stringPref(key = "PREF_CLOCK_APP_PACKAGE", default = "")
var showNextAlarm by booleanPref(default = false)
var textShadow by intPref(key = "PREF_TEXT_SHADOW", default = 1) var textShadow by intPref(key = "PREF_TEXT_SHADOW", default = 1)
var showDiffTime by booleanPref(key = "PREF_SHOW_DIFF_TIME", default = true) var showDiffTime by booleanPref(key = "PREF_SHOW_DIFF_TIME", default = true)
var showDeclinedEvents by booleanPref(key = "PREF_SHOW_DECLINED_EVENTS", default = false) var showDeclinedEvents by booleanPref(key = "PREF_SHOW_DECLINED_EVENTS", default = false)
@ -61,9 +75,32 @@ object Preferences : KotprefModel() {
var customFontFile by stringPref(key = "PREF_CUSTOM_FONT_FILE") var customFontFile by stringPref(key = "PREF_CUSTOM_FONT_FILE")
var showNextEvent by booleanPref(key = "PREF_SHOW_NEXT_EVENT", default = true) var showNextEvent by booleanPref(key = "PREF_SHOW_NEXT_EVENT", default = true)
var showDividers by booleanPref(default = true)
// Settings
var showWallpaper by booleanPref(default = true) var showWallpaper by booleanPref(default = true)
var showBigClockWarning by booleanPref(default = true) var showBigClockWarning by booleanPref(default = true)
var showWeatherWarning 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
var showGlance by booleanPref(default = true)
var enabledGlanceProviderOrder by stringPref(default = "")
var customNotes by stringPref(default = "")
var showNextAlarm by booleanPref(default = true)
var showBatteryCharging by booleanPref(default = false)
var isBatteryLevelLow by booleanPref(default = false)
var googleFitSteps by longPref(default = -1)
var showDailySteps by booleanPref(default = false)
var showMusic by booleanPref(default = false)
var mediaInfoFormat by stringPref(default = "")
var mediaPlayerTitle by stringPref(default = "")
var mediaPlayerAlbum by stringPref(default = "")
var mediaPlayerArtist by stringPref(default = "")
var mediaPlayerPackage by stringPref(default = "")
// Integrations
var installedIntegrations by intPref(default = 0)
} }

View File

@ -15,22 +15,43 @@ object BitmapHelper {
fun getBitmapFromView(view: View, width: Int? = null, height: Int? = null, draw: Boolean = true): Bitmap { fun getBitmapFromView(view: View, width: Int? = null, height: Int? = null, draw: Boolean = true): Bitmap {
//Define a bitmap with the same size as the view //Define a bitmap with the same size as the view
val measuredWidth = View.MeasureSpec.makeMeasureSpec(width ?: view.width, if (width != null) View.MeasureSpec.EXACTLY else View.MeasureSpec.UNSPECIFIED) val measuredWidth = View.MeasureSpec.makeMeasureSpec(width ?: view.width, if (width != null) View.MeasureSpec.EXACTLY else View.MeasureSpec.AT_MOST)
val measuredHeight = View.MeasureSpec.makeMeasureSpec(height ?: view.height, if (height != null) View.MeasureSpec.EXACTLY else View.MeasureSpec.UNSPECIFIED) val measuredHeight = View.MeasureSpec.makeMeasureSpec(height ?: view.height, if (height != null) View.MeasureSpec.EXACTLY else View.MeasureSpec.UNSPECIFIED)
view.measure(measuredWidth, measuredHeight) view.measure(
if (measuredWidth > 0) measuredWidth else 0,
if (measuredHeight > 0) measuredHeight else 0
)
val calculatedWidth = view.measuredWidth
val widgetWidth = if (calculatedWidth in 1..16000) {
calculatedWidth
} else if (width != null && width > 0) {
width
} else {
1
}
val calculatedHeight = view.measuredHeight
val widgetHeight = if (calculatedHeight in 1..16000) {
calculatedHeight
} else if (height != null && height > 0) {
height
} else {
1
}
if (draw) { if (draw) {
FirebaseCrashlytics.getInstance().setCustomKey("measuredWidth", view.measuredWidth) FirebaseCrashlytics.getInstance().setCustomKey("WIDTH SPEC", measuredWidth)
FirebaseCrashlytics.getInstance().setCustomKey("measuredWidth_spec", measuredWidth) FirebaseCrashlytics.getInstance().setCustomKey("HEIGHT SPEC", measuredHeight)
FirebaseCrashlytics.getInstance().setCustomKey("measuredHeight", view.measuredHeight) FirebaseCrashlytics.getInstance().setCustomKey("VIEW measuredWidth", view.measuredWidth)
FirebaseCrashlytics.getInstance() FirebaseCrashlytics.getInstance().setCustomKey("VIEW measuredHeight", view.measuredHeight)
.setCustomKey("measuredHeight_spec", measuredHeight) FirebaseCrashlytics.getInstance().setCustomKey("WIDGET final width", measuredWidth)
FirebaseCrashlytics.getInstance().setCustomKey("WIDGET final height", view.measuredHeight)
} }
return try { return try {
val btm = Bitmap.createBitmap( val btm = Bitmap.createBitmap(
view.measuredWidth, widgetWidth,
view.measuredHeight, widgetHeight,
if (draw) Bitmap.Config.ARGB_8888 else Bitmap.Config.ALPHA_8 if (draw) Bitmap.Config.ARGB_8888 else Bitmap.Config.ALPHA_8
) )
if (draw) { if (draw) {

View File

@ -10,7 +10,7 @@ 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.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.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
@ -31,6 +31,12 @@ object CalendarHelper {
val eventList = ArrayList<Event>() val eventList = ArrayList<Event>()
val now = Calendar.getInstance() 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() val limit = Calendar.getInstance()
when (Preferences.showUntil) { when (Preferences.showUntil) {
0 -> limit.add(Calendar.HOUR, 3) 0 -> limit.add(Calendar.HOUR, 3)
@ -45,7 +51,7 @@ object CalendarHelper {
} }
val builder = CalendarContract.Instances.CONTENT_URI.buildUpon() val builder = CalendarContract.Instances.CONTENT_URI.buildUpon()
ContentUris.appendId(builder, now.timeInMillis) ContentUris.appendId(builder, begin.timeInMillis)
ContentUris.appendId(builder, limit.timeInMillis) ContentUris.appendId(builder, limit.timeInMillis)
if (!context.checkGrantedPermission( if (!context.checkGrantedPermission(
@ -56,13 +62,13 @@ object CalendarHelper {
} else { } else {
try { try {
val provider = CalendarProvider(context) val provider = CalendarProvider(context)
val data = provider.getInstances(now.timeInMillis, limit.timeInMillis) val data = provider.getInstances(begin.timeInMillis, limit.timeInMillis)
if (data != null) { if (data != null) {
val instances = data.list val instances = data.list
for (instance in instances) { for (instance in instances) {
try { try {
val e = provider.getEvent(instance.eventId) val e = provider.getEvent(instance.eventId)
if (e != null && !e.deleted && instance.begin <= limit.timeInMillis && (Preferences.calendarAllDay || !e.allDay) && !getFilteredCalendarIdList().contains( if (e != null && !e.deleted && instance.begin <= limit.timeInMillis && now.timeInMillis < instance.end && (Preferences.calendarAllDay || !e.allDay) && !getFilteredCalendarIdList().contains(
e.calendarId e.calendarId
) && (Preferences.showDeclinedEvents || e.selfAttendeeStatus.toInt() != CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED) ) && (Preferences.showDeclinedEvents || e.selfAttendeeStatus.toInt() != CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED)
) { ) {
@ -127,7 +133,7 @@ object CalendarHelper {
UpdatesReceiver.setUpdates(context) UpdatesReceiver.setUpdates(context)
MainWidget.updateWidget(context) MainWidget.updateWidget(context)
EventBus.getDefault().post(MainActivity.UpdateUiMessageEvent()) EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
} }
fun getCalendarList(context: Context): List<me.everything.providers.android.calendar.Calendar> { fun getCalendarList(context: Context): List<me.everything.providers.android.calendar.Calendar> {

View File

@ -31,6 +31,54 @@ object ColorHelper {
} }
} }
fun getSecondaryFontColor(): Int {
return try {
Color.parseColor("#%s%s".format(Preferences.textSecondaryAlpha, Preferences.textSecondaryColor.replace("#", "")))
} catch (e: Exception) {
Color.parseColor("#FFFFFFFF")
}
}
fun getSecondaryFontColorAlpha(): Int {
return try {
Preferences.textSecondaryAlpha.toIntValue().toDouble() * 255 / 100
} catch (e: Exception) {
"FF".toIntValue().toDouble() * 255 / 100
}.roundToInt()
}
fun getSecondaryFontColorRgb(): Int {
return try {
Color.parseColor(Preferences.textSecondaryColor)
} catch (e: Exception) {
Color.parseColor("#000000")
}
}
fun getClockFontColor(): Int {
return try {
Color.parseColor("#%s%s".format(Preferences.clockTextAlpha, Preferences.clockTextColor.replace("#", "")))
} catch (e: Exception) {
Color.parseColor("#FFFFFFFF")
}
}
fun getClockFontColorAlpha(): Int {
return try {
Preferences.clockTextAlpha.toIntValue().toDouble() * 255 / 100
} catch (e: Exception) {
"FF".toIntValue().toDouble() * 255 / 100
}.roundToInt()
}
fun getClockFontColorRgb(): Int {
return try {
Color.parseColor(Preferences.clockTextColor)
} catch (e: Exception) {
Color.parseColor("#000000")
}
}
fun getBackgroundColor(): Int { fun getBackgroundColor(): Int {
return try { return try {
Color.parseColor("#%s%s".format(Preferences.backgroundCardAlpha, Preferences.backgroundCardColor.replace("#", ""))) Color.parseColor("#%s%s".format(Preferences.backgroundCardAlpha, Preferences.backgroundCardColor.replace("#", "")))

View File

@ -12,27 +12,30 @@ import java.util.*
object DateHelper { object DateHelper {
fun getDateText(context: Context, date: Calendar): String { fun getDateText(context: Context, date: Calendar): String {
return if (Preferences.dateFormat != "") { return if (Preferences.dateFormat != "") {
try { val text = try {
SimpleDateFormat(Preferences.dateFormat, Locale.getDefault()).format(date.time) SimpleDateFormat(Preferences.dateFormat, Locale.getDefault()).format(date.time)
} catch (e: Exception) { } catch (e: Exception) {
getDefaultDateText(context, date) getDefaultDateText(context, date)
} }
if (Preferences.isDateCapitalize) text.getCapWordString() else text
} else { } else {
val flags: Int = val flags: Int =
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
"%s, %s".format( val text = "%s, %s".format(
SimpleDateFormat("EEEE", Locale.getDefault()).format(date.time), SimpleDateFormat("EEEE", Locale.getDefault()).format(date.time),
DateUtils.formatDateTime(context, date.timeInMillis, flags) DateUtils.formatDateTime(context, date.timeInMillis, flags)
).getCapWordString() )
if (Preferences.isDateCapitalize) text.getCapWordString() else text
} }
} }
fun getDefaultDateText(context: Context, date: Calendar): String { fun getDefaultDateText(context: Context, date: Calendar): String {
val flags: Int = val flags: Int =
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
return "%s, %s".format( val text = "%s, %s".format(
SimpleDateFormat("EEEE", Locale.getDefault()).format(date.time), SimpleDateFormat("EEEE", Locale.getDefault()).format(date.time),
DateUtils.formatDateTime(context, date.timeInMillis, flags) DateUtils.formatDateTime(context, date.timeInMillis, flags)
).getCapWordString() )
return if (Preferences.isDateCapitalize) text.getCapWordString() else text
} }
} }

View File

@ -0,0 +1,90 @@
package com.tommasoberlose.anotherwidget.helpers
import android.content.Context
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.models.GlanceProvider
import com.tommasoberlose.anotherwidget.utils.checkIfFitInstalled
import java.util.ArrayList
object GlanceProviderHelper {
fun getGlanceProviders(context: Context): ArrayList<Constants.GlanceProviderId> {
val enabledProviders = Preferences.enabledGlanceProviderOrder.split(",").filter { it != "" }
val providers = Constants.GlanceProviderId.values()
.filter { it != Constants.GlanceProviderId.BATTERY_LEVEL_LOW }
.filter {
context.checkIfFitInstalled() || it != Constants.GlanceProviderId.GOOGLE_FIT_STEPS
}.toTypedArray()
providers.sortWith(Comparator { p1, p2 ->
when {
enabledProviders.contains(p1.id) && enabledProviders.contains(p2.id) -> {
enabledProviders.indexOf(p1.id).compareTo(enabledProviders.indexOf(p2.id))
}
enabledProviders.contains(p1.id) -> {
-1
}
enabledProviders.contains(p2.id) -> {
1
}
else -> {
p1.id.compareTo(p2.id)
}
}
})
return ArrayList(providers.toList())
}
fun getGlanceProviderById(context: Context, providerId: Constants.GlanceProviderId): GlanceProvider? {
return when(providerId) {
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_show_next_alarm_title),
R.drawable.round_alarm
)
}
Constants.GlanceProviderId.PLAYING_SONG -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_show_music_title),
R.drawable.round_music_note
)
}
Constants.GlanceProviderId.CUSTOM_INFO -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_custom_notes_title),
R.drawable.round_notes
)
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_low_battery_level_title),
R.drawable.round_battery_charging_full
)
}
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_daily_steps_title),
R.drawable.round_steps
)
}
}
}
fun saveGlanceProviderOrder(list: ArrayList<Constants.GlanceProviderId>) {
Preferences.enabledGlanceProviderOrder = list.joinToString(separator = ",")
}
fun showGlanceProviders(context: Context): Boolean {
return Preferences.showGlance && EventRepository(context).getEventsCount() == 0 && (
(Preferences.showNextAlarm && AlarmHelper.getNextAlarm(context) != "") ||
(MediaPlayerHelper.isSomeonePlaying(context)) ||
(Preferences.isBatteryLevelLow) ||
(Preferences.customNotes.isNotEmpty()) ||
(Preferences.showDailySteps && Preferences.googleFitSteps > 0)
)
}
}

View File

@ -79,7 +79,7 @@ object IntentHelper {
val calendarUri = CalendarContract.CONTENT_URI val calendarUri = CalendarContract.CONTENT_URI
.buildUpon() .buildUpon()
.appendPath("time") .appendPath("time")
.appendPath("0".toString()) .appendPath(Calendar.getInstance().timeInMillis.toString())
.build() .build()
return when (Preferences.calendarAppPackage) { return when (Preferences.calendarAppPackage) {
"" -> { "" -> {
@ -114,15 +114,40 @@ object IntentHelper {
if (Preferences.calendarAppPackage == "") { if (Preferences.calendarAppPackage == "") {
Intent(Intent.ACTION_VIEW).apply { Intent(Intent.ACTION_VIEW).apply {
data = uri data = uri
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate) if (!e.allDay) {
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate) putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate)
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate)
} else {
val start = Calendar.getInstance().apply {
timeInMillis = e.startDate
}
val end = Calendar.getInstance().apply {
timeInMillis = e.endDate
}
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate + start.timeZone.getOffset(start.timeInMillis))
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate + end.timeZone.getOffset(end.timeInMillis))
putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, 1)
}
} }
} else { } else {
getCalendarIntent(context).apply { getCalendarIntent(context).apply {
action = Intent.ACTION_VIEW action = Intent.ACTION_VIEW
data = uri data = uri
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate) if (!e.allDay) {
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate) putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate)
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate)
} else {
val start = Calendar.getInstance().apply {
timeInMillis = e.startDate
}
val end = Calendar.getInstance().apply {
timeInMillis = e.endDate
}
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, start.timeInMillis + start.timeZone.getOffset(start.timeInMillis))
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end.timeInMillis + end.timeZone.getOffset(end.timeInMillis))
putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, 1)
}
} }
} }
} }
@ -156,4 +181,33 @@ object IntentHelper {
} }
} }
} }
fun getMusicIntent(context: Context): Intent {
return when (Preferences.mediaPlayerPackage) {
"" -> {
Intent()
}
else -> {
val pm: PackageManager = context.packageManager
try {
pm.getLaunchIntentForPackage(Preferences.mediaPlayerPackage)!!.apply {
addCategory(Intent.CATEGORY_LAUNCHER)
}
} catch (e: Exception) {
Intent()
}
}
}
}
fun getFitIntent(context: Context): Intent {
val pm: PackageManager = context.packageManager
return try {
pm.getLaunchIntentForPackage("com.google.android.apps.fitness")!!.apply {
addCategory(Intent.CATEGORY_LAUNCHER)
}
} catch (e: Exception) {
Intent()
}
}
} }

View File

@ -0,0 +1,88 @@
package com.tommasoberlose.anotherwidget.helpers
import android.app.Notification
import android.content.ComponentName
import android.content.Context
import android.media.MediaMetadata
import android.media.session.MediaController
import android.media.session.MediaSession
import android.media.session.MediaSessionManager
import android.media.session.PlaybackState
import android.util.Log
import androidx.core.app.NotificationManagerCompat
import com.chibatching.kotpref.bulk
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.receivers.MusicNotificationListener
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import java.lang.Exception
object MediaPlayerHelper {
fun isSomeonePlaying(context: Context) = Preferences.showMusic && NotificationManagerCompat.getEnabledListenerPackages(context).contains(context.packageName) && Preferences.mediaPlayerTitle != ""
fun getMediaInfo(): String {
return if (Preferences.mediaPlayerArtist == "") {
Preferences.mediaPlayerTitle
} else {
"%s, %s".format(Preferences.mediaPlayerTitle, Preferences.mediaPlayerArtist)
}
}
fun updatePlayingMediaInfo(context: Context) {
if (NotificationManagerCompat.getEnabledListenerPackages(context).contains(context.packageName)) {
val list = try {
(context.getSystemService(Context.MEDIA_SESSION_SERVICE) as MediaSessionManager).getActiveSessions(
ComponentName(context.packageName, MusicNotificationListener::class.java.name)
)
} catch (ex: Exception) {
emptyList<MediaController>()
}
if (list.isNotEmpty()) {
var isSomeonePlaying = false
list.forEach { mc ->
val metadata = mc.metadata
val isPlaying =
mc.playbackState?.state == PlaybackState.STATE_PLAYING || mc.playbackState?.state == PlaybackState.STATE_CONNECTING
if (isPlaying) {
isSomeonePlaying = true
if (metadata != null) {
Preferences.bulk {
mediaPlayerTitle =
metadata.getText(MediaMetadata.METADATA_KEY_TITLE)?.toString()
?: ""
mediaPlayerArtist =
metadata.getText(MediaMetadata.METADATA_KEY_ARTIST)?.toString()
?: ""
mediaPlayerAlbum =
metadata.getText(MediaMetadata.METADATA_KEY_ALBUM)?.toString()
?: ""
}
}
Preferences.mediaPlayerPackage = mc.packageName
}
}
if (!isSomeonePlaying) {
removeMediaInfo()
}
} else {
removeMediaInfo()
}
} else {
removeMediaInfo()
}
MainWidget.updateWidget(context)
}
private fun removeMediaInfo() {
Preferences.bulk {
remove(Preferences::mediaPlayerTitle)
remove(Preferences::mediaPlayerArtist)
remove(Preferences::mediaPlayerAlbum)
remove(Preferences::mediaPlayerPackage)
}
}
}

View File

@ -3,6 +3,8 @@ package com.tommasoberlose.anotherwidget.helpers
import android.content.Context import android.content.Context
import android.text.format.DateUtils import android.text.format.DateUtils
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import org.joda.time.DateTime import org.joda.time.DateTime
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -39,7 +41,6 @@ object SettingsStringHelper {
return when (info) { return when (info) {
0 -> R.string.settings_second_row_info_subtitle_0 0 -> R.string.settings_second_row_info_subtitle_0
1 -> R.string.settings_second_row_info_subtitle_1 1 -> R.string.settings_second_row_info_subtitle_1
2 -> R.string.settings_second_row_info_subtitle_2
else -> R.string.settings_second_row_info_subtitle_0 else -> R.string.settings_second_row_info_subtitle_0
} }
} }
@ -69,9 +70,21 @@ object SettingsStringHelper {
difference += 60 * 1000 - (difference % (60 * 1000)) difference += 60 * 1000 - (difference % (60 * 1000))
when { when {
difference <= 0 || TimeUnit.MILLISECONDS.toHours(difference) < 1 -> { difference <= 0 -> {
return "" return ""
} }
TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.HIGH.value && TimeUnit.MILLISECONDS.toMinutes(difference) > 5 -> {
return DateUtils.getRelativeTimeSpanString(start, start - 1000 * 60 * (TimeUnit.MILLISECONDS.toMinutes(difference) - 1 - (TimeUnit.MILLISECONDS.toMinutes(difference) - 1) % 5), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString()
}
TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.DEFAULT.value && TimeUnit.MILLISECONDS.toMinutes(difference) > 5 -> {
return DateUtils.getRelativeTimeSpanString(start, start - 1000 * 60 * (TimeUnit.MILLISECONDS.toMinutes(difference) - 1 - (TimeUnit.MILLISECONDS.toMinutes(difference) - 1) % 15), DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString()
}
TimeUnit.MILLISECONDS.toHours(difference) < 1 && Preferences.widgetUpdateFrequency == Constants.WidgetUpdateFrequency.LOW.value -> {
return context.getString(R.string.soon)
}
TimeUnit.MILLISECONDS.toHours(difference) < 1 -> {
return context.getString(R.string.now)
}
TimeUnit.MILLISECONDS.toHours(difference) < 12 -> { TimeUnit.MILLISECONDS.toHours(difference) < 12 -> {
return DateUtils.getRelativeTimeSpanString(start, now, DateUtils.HOUR_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString() return DateUtils.getRelativeTimeSpanString(start, now, DateUtils.HOUR_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString()
} }

View File

@ -3,17 +3,13 @@ package com.tommasoberlose.anotherwidget.helpers
import android.Manifest import android.Manifest
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
import android.util.EventLog
import android.util.Log
import com.google.android.gms.location.LocationServices import com.google.android.gms.location.LocationServices
import com.kwabenaberko.openweathermaplib.constants.Units
import com.kwabenaberko.openweathermaplib.implementation.OpenWeatherMapHelper
import com.kwabenaberko.openweathermaplib.implementation.callbacks.CurrentWeatherCallback
import com.kwabenaberko.openweathermaplib.models.currentweather.CurrentWeather
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
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 org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
@ -38,7 +34,7 @@ object WeatherHelper {
Preferences.customLocationLon = location.longitude.toString() Preferences.customLocationLon = location.longitude.toString()
networkApi.updateWeather() networkApi.updateWeather()
EventBus.getDefault().post(MainActivity.UpdateUiMessageEvent()) EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
} }
} }
} }
@ -54,79 +50,79 @@ object WeatherHelper {
fun getWeatherIconResource(icon: String): Int { fun getWeatherIconResource(icon: String): Int {
when (icon) { when (icon) {
"01d" -> { "01d" -> {
return R.drawable.clear_day return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.clear_day else R.drawable.clear_day_2
} }
"02d" -> { "02d" -> {
return R.drawable.partly_cloudy return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.partly_cloudy else R.drawable.partly_cloudy_2
} }
"03d" -> { "03d" -> {
return R.drawable.mostly_cloudy return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.mostly_cloudy else R.drawable.mostly_cloudy_2
} }
"04d" -> { "04d" -> {
return R.drawable.cloudy_weather return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.cloudy_weather else R.drawable.cloudy_weather_2
} }
"09d" -> { "09d" -> {
return R.drawable.storm_weather_day return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.storm_weather_day else R.drawable.storm_weather_day_2
} }
"10d" -> { "10d" -> {
return R.drawable.rainy_day return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.rainy_day else R.drawable.rainy_day_2
} }
"11d" -> { "11d" -> {
return R.drawable.thunder_day return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.thunder_day else R.drawable.thunder_day_2
} }
"13d" -> { "13d" -> {
return R.drawable.snow_day return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.snow_day else R.drawable.snow_day_2
} }
"50d" -> { "50d" -> {
return R.drawable.haze_day return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.haze_day else R.drawable.haze_day_2
} }
"80d" -> { "80d" -> {
return R.drawable.windy_day return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.windy_day else R.drawable.windy_day_2
} }
"81d" -> { "81d" -> {
return R.drawable.rain_snow_day return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.rain_snow_day else R.drawable.rain_snow_day_2
} }
"82d" -> { "82d" -> {
return R.drawable.haze_weather return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.haze_weather else R.drawable.haze_weather_2
} }
"01n" -> { "01n" -> {
return R.drawable.clear_night return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.clear_night else R.drawable.clear_night_2
} }
"02n" -> { "02n" -> {
return R.drawable.partly_cloudy_night return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.partly_cloudy_night else R.drawable.partly_cloudy_night_2
} }
"03n" -> { "03n" -> {
return R.drawable.mostly_cloudy_night return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.mostly_cloudy_night else R.drawable.mostly_cloudy_night_2
} }
"04n" -> { "04n" -> {
return R.drawable.cloudy_weather return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.cloudy_weather else R.drawable.cloudy_weather_2
} }
"09n" -> { "09n" -> {
return R.drawable.storm_weather_night return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.storm_weather_night else R.drawable.storm_weather_night_2
} }
"10n" -> { "10n" -> {
return R.drawable.rainy_night return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.rainy_night else R.drawable.rainy_night_2
} }
"11n" -> { "11n" -> {
return R.drawable.thunder_night return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.thunder_night else R.drawable.thunder_night_2
} }
"13n" -> { "13n" -> {
return R.drawable.snow_night return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.snow_night else R.drawable.snow_night_2
} }
"50n" -> { "50n" -> {
return R.drawable.haze_night return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.haze_night else R.drawable.haze_night_2
} }
"80n" -> { "80n" -> {
return R.drawable.windy_night return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.windy_night else R.drawable.windy_night_2
} }
"81n" -> { "81n" -> {
return R.drawable.rain_snow_night return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.rain_snow_night else R.drawable.rain_snow_night_2
} }
"82n" -> { "82n" -> {
return R.drawable.haze_weather return if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) R.drawable.haze_weather else R.drawable.haze_weather_2
} }
else -> { else -> {
return R.drawable.unknown return R.drawable.unknown

View File

@ -3,6 +3,10 @@ package com.tommasoberlose.anotherwidget.helpers
import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetManager
import android.content.Context import android.content.Context
import android.content.res.Configuration.ORIENTATION_PORTRAIT import android.content.res.Configuration.ORIENTATION_PORTRAIT
import android.util.Log
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.global.Preferences
object WidgetHelper { object WidgetHelper {
class WidgetSizeProvider( class WidgetSizeProvider(
@ -16,22 +20,14 @@ object WidgetHelper {
val height = getWidgetHeight(isPortrait, 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("heightInPx", heightInPx)
return widthInPx to heightInPx return widthInPx to heightInPx
} }
private fun getWidgetWidth(isPortrait: Boolean, widgetId: Int): Int = private fun getWidgetWidth(isPortrait: Boolean, widgetId: Int): Int = getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
if (isPortrait) {
getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
} else {
getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
}
private fun getWidgetHeight(isPortrait: Boolean, widgetId: Int): Int = private fun getWidgetHeight(isPortrait: Boolean, widgetId: Int): Int = getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)
if (isPortrait) {
getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)
} else {
getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MIN_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

@ -0,0 +1,7 @@
package com.tommasoberlose.anotherwidget.models
class GlanceProvider(
val id: String,
val title: String,
val icon: Int
)

View File

@ -1,14 +1,13 @@
package com.tommasoberlose.anotherwidget.network package com.tommasoberlose.anotherwidget.network
import android.content.Context import android.content.Context
import android.util.Log
import com.kwabenaberko.openweathermaplib.constants.Units import com.kwabenaberko.openweathermaplib.constants.Units
import com.kwabenaberko.openweathermaplib.implementation.OpenWeatherMapHelper import com.kwabenaberko.openweathermaplib.implementation.OpenWeatherMapHelper
import com.kwabenaberko.openweathermaplib.implementation.callbacks.CurrentWeatherCallback import com.kwabenaberko.openweathermaplib.implementation.callbacks.CurrentWeatherCallback
import com.kwabenaberko.openweathermaplib.models.currentweather.CurrentWeather import com.kwabenaberko.openweathermaplib.models.currentweather.CurrentWeather
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.ui.activities.MainActivity import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
@ -26,7 +25,7 @@ class WeatherNetworkApi(val context: Context) {
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
MainWidget.updateWidget(context) MainWidget.updateWidget(context)
EventBus.getDefault().post(MainActivity.UpdateUiMessageEvent()) EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
} }
} }

View File

@ -0,0 +1,176 @@
package com.tommasoberlose.anotherwidget.receivers
import android.Manifest
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.util.Log
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.fitness.Fitness
import com.google.android.gms.fitness.FitnessOptions
import com.google.android.gms.fitness.data.DataType
import com.google.android.gms.fitness.data.Field.FIELD_STEPS
import com.google.android.gms.fitness.request.DataReadRequest
import com.google.android.gms.location.*
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import java.util.*
import java.util.concurrent.TimeUnit
class ActivityDetectionReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (ActivityTransitionResult.hasResult(intent)) {
val result = ActivityTransitionResult.extractResult(intent)!!
val lastEvent = result.transitionEvents.last()
if (lastEvent.activityType == DetectedActivity.WALKING || lastEvent.activityType == DetectedActivity.RUNNING && lastEvent.transitionType == ActivityTransition.ACTIVITY_TRANSITION_EXIT) {
requestDailySteps(context)
}
} else {
if (intent.action == Intent.ACTION_BOOT_COMPLETED || intent.action == Intent.ACTION_MY_PACKAGE_REPLACED && Preferences.showDailySteps && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || context.checkGrantedPermission(Manifest.permission.ACTIVITY_RECOGNITION)) {
resetDailySteps()
registerFence(context)
} else {
resetDailySteps()
}
}
}
private fun resetDailySteps() {
Preferences.googleFitSteps = -1
}
companion object {
val FITNESS_OPTIONS: FitnessOptions = FitnessOptions.builder()
.addDataType(DataType.TYPE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
.addDataType(DataType.AGGREGATE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ)
.build()
fun registerFence(context: Context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || context.checkGrantedPermission(
Manifest.permission.ACTIVITY_RECOGNITION)) {
val transitions = mutableListOf<ActivityTransition>()
transitions +=
ActivityTransition.Builder()
.setActivityType(DetectedActivity.WALKING)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
.build()
transitions +=
ActivityTransition.Builder()
.setActivityType(DetectedActivity.RUNNING)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
.build()
val request = ActivityTransitionRequest(transitions)
// myPendingIntent is the instance of PendingIntent where the app receives callbacks.
val task = ActivityRecognition.getClient(context)
.requestActivityTransitionUpdates(
request,
PendingIntent.getBroadcast(
context,
2,
Intent(context, ActivityDetectionReceiver::class.java),
0
)
)
task.addOnFailureListener { e: Exception ->
e.printStackTrace()
Preferences.showDailySteps = false
}
}
}
fun unregisterFence(context: Context) {
val task = ActivityRecognition.getClient(context)
.removeActivityTransitionUpdates(
PendingIntent.getBroadcast(
context,
2,
Intent(context, ActivityDetectionReceiver::class.java),
0
)
)
task.addOnCompleteListener {
if (it.isSuccessful) {
PendingIntent.getBroadcast(
context,
2,
Intent(context, ActivityDetectionReceiver::class.java),
0
).cancel()
}
}
}
fun requestDailySteps(context: Context) {
val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(context)
if (account != null && GoogleSignIn.hasPermissions(account, FITNESS_OPTIONS)) {
val cal: Calendar = Calendar.getInstance()
cal.set(Calendar.HOUR_OF_DAY, 0)
cal.set(Calendar.MINUTE, 0)
cal.set(Calendar.SECOND, 0)
cal.set(Calendar.MILLISECOND, 0)
val startTime: Long = cal.timeInMillis
cal.add(Calendar.DAY_OF_YEAR, 1)
val endTime: Long = cal.timeInMillis
val readRequest = DataReadRequest.Builder()
.aggregate(
DataType.TYPE_STEP_COUNT_DELTA,
DataType.AGGREGATE_STEP_COUNT_DELTA
)
.setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
.bucketByTime(1, TimeUnit.DAYS)
.build()
Fitness.getHistoryClient(context, account)
.readData(readRequest)
.addOnSuccessListener { response ->
Preferences.googleFitSteps = response.buckets.sumBy {
try {
it.getDataSet(DataType.AGGREGATE_STEP_COUNT_DELTA)?.dataPoints?.get(
0
)?.getValue(FIELD_STEPS)?.asInt() ?: 0
} catch (ex: Exception) {
0
}
}.toLong()
MainWidget.updateWidget(context)
setTimeout(context)
}
}
}
private fun setTimeout(context: Context) {
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
cancel(PendingIntent.getBroadcast(context, 5, Intent(context, ActivityDetectionReceiver::class.java), 0))
setExactAndAllowWhileIdle(
AlarmManager.RTC,
Calendar.getInstance().timeInMillis + 5 * 60 * 1000,
PendingIntent.getBroadcast(
context,
5,
Intent(context, ActivityDetectionReceiver::class.java),
0
)
)
}
}
}
}

View File

@ -0,0 +1,20 @@
package com.tommasoberlose.anotherwidget.receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.BatteryManager
import android.util.Log
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
class BatteryLevelReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when(intent.action) {
Intent.ACTION_BATTERY_LOW -> Preferences.isBatteryLevelLow = true
Intent.ACTION_BATTERY_OKAY -> Preferences.isBatteryLevelLow = false
}
MainWidget.updateWidget(context)
}
}

View File

@ -0,0 +1,37 @@
package com.tommasoberlose.anotherwidget.receivers
import android.app.Notification
import android.media.MediaMetadata
import android.media.session.MediaController
import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
import android.util.Log
import com.chibatching.kotpref.bulk
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
import com.tommasoberlose.anotherwidget.helpers.WidgetHelper
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
class MusicNotificationListener : NotificationListenerService() {
override fun onListenerConnected() {
MediaPlayerHelper.updatePlayingMediaInfo(this)
super.onListenerConnected()
}
override fun onNotificationPosted(sbn: StatusBarNotification?) {
sbn?.notification?.extras?.let { bundle ->
bundle.getParcelable<MediaSession.Token>(Notification.EXTRA_MEDIA_SESSION)?.let {
MediaPlayerHelper.updatePlayingMediaInfo(this)
}
}
super.onNotificationPosted(sbn)
}
override fun onNotificationRemoved(sbn: StatusBarNotification?) {
MediaPlayerHelper.updatePlayingMediaInfo(this)
super.onNotificationRemoved(sbn)
}
}

View File

@ -1,22 +0,0 @@
package com.tommasoberlose.anotherwidget.receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
class PlayerReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("ciao", "player ok")
// val cmd = intent.getStringExtra("command")
// Log.v("tag ", "$action / $cmd")
// val artist = intent.getStringExtra("artist")
// val album = intent.getStringExtra("album")
// val track = intent.getStringExtra("track")
// Log.v("tag", "$artist:$album:$track")
}
}

View File

@ -10,7 +10,10 @@ import androidx.core.app.AlarmManagerCompat
import androidx.core.content.ContextCompat.getSystemService import androidx.core.content.ContextCompat.getSystemService
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.Preferences
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
import com.tommasoberlose.anotherwidget.models.Event
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import org.joda.time.Period import org.joda.time.Period
import java.util.* import java.util.*
@ -32,47 +35,99 @@ class UpdatesReceiver : BroadcastReceiver() {
AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED, AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED,
Actions.ACTION_TIME_UPDATE -> { Actions.ACTION_TIME_UPDATE -> {
MainWidget.updateWidget(context) MainWidget.updateWidget(context)
if (intent.hasExtra(EVENT_ID)) {
setUpdates(context, intent.getLongExtra(EVENT_ID, -1))
}
} }
} }
} }
companion object { companion object {
const val EVENT_ID = "EVENT_ID"
fun setUpdates(context: Context) { fun setUpdates(context: Context, eventId: Long? = null) {
removeUpdates(context)
val eventRepository = EventRepository(context) val eventRepository = EventRepository(context)
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) { if (eventId == null) {
eventRepository.getEvents().forEach { event -> removeUpdates(context)
val now = Calendar.getInstance().apply {
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
val diff = Period(now.timeInMillis, event.startDate)
if (event.startDate > now.timeInMillis) {
// Update the widget every hour till the event
(0..diff.hours).forEach {
setExactAndAllowWhileIdle(
AlarmManager.RTC,
if (event.startDate - it * 1000 * 60 * 60 > 60 * 1000) event.startDate - it * 1000 * 60 * 60 else now.timeInMillis + 120000,
PendingIntent.getBroadcast(
context,
event.eventID.toInt() + it,
Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE
},
0
)
)
}
}
eventRepository.getEvents().forEach { event ->
setEventUpdate(context, event)
}
} else {
val event = eventRepository.getEventByEventId(eventId)
if (event != null) {
setEventUpdate(context, event)
}
}
}
private fun setEventUpdate(context: Context, event: Event) {
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
val now = Calendar.getInstance().apply {
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
val diff = Period(now.timeInMillis, event.startDate)
if (event.startDate > now.timeInMillis) {
// Update the widget every hour till the event
if (diff.hours == 0) {
var minutes = 0
when (Preferences.widgetUpdateFrequency) {
Constants.WidgetUpdateFrequency.DEFAULT.value -> {
minutes = when {
diff.minutes > 50 -> 50
diff.minutes > 30 -> 30
diff.minutes > 15 -> 15
else -> 0
}
}
Constants.WidgetUpdateFrequency.HIGH.value -> {
minutes = diff.minutes - (diff.minutes % 5)
}
}
setExact(
AlarmManager.RTC,
if (event.startDate - minutes * 1000 * 60 > (now.timeInMillis + 120 * 1000)) event.startDate - 60 * 1000 * minutes else now.timeInMillis + 120000,
PendingIntent.getBroadcast(
context,
event.eventID.toInt(),
Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE
putExtra(EVENT_ID, event.eventID)
},
0
)
)
} else {
setExact(
AlarmManager.RTC,
event.startDate - diff.hours * 1000 * 60 * 60,
PendingIntent.getBroadcast(
context,
event.eventID.toInt(),
Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE
putExtra(EVENT_ID, event.eventID)
},
0
)
)
}
} else {
// Update the widget one second after the event is finished // Update the widget one second after the event is finished
setExactAndAllowWhileIdle( val fireTime =
if (event.endDate > now.timeInMillis + 120 * 1000) event.endDate else now.timeInMillis + 120000
setExact(
AlarmManager.RTC, AlarmManager.RTC,
if (event.endDate > 60 *1000) event.endDate else now.timeInMillis + 120000, fireTime,
PendingIntent.getBroadcast(context, 1, Intent(context, UpdatesReceiver::class.java).apply { action = Actions.ACTION_TIME_UPDATE }, 0) PendingIntent.getBroadcast(
context,
event.eventID.toInt(),
Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE
},
0
)
) )
} }
} }
@ -80,11 +135,8 @@ class UpdatesReceiver : BroadcastReceiver() {
fun removeUpdates(context: Context) { fun removeUpdates(context: Context) {
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) { with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
cancel(PendingIntent.getBroadcast(context, 1, Intent(context, UpdatesReceiver::class.java), 0))
EventRepository(context).getEvents().forEach { EventRepository(context).getEvents().forEach {
(0..24).forEach { hour -> cancel(PendingIntent.getBroadcast(context, it.eventID.toInt(), Intent(context, UpdatesReceiver::class.java), 0))
cancel(PendingIntent.getBroadcast(context, it.eventID.toInt() * hour, Intent(context, UpdatesReceiver::class.java), 0))
}
} }
} }
} }

View File

@ -34,8 +34,6 @@ class WeatherReceiver : BroadcastReceiver() {
removeUpdates(context) removeUpdates(context)
if (Preferences.showWeather && Preferences.weatherProviderApi != "") { if (Preferences.showWeather && Preferences.weatherProviderApi != "") {
WeatherHelper.updateWeather(context)
val interval = MINUTE * when (Preferences.weatherRefreshPeriod) { val interval = MINUTE * when (Preferences.weatherRefreshPeriod) {
0 -> 30 0 -> 30
1 -> 60 1 -> 60
@ -48,7 +46,7 @@ class WeatherReceiver : BroadcastReceiver() {
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) { with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
setRepeating( setRepeating(
AlarmManager.RTC, AlarmManager.RTC,
Calendar.getInstance().timeInMillis + interval, Calendar.getInstance().timeInMillis,
interval, interval,
PendingIntent.getBroadcast(context, 0, Intent(context, WeatherReceiver::class.java).apply { action = Actions.ACTION_WEATHER_UPDATE }, 0) PendingIntent.getBroadcast(context, 0, Intent(context, WeatherReceiver::class.java).apply { action = Actions.ACTION_WEATHER_UPDATE }, 0)
) )

View File

@ -18,7 +18,9 @@ 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.helpers.DateHelper
import com.tommasoberlose.anotherwidget.ui.viewmodels.CustomDateViewModel import com.tommasoberlose.anotherwidget.ui.viewmodels.CustomDateViewModel
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 kotlinx.android.synthetic.main.activity_custom_date.* 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.action_back
import kotlinx.android.synthetic.main.activity_custom_location.list_view import kotlinx.android.synthetic.main.activity_custom_location.list_view
@ -78,7 +80,7 @@ class CustomDateActivity : AppCompatActivity() {
} }
delay(200) delay(200)
val text = if (dateFormat != "") { var text = if (dateFormat != "") {
try { try {
SimpleDateFormat(dateFormat, Locale.getDefault()).format(DATE.time) SimpleDateFormat(dateFormat, Locale.getDefault()).format(DATE.time)
} catch (e: Exception) { } catch (e: Exception) {
@ -88,6 +90,10 @@ class CustomDateActivity : AppCompatActivity() {
"__" "__"
} }
if (Preferences.isDateCapitalize) {
text = text.getCapWordString()
}
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
action_save.isVisible = text != ERROR_STRING action_save.isVisible = text != ERROR_STRING
loader.visibility = View.INVISIBLE loader.visibility = View.INVISIBLE
@ -96,6 +102,11 @@ class CustomDateActivity : AppCompatActivity() {
} }
}) })
viewModel.isDateCapitalize.observe(this, Observer {
viewModel.dateInput.value = viewModel.dateInput.value
binding.isdCapitalizeEnabled = it
})
} }
private fun setupListener() { private fun setupListener() {
@ -108,6 +119,15 @@ class CustomDateActivity : AppCompatActivity() {
finish() finish()
} }
action_capitalize.setOnClickListener {
Preferences.isDateCapitalize = !Preferences.isDateCapitalize
}
action_capitalize.setOnLongClickListener {
toast(getString(R.string.action_capitalize_the_date))
true
}
action_date_format_info.setOnClickListener { action_date_format_info.setOnClickListener {
openURI("https://developer.android.com/reference/java/text/SimpleDateFormat") openURI("https://developer.android.com/reference/java/text/SimpleDateFormat")
} }

View File

@ -0,0 +1,52 @@
package com.tommasoberlose.anotherwidget.ui.activities
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.ActivityIntegrationsBinding
import com.tommasoberlose.anotherwidget.ui.viewmodels.IntegrationsViewModel
import kotlinx.android.synthetic.main.activity_integrations.*
import net.idik.lib.slimadapter.SlimAdapter
class IntegrationsActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter
private lateinit var viewModel: IntegrationsViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(IntegrationsViewModel::class.java)
val binding = DataBindingUtil.setContentView<ActivityIntegrationsBinding>(this, R.layout.activity_integrations)
list_view.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager
adapter = SlimAdapter.create()
adapter
.register<String>(R.layout.application_info_layout) { _, injector ->
injector
.text(R.id.text, getString(R.string.default_name))
}
.attachTo(list_view)
setupListener()
subscribeUi(binding, viewModel)
}
private fun subscribeUi(binding: ActivityIntegrationsBinding, viewModel: IntegrationsViewModel) {
binding.viewModel = viewModel
}
private fun setupListener() {
action_back.setOnClickListener {
onBackPressed()
}
}
}

View File

@ -24,8 +24,16 @@ import androidx.core.view.isVisible
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 com.chibatching.kotpref.Kotpref
import com.google.android.material.badge.BadgeDrawable import com.google.android.material.badge.BadgeDrawable
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import com.karumi.dexter.Dexter
import com.karumi.dexter.MultiplePermissionsReport
import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.MaterialBottomSheetDialog import com.tommasoberlose.anotherwidget.components.MaterialBottomSheetDialog
import com.tommasoberlose.anotherwidget.global.Actions import com.tommasoberlose.anotherwidget.global.Actions
@ -50,10 +58,16 @@ import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode 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 val mainNavController: NavController? by lazy {
Navigation.findNavController(
this,
R.id.content_fragment
)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -62,238 +76,19 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh
viewModel = ViewModelProvider(this).get(MainViewModel::class.java) viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
controlExtras(intent) controlExtras(intent)
requirePermission()
// Viewpager
pager.adapter = ViewPagerAdapter(this)
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.advanced_settings_title)
else -> ""
}
}.attach()
// Init clock
time.setTextColor(ColorHelper.getFontColor())
time.setTextSize(TypedValue.COMPLEX_UNIT_SP, Preferences.clockTextSize.toPixel(this@MainActivity))
time_am_pm.setTextColor(ColorHelper.getFontColor())
time_am_pm.setTextSize(TypedValue.COMPLEX_UNIT_SP, Preferences.clockTextSize.toPixel(this@MainActivity) / 5 * 2)
time_container.isVisible = Preferences.showClock
preview.layoutParams = preview.layoutParams.apply {
height = 160.toPixel(this@MainActivity) + if (Preferences.showClock) 100.toPixel(this@MainActivity) else 0
}
Preferences.preferences.registerOnSharedPreferenceChangeListener(this)
subscribeUi(viewModel)
updateUI()
WeatherHelper.updateWeather(this)
// Warnings
if (getString(R.string.xiaomi_manufacturer).equals(Build.MANUFACTURER, ignoreCase = true) && Preferences.showXiaomiWarning) {
MaterialBottomSheetDialog(this, getString(R.string.xiaomi_warning_title), getString(R.string.xiaomi_warning_message))
.setNegativeButton(getString(R.string.action_ignore)) {
Preferences.showXiaomiWarning = false
}
.setPositiveButton(getString(R.string.action_grant_permission)) {
Preferences.showXiaomiWarning = false
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.parse("package:$packageName")
}
startActivity(intent)
}
.show()
}
}
private var uiJob: Job? = null
private fun updateUI() {
uiJob?.cancel()
if (Preferences.showPreview) {
preview.setCardBackgroundColor(
getColor(
if (ColorHelper.getFontColor()
.isColorDark()
) android.R.color.white else R.color.colorAccent
)
)
widget_shape_background.setImageDrawable(BitmapHelper.getTintedDrawable(this, R.drawable.card_background, ColorHelper.getBackgroundColor()))
uiJob = lifecycleScope.launch(Dispatchers.IO) {
delay(200)
val generatedView = MainWidget.generateWidgetView(this@MainActivity)
withContext(Dispatchers.Main) {
generatedView.measure(0, 0)
preview.measure(0, 0)
}
val bitmap = BitmapHelper.getBitmapFromView(
generatedView,
if (preview.width > 0) preview.width else generatedView.measuredWidth,
generatedView.measuredHeight
)
withContext(Dispatchers.Main) {
// Clock
time.setTextColor(ColorHelper.getFontColor())
time_am_pm.setTextColor(ColorHelper.getFontColor())
time.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(this@MainActivity)
)
time_am_pm.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(this@MainActivity) / 5 * 2
)
// 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_container.isVisible) || (!Preferences.showClock && time_container.isVisible)) {
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
ValueAnimator.ofFloat(
if (Preferences.showClock) 0f else 1f,
if (Preferences.showClock) 1f else 0f
).apply {
duration = 500L
addUpdateListener {
val animatedValue = animatedValue as Float
time_container.layoutParams = time_container.layoutParams.apply {
height = (initialHeight * animatedValue).toInt()
}
}
addListener(
onStart = {
if (Preferences.showClock) {
time_container.isVisible = true
}
},
onEnd = {
if (!Preferences.showClock) {
time_container.isVisible = false
}
}
)
}.start()
ValueAnimator.ofInt(
preview.height,
160.toPixel(this@MainActivity) + if (Preferences.showClock) 100.toPixel(
this@MainActivity
) else 0
).apply {
duration = 500L
addUpdateListener {
val animatedValue = animatedValue as Int
val layoutParams = preview.layoutParams
layoutParams.height = animatedValue
preview.layoutParams = layoutParams
}
}.start()
} else {
time_container.layoutParams = time_container.layoutParams.apply {
height = RelativeLayout.LayoutParams.WRAP_CONTENT
}
time_container.measure(0, 0)
}
if (preview.height == 0) {
ValueAnimator.ofInt(
preview.height,
160.toPixel(this@MainActivity) + if (Preferences.showClock) 100.toPixel(
this@MainActivity
) else 0
).apply {
duration = 300L
addUpdateListener {
val animatedValue = animatedValue as Int
val layoutParams = preview.layoutParams
layoutParams.height = animatedValue
preview.layoutParams = layoutParams
}
}.start()
}
bitmap_container.setImageBitmap(bitmap)
widget_loader.animate().scaleX(0f).scaleY(0f).start()
widget.animate().alpha(1f).start()
}
}
} else {
ValueAnimator.ofInt(
preview.height,
0
).apply {
duration = 300L
addUpdateListener {
val animatedValue = animatedValue as Int
val layoutParams = preview.layoutParams
layoutParams.height = animatedValue
preview.layoutParams = layoutParams
}
}.start()
}
// Calendar error indicator
tabs.getTabAt(1)?.orCreateBadge?.apply {
backgroundColor = ContextCompat.getColor(this@MainActivity, R.color.errorColorText)
badgeGravity = BadgeDrawable.TOP_END
}?.isVisible = Preferences.showEvents && !checkGrantedPermission(Manifest.permission.READ_CALENDAR)
// Weather error indicator
tabs.getTabAt(2)?.orCreateBadge?.apply {
backgroundColor = ContextCompat.getColor(this@MainActivity, R.color.errorColorText)
badgeGravity = BadgeDrawable.TOP_END
}?.isVisible = Preferences.showWeather && (Preferences.weatherProviderApi == "" || (Preferences.customLocationAdd == "" && !checkGrantedPermission(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else Manifest.permission.ACCESS_FINE_LOCATION)))
}
private fun subscribeUi(viewModel: MainViewModel) {
viewModel.showWallpaper.observe(this, Observer {
val wallpaper = getCurrentWallpaper()
widget_bg.setImageDrawable(if (it) wallpaper else null)
widget_bg.layoutParams = widget_bg.layoutParams.apply {
val metrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(metrics)
height = metrics.heightPixels
width = (wallpaper?.intrinsicWidth ?: 1) * metrics.heightPixels / (wallpaper?.intrinsicWidth ?: 1)
}
})
logo.setOnClickListener {
// startActivity(Intent(this, SupportDevActivity::class.java))
}
} }
override fun onBackPressed() { override fun onBackPressed() {
if (mAppWidgetId > 0) { if (mainNavController?.currentDestination?.id == R.id.appMainFragment) {
addNewWidget() if (mAppWidgetId > 0) {
addNewWidget()
} else {
setResult(Activity.RESULT_OK)
finish()
}
} else { } else {
setResult(Activity.RESULT_OK) super.onBackPressed()
finish()
} }
} }
@ -333,30 +128,27 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh
finish() finish()
} }
override fun onDestroy() { private fun requirePermission() {
super.onDestroy() Dexter.withContext(this)
Preferences.preferences.unregisterOnSharedPreferenceChangeListener(this) .withPermissions(
} Manifest.permission.READ_EXTERNAL_STORAGE
).withListener(object : MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
report?.let {
Preferences.showWallpaper = false
Preferences.showWallpaper = report.areAllPermissionsGranted()
}
}
override fun onSharedPreferenceChanged(preferences: SharedPreferences, p1: String) { override fun onPermissionRationaleShouldBeShown(
updateUI() permissions: MutableList<PermissionRequest>?,
MainWidget.updateWidget(this) token: PermissionToken?
} ) {
// Remember to invoke this method when the custom rationale is closed
override fun onStart() { // or just by default if you don't want to use any custom rationale.
super.onStart() token?.cancelPermissionRequest()
EventBus.getDefault().register(this) }
} })
.check()
override fun onStop() {
super.onStop()
EventBus.getDefault().unregister(this)
}
class UpdateUiMessageEvent
@Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(ignore: UpdateUiMessageEvent?) {
updateUI()
} }
} }

View File

@ -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 -> CalendarSettingsFragment.newInstance() 1 -> CalendarTabFragment.newInstance()
2 -> WeatherSettingsFragment.newInstance() 2 -> WeatherTabFragment.newInstance()
3 -> ClockSettingsFragment.newInstance() 3 -> ClockTabFragment.newInstance()
4 -> AdvancedSettingsFragment.newInstance() 4 -> GlanceTabFragment.newInstance()
else -> GeneralSettingsFragment.newInstance() else -> GeneralTabFragment.newInstance()
} }
} }
} }

View File

@ -4,10 +4,10 @@ import android.Manifest
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
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 androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@ -43,10 +43,10 @@ import kotlinx.coroutines.launch
import java.util.* import java.util.*
import kotlin.Comparator import kotlin.Comparator
class CalendarSettingsFragment : Fragment() { class CalendarTabFragment : Fragment() {
companion object { companion object {
fun newInstance() = CalendarSettingsFragment() fun newInstance() = CalendarTabFragment()
} }
private lateinit var viewModel: MainViewModel private lateinit var viewModel: MainViewModel
@ -81,6 +81,8 @@ class CalendarSettingsFragment : Fragment() {
binding: FragmentCalendarSettingsBinding, binding: FragmentCalendarSettingsBinding,
viewModel: MainViewModel viewModel: MainViewModel
) { ) {
binding.isCalendarEnabled = Preferences.showEvents
viewModel.showEvents.observe(viewLifecycleOwner, Observer { viewModel.showEvents.observe(viewLifecycleOwner, Observer {
maintainScrollPosition { maintainScrollPosition {
binding.isCalendarEnabled = it binding.isCalendarEnabled = it
@ -121,6 +123,17 @@ class CalendarSettingsFragment : Fragment() {
} }
}) })
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 { viewModel.showUntil.observe(viewLifecycleOwner, Observer {
maintainScrollPosition { maintainScrollPosition {
show_until_label?.text = getString(SettingsStringHelper.getShowUntilString(it)) show_until_label?.text = getString(SettingsStringHelper.getShowUntilString(it))
@ -129,12 +142,9 @@ class CalendarSettingsFragment : Fragment() {
}) })
viewModel.showNextEvent.observe(viewLifecycleOwner, Observer { viewModel.showNextEvent.observe(viewLifecycleOwner, Observer {
show_multiple_events_label?.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
})
viewModel.dateFormat.observe(viewLifecycleOwner, Observer {
maintainScrollPosition { maintainScrollPosition {
date_format_label?.text = DateHelper.getDateText(requireContext(), Calendar.getInstance()) show_multiple_events_label?.text =
if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
} }
}) })
@ -156,6 +166,16 @@ class CalendarSettingsFragment : Fragment() {
action_show_events.setOnClickListener { action_show_events.setOnClickListener {
Preferences.showEvents = !Preferences.showEvents 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 { action_filter_calendar.setOnClickListener {
@ -253,6 +273,18 @@ class CalendarSettingsFragment : Fragment() {
} }
} }
action_widget_update_frequency.setOnClickListener {
if (Preferences.showEvents) {
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 { action_second_row_info.setOnClickListener {
if (Preferences.showEvents) { if (Preferences.showEvents) {
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_second_row_info_title)).setSelectedValue(Preferences.secondRowInformation) val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_second_row_info_title)).setSelectedValue(Preferences.secondRowInformation)
@ -268,7 +300,7 @@ class CalendarSettingsFragment : Fragment() {
action_show_until.setOnClickListener { action_show_until.setOnClickListener {
if (Preferences.showEvents) { if (Preferences.showEvents) {
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_show_until_title)).setSelectedValue(Preferences.showUntil) 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 { intArrayOf(6,7,0,1,2,3).forEach {
dialog.addItem(getString(SettingsStringHelper.getShowUntilString(it)), it) dialog.addItem(getString(SettingsStringHelper.getShowUntilString(it)), it)
} }
dialog.addOnSelectItemListener { value -> dialog.addOnSelectItemListener { value ->
@ -277,27 +309,6 @@ class CalendarSettingsFragment : Fragment() {
} }
} }
action_date_format.setOnClickListener {
if (Preferences.showEvents) {
val now = Calendar.getInstance()
val dialog = BottomSheetMenu<String>(requireContext(), header = getString(R.string.settings_date_format_title)).setSelectedValue(Preferences.dateFormat)
dialog.addItem(DateHelper.getDefaultDateText(requireContext(), now), "")
if (Preferences.dateFormat != "") {
dialog.addItem(DateHelper.getDateText(requireContext(), now), Preferences.dateFormat)
}
dialog.addItem(getString(R.string.custom_date_format), "-")
dialog.addOnSelectItemListener { value ->
if (value == "-") {
startActivity(Intent(requireContext(), CustomDateActivity::class.java))
} else {
Preferences.dateFormat = value
}
}.show()
}
}
action_open_event_details.setOnClickListener { action_open_event_details.setOnClickListener {
if (Preferences.showEvents) { if (Preferences.showEvents) {
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_event_app_title)).setSelectedValue(Preferences.openEventDetails) BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_event_app_title)).setSelectedValue(Preferences.openEventDetails)
@ -337,6 +348,8 @@ class CalendarSettingsFragment : Fragment() {
report?.let { report?.let {
if (report.areAllPermissionsGranted()){ if (report.areAllPermissionsGranted()){
checkReadEventsPermission() checkReadEventsPermission()
} else {
Preferences.showEvents = false
} }
} }
} }

View File

@ -6,7 +6,6 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -20,29 +19,35 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.chibatching.kotpref.bulk import com.chibatching.kotpref.bulk
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.BottomSheetColorPicker
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
import com.tommasoberlose.anotherwidget.databinding.FragmentClockSettingsBinding import com.tommasoberlose.anotherwidget.databinding.FragmentClockSettingsBinding
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.global.RequestCode import com.tommasoberlose.anotherwidget.global.RequestCode
import com.tommasoberlose.anotherwidget.helpers.AlarmHelper 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.ui.activities.ChooseApplicationActivity import com.tommasoberlose.anotherwidget.ui.activities.ChooseApplicationActivity
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.toast
import kotlinx.android.synthetic.main.fragment_clock_settings.* import kotlinx.android.synthetic.main.fragment_clock_settings.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.lang.Exception import java.lang.Exception
class ClockSettingsFragment : Fragment() { class ClockTabFragment : Fragment() {
companion object { companion object {
fun newInstance() = ClockSettingsFragment() fun newInstance() = ClockTabFragment()
} }
private lateinit var viewModel: MainViewModel private lateinit var viewModel: MainViewModel
private lateinit var colors: IntArray
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -67,14 +72,21 @@ class ClockSettingsFragment : Fragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
lifecycleScope.launch(Dispatchers.IO) {
val lazyColors = requireContext().resources.getIntArray(R.array.material_colors)
withContext(Dispatchers.Main) {
colors = lazyColors
}
}
setupListener() setupListener()
updateNextAlarmWarningUi()
} }
private fun subscribeUi( private fun subscribeUi(
binding: FragmentClockSettingsBinding, binding: FragmentClockSettingsBinding,
viewModel: MainViewModel viewModel: MainViewModel
) { ) {
binding.isClockVisible = Preferences.showClock
viewModel.showBigClockWarning.observe(viewLifecycleOwner, Observer { viewModel.showBigClockWarning.observe(viewLifecycleOwner, Observer {
large_clock_warning?.isVisible = it large_clock_warning?.isVisible = it
small_clock_warning?.isVisible = !it small_clock_warning?.isVisible = !it
@ -94,6 +106,34 @@ class ClockSettingsFragment : Fragment() {
} }
}) })
viewModel.showAMPMIndicator.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
ampm_indicator_label?.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
}
})
viewModel.clockTextColor.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
if (Preferences.clockTextAlpha == "00") {
clock_text_color_label?.text = getString(R.string.transparent)
} else {
clock_text_color_label?.text =
"#%s".format(Integer.toHexString(ColorHelper.getClockFontColor())).toUpperCase()
}
}
})
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())).toUpperCase()
}
}
})
viewModel.clockBottomMargin.observe(viewLifecycleOwner, Observer { viewModel.clockBottomMargin.observe(viewLifecycleOwner, Observer {
maintainScrollPosition { maintainScrollPosition {
clock_bottom_margin_label?.text = when (it) { clock_bottom_margin_label?.text = when (it) {
@ -105,10 +145,6 @@ class ClockSettingsFragment : Fragment() {
} }
}) })
viewModel.showNextAlarm.observe(viewLifecycleOwner, Observer {
updateNextAlarmWarningUi()
})
viewModel.clockAppName.observe(viewLifecycleOwner, Observer { viewModel.clockAppName.observe(viewLifecycleOwner, Observer {
maintainScrollPosition { maintainScrollPosition {
clock_app_label?.text = clock_app_label?.text =
@ -126,6 +162,10 @@ class ClockSettingsFragment : Fragment() {
Preferences.showClock = !Preferences.showClock Preferences.showClock = !Preferences.showClock
} }
show_clock_switch.setOnCheckedChangeListener { _, enabled: Boolean ->
Preferences.showClock = enabled
}
action_clock_text_size.setOnClickListener { action_clock_text_size.setOnClickListener {
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.settings_clock_text_size_title)).setSelectedValue(Preferences.clockTextSize) 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 { (46 downTo 12).filter { it % 2 == 0 }.forEach {
@ -136,8 +176,34 @@ class ClockSettingsFragment : Fragment() {
}.show() }.show()
} }
action_ampm_indicator_size.setOnClickListener {
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_ampm_indicator_title)).setSelectedValue(Preferences.showAMPMIndicator)
.addItem(getString(R.string.settings_visible), true)
.addItem(getString(R.string.settings_not_visible), false)
.addOnSelectItemListener { value ->
Preferences.showAMPMIndicator = value
}.show()
}
action_clock_text_color.setOnClickListener {
BottomSheetColorPicker(requireContext(),
colors = colors,
header = getString(R.string.settings_font_color_title),
getSelected = ColorHelper::getClockFontColorRgb,
onColorSelected = { color: Int ->
val colorString = Integer.toHexString(color)
Preferences.clockTextColor = "#" + if (colorString.length > 6) colorString.substring(2) else colorString
},
showAlphaSelector = true,
alpha = Preferences.clockTextAlpha.toIntValue(),
onAlphaChangeListener = { alpha ->
Preferences.clockTextAlpha = alpha.toHexValue()
}
).show()
}
action_clock_bottom_margin_size.setOnClickListener { action_clock_bottom_margin_size.setOnClickListener {
BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_show_next_alarm_title)).setSelectedValue(Preferences.clockBottomMargin) 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_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_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_medium), Constants.ClockBottomMargin.MEDIUM.value)
@ -154,50 +220,6 @@ class ClockSettingsFragment : Fragment() {
) )
} }
} }
action_show_next_alarm.setOnClickListener {
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_show_next_alarm_title)).setSelectedValue(Preferences.showNextAlarm)
.addItem(getString(R.string.settings_visible), true)
.addItem(getString(R.string.settings_not_visible), false)
.addOnSelectItemListener { value ->
Preferences.showNextAlarm = value
}.show()
}
}
private fun updateNextAlarmWarningUi() {
with(requireContext().getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
val alarm = nextAlarmClock
if (AlarmHelper.isAlarmProbablyWrong(requireContext()) && alarm != null && alarm.showIntent != null) {
val pm = requireContext().packageManager as PackageManager
val appNameOrPackage = try {
pm.getApplicationLabel(pm.getApplicationInfo(alarm.showIntent?.creatorPackage ?: "", 0))
} catch (e: Exception) {
alarm.showIntent?.creatorPackage ?: ""
}
show_next_alarm_warning.text = getString(R.string.next_alarm_warning).format(appNameOrPackage)
} else {
maintainScrollPosition {
show_next_alarm_label?.text = if (Preferences.showNextAlarm) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
}
}
}
}
private val nextAlarmChangeBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
updateNextAlarmWarningUi()
}
}
override fun onStart() {
super.onStart()
activity?.registerReceiver(nextAlarmChangeBroadcastReceiver, IntentFilter(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED))
}
override fun onStop() {
activity?.unregisterReceiver(nextAlarmChangeBroadcastReceiver)
super.onStop()
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

View File

@ -3,9 +3,7 @@ package com.tommasoberlose.anotherwidget.ui.fragments
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.graphics.Color
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
@ -23,7 +21,9 @@ import com.tommasoberlose.anotherwidget.global.RequestCode
import com.tommasoberlose.anotherwidget.helpers.ColorHelper import com.tommasoberlose.anotherwidget.helpers.ColorHelper
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toHexValue import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toHexValue
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toIntValue import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toIntValue
import com.tommasoberlose.anotherwidget.helpers.DateHelper
import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper
import com.tommasoberlose.anotherwidget.ui.activities.CustomDateActivity
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 kotlinx.android.synthetic.main.fragment_general_settings.* import kotlinx.android.synthetic.main.fragment_general_settings.*
@ -31,12 +31,13 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.*
class GeneralSettingsFragment : Fragment() { class GeneralTabFragment : Fragment() {
companion object { companion object {
fun newInstance() = GeneralSettingsFragment() fun newInstance() = GeneralTabFragment()
} }
private lateinit var viewModel: MainViewModel private lateinit var viewModel: MainViewModel
@ -115,6 +116,28 @@ class GeneralSettingsFragment : Fragment() {
} }
}) })
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())).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())).toUpperCase()
}
}
})
viewModel.backgroundCardColor.observe(viewLifecycleOwner, Observer { viewModel.backgroundCardColor.observe(viewLifecycleOwner, Observer {
maintainScrollPosition { maintainScrollPosition {
if (Preferences.backgroundCardAlpha == "00") { if (Preferences.backgroundCardAlpha == "00") {
@ -143,11 +166,24 @@ class GeneralSettingsFragment : Fragment() {
} }
}) })
viewModel.dateFormat.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
date_format_label?.text = DateHelper.getDateText(requireContext(), Calendar.getInstance())
}
})
viewModel.customFont.observe(viewLifecycleOwner, Observer { viewModel.customFont.observe(viewLifecycleOwner, Observer {
maintainScrollPosition { maintainScrollPosition {
custom_font_label?.text = getString(SettingsStringHelper.getCustomFontLabel(it)) custom_font_label?.text = getString(SettingsStringHelper.getCustomFontLabel(it))
} }
}) })
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 maintainScrollPosition(callback: () -> Unit) { private fun maintainScrollPosition(callback: () -> Unit) {
@ -162,7 +198,7 @@ class GeneralSettingsFragment : Fragment() {
private fun setupListener() { private fun setupListener() {
action_main_text_size.setOnClickListener { action_main_text_size.setOnClickListener {
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.title_main_text_size)).setSelectedValue(Preferences.textMainSize) val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.title_main_text_size)).setSelectedValue(Preferences.textMainSize)
(32 downTo 10).filter { it % 2 == 0 }.forEach { (40 downTo 10).filter { it % 2 == 0 }.forEach {
dialog.addItem("${it}sp", it.toFloat()) dialog.addItem("${it}sp", it.toFloat())
} }
dialog.addOnSelectItemListener { value -> dialog.addOnSelectItemListener { value ->
@ -172,7 +208,7 @@ class GeneralSettingsFragment : Fragment() {
action_second_text_size.setOnClickListener { action_second_text_size.setOnClickListener {
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.title_second_text_size)).setSelectedValue(Preferences.textSecondSize) val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.title_second_text_size)).setSelectedValue(Preferences.textSecondSize)
(28 downTo 10).filter { it % 2 == 0 }.forEach { (40 downTo 10).filter { it % 2 == 0 }.forEach {
dialog.addItem("${it}sp", it.toFloat()) dialog.addItem("${it}sp", it.toFloat())
} }
dialog.addOnSelectItemListener { value -> dialog.addOnSelectItemListener { value ->
@ -197,6 +233,44 @@ class GeneralSettingsFragment : Fragment() {
).show() ).show()
} }
action_secondary_font_color.setOnClickListener {
BottomSheetColorPicker(requireContext(),
colors = colors,
header = getString(R.string.settings_secondary_font_color_title),
getSelected = ColorHelper::getSecondaryFontColorRgb,
onColorSelected = { color: Int ->
val colorString = Integer.toHexString(color)
Preferences.textSecondaryColor = "#" + if (colorString.length > 6) colorString.substring(2) else colorString
},
showAlphaSelector = true,
alpha = Preferences.textSecondaryAlpha.toIntValue(),
onAlphaChangeListener = { alpha ->
Preferences.textSecondaryAlpha = alpha.toHexValue()
}
).show()
}
action_date_format.setOnClickListener {
if (Preferences.showEvents) {
val now = Calendar.getInstance()
val dialog = BottomSheetMenu<String>(requireContext(), header = getString(R.string.settings_date_format_title)).setSelectedValue(Preferences.dateFormat)
dialog.addItem(DateHelper.getDefaultDateText(requireContext(), now), "")
if (Preferences.dateFormat != "") {
dialog.addItem(DateHelper.getDateText(requireContext(), now), Preferences.dateFormat)
}
dialog.addItem(getString(R.string.custom_date_format), "-")
dialog.addOnSelectItemListener { value ->
if (value == "-") {
startActivity(Intent(requireContext(), CustomDateActivity::class.java))
} else {
Preferences.dateFormat = value
}
}.show()
}
}
action_background_color.setOnClickListener { action_background_color.setOnClickListener {
BottomSheetColorPicker(requireContext(), BottomSheetColorPicker(requireContext(),
colors = colors, colors = colors,
@ -245,6 +319,15 @@ class GeneralSettingsFragment : Fragment() {
} }
*/ */
} }
action_show_dividers.setOnClickListener {
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_show_multiple_events_title)).setSelectedValue(Preferences.showDividers)
.addItem(getString(R.string.settings_visible), true)
.addItem(getString(R.string.settings_not_visible), false)
.addOnSelectItemListener { value ->
Preferences.showDividers = value
}.show()
}
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

View File

@ -0,0 +1,371 @@
package com.tommasoberlose.anotherwidget.ui.fragments
import android.Manifest
import android.app.Activity
import android.app.AlarmManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.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.app.NotificationManagerCompat
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.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.common.api.ApiException
import com.karumi.dexter.Dexter
import com.karumi.dexter.MultiplePermissionsReport
import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
import com.tommasoberlose.anotherwidget.components.CustomNotesDialog
import com.tommasoberlose.anotherwidget.components.GlanceProviderSortMenu
import com.tommasoberlose.anotherwidget.databinding.FragmentGlanceSettingsBinding
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.AlarmHelper
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver
import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver.Companion.FITNESS_OPTIONS
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.checkIfFitInstalled
import kotlinx.android.synthetic.main.fragment_calendar_settings.*
import kotlinx.android.synthetic.main.fragment_glance_settings.*
import kotlinx.android.synthetic.main.fragment_glance_settings.scrollView
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class GlanceTabFragment : Fragment() {
companion object {
fun newInstance() = GlanceTabFragment()
}
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<FragmentGlanceSettingsBinding>(inflater, R.layout.fragment_glance_settings, container, false)
subscribeUi(binding, viewModel)
binding.lifecycleOwner = this
binding.viewModel = viewModel
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
action_show_steps.isVisible = requireContext().checkIfFitInstalled()
setupListener()
updateNextAlarmWarningUi()
}
private fun subscribeUi(
binding: FragmentGlanceSettingsBinding,
viewModel: MainViewModel
) {
binding.isGlanceVisible = Preferences.showGlance
viewModel.showGlance.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
binding.isGlanceVisible = it
}
})
viewModel.showMusic.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
checkNotificationPermission()
}
})
viewModel.showNextAlarm.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
updateNextAlarmWarningUi()
}
})
viewModel.showBatteryCharging.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
show_low_battery_level_warning_label?.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
}
})
viewModel.showDailySteps.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
show_steps_label?.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
}
checkFitnessPermission()
})
viewModel.customInfo.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
show_custom_notes_label?.text = if (it == "") getString(R.string.settings_not_visible) else it
}
})
}
private fun setupListener() {
action_show_glance.setOnClickListener {
Preferences.showGlance = !Preferences.showGlance
}
show_glance_switch.setOnCheckedChangeListener { _, enabled: Boolean ->
Preferences.showGlance = enabled
}
action_sort_glance_providers.setOnClickListener {
GlanceProviderSortMenu(requireContext())
.show()
}
action_show_music.setOnClickListener {
if (Preferences.showGlance) {
BottomSheetMenu<Boolean>(
requireContext(),
header = getString(R.string.settings_show_music_title)
).setSelectedValue(Preferences.showMusic)
.addItem(getString(R.string.settings_visible), true)
.addItem(getString(R.string.settings_not_visible), false)
.addOnSelectItemListener { value ->
Preferences.showMusic = value
}.show()
}
}
action_show_next_alarm.setOnClickListener {
if (Preferences.showGlance) {
BottomSheetMenu<Boolean>(
requireContext(),
header = getString(R.string.settings_show_next_alarm_title)
).setSelectedValue(Preferences.showNextAlarm)
.addItem(getString(R.string.settings_visible), true)
.addItem(getString(R.string.settings_not_visible), false)
.addOnSelectItemListener { value ->
Preferences.showNextAlarm = value
}.show()
}
}
action_show_low_battery_level_warning.setOnClickListener {
if (Preferences.showGlance) {
BottomSheetMenu<Boolean>(
requireContext(),
header = getString(R.string.settings_low_battery_level_title)
).setSelectedValue(Preferences.showBatteryCharging)
.addItem(getString(R.string.settings_visible), true)
.addItem(getString(R.string.settings_not_visible), false)
.addOnSelectItemListener { value ->
Preferences.showBatteryCharging = value
}.show()
}
}
action_show_steps.setOnClickListener {
if (Preferences.showGlance) {
BottomSheetMenu<Boolean>(
requireContext(),
header = getString(R.string.settings_daily_steps_title)
).setSelectedValue(Preferences.showDailySteps)
.addItem(getString(R.string.settings_visible), true)
.addItem(getString(R.string.settings_not_visible), false)
.addOnSelectItemListener { value ->
if (value) {
val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(requireContext())
if (!GoogleSignIn.hasPermissions(account, FITNESS_OPTIONS)) {
val mGoogleSignInClient = GoogleSignIn.getClient(requireActivity(), GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).addExtension(FITNESS_OPTIONS).build())
startActivityForResult(mGoogleSignInClient.signInIntent, 2)
} else {
Preferences.showDailySteps = true
}
} else {
Preferences.showDailySteps = false
}
}.show()
}
}
action_show_custom_notes.setOnClickListener {
if (Preferences.showGlance) {
CustomNotesDialog(requireContext()).show()
}
}
}
private fun updateNextAlarmWarningUi() {
with(requireContext().getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
val alarm = nextAlarmClock
if (AlarmHelper.isAlarmProbablyWrong(requireContext()) && alarm != null && alarm.showIntent != null) {
val pm = requireContext().packageManager as PackageManager
val appNameOrPackage = try {
pm.getApplicationLabel(pm.getApplicationInfo(alarm.showIntent?.creatorPackage ?: "", 0))
} catch (e: Exception) {
alarm.showIntent?.creatorPackage ?: ""
}
show_next_alarm_warning.text =
getString(R.string.next_alarm_warning).format(appNameOrPackage)
} else {
show_next_alarm_label?.text = if (Preferences.showNextAlarm) getString(R.string.settings_visible) else getString(
R.string.settings_not_visible)
}
}
}
private val nextAlarmChangeBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
updateNextAlarmWarningUi()
}
}
override fun onStart() {
super.onStart()
activity?.registerReceiver(nextAlarmChangeBroadcastReceiver, IntentFilter(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED))
}
override fun onStop() {
activity?.unregisterReceiver(nextAlarmChangeBroadcastReceiver)
super.onStop()
}
private fun checkNotificationPermission() {
if (NotificationManagerCompat.getEnabledListenerPackages(requireContext()).contains(requireContext().packageName)) {
notification_permission_alert?.isVisible = false
MediaPlayerHelper.updatePlayingMediaInfo(requireContext())
show_music_label?.text = if (Preferences.showMusic) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
} else if (Preferences.showMusic) {
notification_permission_alert?.isVisible = true
show_music_label?.text = getString(R.string.settings_request_notification_access)
notification_permission_alert?.setOnClickListener {
activity?.startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"))
}
} else {
show_music_label?.text = getString(R.string.settings_not_visible)
notification_permission_alert?.isVisible = false
}
}
private fun checkFitnessPermission() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || activity?.checkGrantedPermission(Manifest.permission.ACTIVITY_RECOGNITION) == true) {
fitness_permission_alert?.isVisible = false
if (Preferences.showDailySteps) {
ActivityDetectionReceiver.registerFence(requireContext())
} else {
ActivityDetectionReceiver.unregisterFence(requireContext())
}
show_steps_label?.text = if (Preferences.showDailySteps) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
} else if (Preferences.showDailySteps) {
ActivityDetectionReceiver.unregisterFence(requireContext())
fitness_permission_alert?.isVisible = true
show_steps_label?.text = getString(R.string.settings_request_fitness_access)
fitness_permission_alert?.setOnClickListener {
requireFitnessPermission()
}
} else {
ActivityDetectionReceiver.unregisterFence(requireContext())
show_steps_label?.text = getString(R.string.settings_not_visible)
fitness_permission_alert?.isVisible = false
}
}
override fun onActivityResult(
requestCode: Int,
resultCode: Int,
data: Intent?
) {
when (requestCode) {
1 -> {
if (resultCode == Activity.RESULT_OK) {
checkFitnessPermission()
} else {
Preferences.showDailySteps = false
}
}
2-> {
try {
val account: GoogleSignInAccount? = GoogleSignIn.getSignedInAccountFromIntent(data).getResult(ApiException::class.java)
if (!GoogleSignIn.hasPermissions(account, FITNESS_OPTIONS)) {
GoogleSignIn.requestPermissions(
requireActivity(),
1,
account,
FITNESS_OPTIONS)
} else {
checkFitnessPermission()
}
} catch (e: ApiException) {
e.printStackTrace()
Preferences.showDailySteps = false
}
}
}
}
private fun requireFitnessPermission() {
Dexter.withContext(requireContext())
.withPermissions(
"com.google.android.gms.permission.ACTIVITY_RECOGNITION",
"android.gms.permission.ACTIVITY_RECOGNITION",
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACTIVITY_RECOGNITION else "com.google.android.gms.permission.ACTIVITY_RECOGNITION"
).withListener(object: MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
report?.let {
if (report.areAllPermissionsGranted()){
checkFitnessPermission()
}
}
}
override fun onPermissionRationaleShouldBeShown(
permissions: MutableList<PermissionRequest>?,
token: PermissionToken?
) {
// Remember to invoke this method when the custom rationale is closed
// or just by default if you don't want to use any custom rationale.
token?.continuePermissionRequest()
}
})
.check()
}
private fun maintainScrollPosition(callback: () -> Unit) {
val scrollPosition = scrollView.scrollY
callback.invoke()
lifecycleScope.launch {
delay(200)
scrollView.smoothScrollTo(0, scrollPosition)
}
}
override fun onResume() {
super.onResume()
checkNotificationPermission()
}
}

View File

@ -0,0 +1,393 @@
package com.tommasoberlose.anotherwidget.ui.fragments
import android.Manifest
import android.animation.ValueAnimator
import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.util.DisplayMetrics
import android.util.Log
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.RelativeLayout
import androidx.core.animation.addListener
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
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.badge.BadgeDrawable
import com.google.android.material.tabs.TabLayoutMediator
import com.google.android.material.transition.MaterialSharedAxis
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.MaterialBottomSheetDialog
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.BitmapHelper
import com.tommasoberlose.anotherwidget.helpers.ColorHelper
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.adapters.ViewPagerAdapter
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.getCurrentWallpaper
import com.tommasoberlose.anotherwidget.utils.toPixel
import kotlinx.android.synthetic.main.fragment_app_main.*
import kotlinx.android.synthetic.main.the_widget_sans.*
import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeListener {
companion object {
fun newInstance() = MainFragment()
private const val PREVIEW_BASE_HEIGHT = 120
}
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis.create(MaterialSharedAxis.X, true)
reenterTransition = MaterialSharedAxis.create(MaterialSharedAxis.X, false)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
return inflater.inflate(R.layout.fragment_app_main, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
subscribeUi(viewModel)
// 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
time.setTextColor(ColorHelper.getClockFontColor())
time.setTextSize(TypedValue.COMPLEX_UNIT_SP, Preferences.clockTextSize.toPixel(requireContext()))
time_am_pm.setTextColor(ColorHelper.getClockFontColor())
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)
updateUI()
// Warnings
if (getString(R.string.xiaomi_manufacturer).equals(Build.MANUFACTURER, ignoreCase = true) && Preferences.showXiaomiWarning) {
MaterialBottomSheetDialog(requireContext(), getString(R.string.xiaomi_warning_title), getString(R.string.xiaomi_warning_message))
.setNegativeButton(getString(R.string.action_ignore)) {
Preferences.showXiaomiWarning = false
}
.setPositiveButton(getString(R.string.action_grant_permission)) {
Preferences.showXiaomiWarning = false
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.parse("package:${activity?.packageName}")
}
startActivity(intent)
}
.show()
}
}
private var uiJob: Job? = null
private fun updateUI() {
uiJob?.cancel()
preview?.clearAnimation()
time_container?.clearAnimation()
if (Preferences.showPreview) {
preview?.setCardBackgroundColor(
ContextCompat.getColor(
requireContext(),
if (ColorHelper.getFontColor()
.isColorDark()
) android.R.color.white else R.color.colorAccent
)
)
widget_shape_background?.setImageDrawable(
BitmapHelper.getTintedDrawable(
requireContext(),
R.drawable.card_background,
ColorHelper.getBackgroundColor()
)
)
uiJob = viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
val generatedView = MainWidget.generateWidgetView(requireContext())
withContext(Dispatchers.Main) {
generatedView.measure(0, 0)
preview?.measure(0, 0)
}
val bitmap = if (preview != null) {
BitmapHelper.getBitmapFromView(
generatedView,
if (preview.width > 0) preview.width else generatedView.measuredWidth,
generatedView.measuredHeight
)
} else {
null
}
withContext(Dispatchers.Main) {
// Clock
time?.setTextColor(ColorHelper.getClockFontColor())
time_am_pm?.setTextColor(ColorHelper.getClockFontColor())
time?.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(requireContext())
)
time_am_pm?.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(requireContext()) / 5 * 2
)
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_container?.isVisible == false) || (!Preferences.showClock && time_container?.isVisible == true)) {
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 {
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 {
val animatedValue = animatedValue as Int
val layoutParams = preview.layoutParams
layoutParams.height = animatedValue
preview?.layoutParams = layoutParams
}
}.start()
}
widget_loader?.animate()?.scaleX(0f)?.scaleY(0f)?.alpha(0f)?.setDuration(200L)?.start()
bitmap_container?.apply {
setImageBitmap(bitmap)
scaleX = 0.9f
scaleY = 0.9f
}
widget?.animate()?.alpha(1f)?.start()
}
}
} else {
if (preview != null) {
ValueAnimator.ofInt(
preview.height,
0
).apply {
duration = 300L
addUpdateListener {
val animatedValue = animatedValue as Int
val layoutParams = preview.layoutParams
layoutParams.height = animatedValue
preview.layoutParams = layoutParams
}
}.start()
}
}
showErrorBadge()
}
private fun subscribeUi(viewModel: MainViewModel) {
viewModel.showWallpaper.observe(viewLifecycleOwner, Observer {
activity?.let { act ->
val wallpaper = act.getCurrentWallpaper()
widget_bg.setImageDrawable(if (it) wallpaper else null)
if (wallpaper != null) {
widget_bg.layoutParams =
(widget_bg.layoutParams as ViewGroup.MarginLayoutParams).apply {
val metrics = DisplayMetrics()
act.windowManager.defaultDisplay.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(
if (dimensions.first >= dimensions.second) (-80).toPixel(requireContext()) else 0,
(-80).toPixel(requireContext()), 0, 0
)
width = dimensions.first
height = dimensions.second
}
}
}
})
logo.setOnClickListener {
// startActivity(Intent(this, SupportDevActivity::class.java))
}
action_settings.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_appMainFragment_to_appSettingsFragment)
}
}
private fun showErrorBadge() {
// Calendar error indicator
tabs?.getTabAt(1)?.orCreateBadge?.apply {
backgroundColor = ContextCompat.getColor(requireContext(), R.color.errorColorText)
badgeGravity = BadgeDrawable.TOP_END
}?.isVisible = Preferences.showEvents && activity?.checkGrantedPermission(Manifest.permission.READ_CALENDAR) != true
// Weather error indicator
tabs?.getTabAt(2)?.orCreateBadge?.apply {
backgroundColor = ContextCompat.getColor(requireContext(), R.color.errorColorText)
badgeGravity = BadgeDrawable.TOP_END
}?.isVisible = Preferences.showWeather && (Preferences.weatherProviderApi == "" || (Preferences.customLocationAdd == "" && activity?.checkGrantedPermission(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else Manifest.permission.ACCESS_FINE_LOCATION) != true))
// Music error indicator
tabs?.getTabAt(4)?.orCreateBadge?.apply {
backgroundColor = ContextCompat.getColor(requireContext(), R.color.errorColorText)
badgeGravity = BadgeDrawable.TOP_END
}?.isVisible = Preferences.showMusic && !NotificationManagerCompat.getEnabledListenerPackages(requireContext()).contains(requireContext().packageName)
}
override fun onResume() {
super.onResume()
Preferences.preferences.registerOnSharedPreferenceChangeListener(this)
EventBus.getDefault().register(this)
showErrorBadge()
updateUI()
}
override fun onPause() {
Preferences.preferences.unregisterOnSharedPreferenceChangeListener(this)
EventBus.getDefault().unregister(this)
super.onPause()
}
var delayJob: Job? = null
override fun onSharedPreferenceChanged(preferences: SharedPreferences, p1: String) {
delayJob?.cancel()
delayJob = lifecycleScope.launch(Dispatchers.IO) {
delay(200)
withContext(Dispatchers.Main) {
updateUI()
}
}
MainWidget.updateWidget(requireContext())
}
class UpdateUiMessageEvent
@Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(ignore: UpdateUiMessageEvent?) {
delayJob?.cancel()
delayJob = lifecycleScope.launch(Dispatchers.IO) {
delay(200)
withContext(Dispatchers.Main) {
updateUI()
}
}
}
}

View File

@ -2,7 +2,6 @@ package com.tommasoberlose.anotherwidget.ui.fragments
import android.Manifest import android.Manifest
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
@ -15,6 +14,8 @@ 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.Navigation
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
@ -23,30 +24,34 @@ 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.FragmentAdvancedSettingsBinding import com.tommasoberlose.anotherwidget.databinding.FragmentSettingsBinding
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.activities.SupportDevActivity import com.tommasoberlose.anotherwidget.ui.activities.SupportDevActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
import com.tommasoberlose.anotherwidget.ui.activities.IntegrationsActivity
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_advanced_settings.* import kotlinx.android.synthetic.main.fragment_settings.*
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class AdvancedSettingsFragment : Fragment() { class SettingsFragment : Fragment() {
companion object { companion object {
fun newInstance() = AdvancedSettingsFragment() fun newInstance() = SettingsFragment()
} }
private lateinit var viewModel: MainViewModel private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis.create(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis.create(MaterialSharedAxis.X, false)
} }
override fun onCreateView( override fun onCreateView(
@ -55,9 +60,7 @@ class AdvancedSettingsFragment : 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<FragmentAdvancedSettingsBinding>(inflater, R.layout.fragment_advanced_settings, container, false) val binding = DataBindingUtil.inflate<FragmentSettingsBinding>(inflater, R.layout.fragment_settings, container, false)
subscribeUi(viewModel)
binding.lifecycleOwner = this binding.lifecycleOwner = this
binding.viewModel = viewModel binding.viewModel = viewModel
@ -68,10 +71,15 @@ class AdvancedSettingsFragment : Fragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
action_back.setOnClickListener {
Navigation.findNavController(it).popBackStack()
}
subscribeUi(viewModel)
setupListener() setupListener()
app_version.text = "v%s (%s)".format(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE) app_version.text = "v%s (%s)".format(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)
requirePermission()
} }
private fun subscribeUi( private fun subscribeUi(
@ -90,6 +98,10 @@ class AdvancedSettingsFragment : Fragment() {
} }
}) })
viewModel.installedIntegrations.observe(viewLifecycleOwner, Observer {
integrations_count_label?.text = getString(R.string.label_count_installed_integrations).format(it)
})
viewModel.showPreview.observe(viewLifecycleOwner, Observer { viewModel.showPreview.observe(viewLifecycleOwner, Observer {
maintainScrollPosition { maintainScrollPosition {
show_widget_preview_label?.text = show_widget_preview_label?.text =
@ -108,27 +120,6 @@ class AdvancedSettingsFragment : Fragment() {
} }
private fun setupListener() { private fun setupListener() {
action_change_theme.setOnClickListener {
maintainScrollPosition {
BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_theme_title))
.setSelectedValue(Preferences.darkThemePreference)
.addItem(
getString(R.string.settings_subtitle_dark_theme_light),
AppCompatDelegate.MODE_NIGHT_NO
)
.addItem(
getString(R.string.settings_subtitle_dark_theme_dark),
AppCompatDelegate.MODE_NIGHT_YES
)
.addItem(
getString(R.string.settings_subtitle_dark_theme_default),
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM else AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY
)
.addOnSelectItemListener { value ->
Preferences.darkThemePreference = value
}.show()
}
}
action_show_widget_preview.setOnClickListener { action_show_widget_preview.setOnClickListener {
maintainScrollPosition { maintainScrollPosition {
@ -170,6 +161,32 @@ class AdvancedSettingsFragment : Fragment() {
} }
} }
action_integrations.setOnClickListener {
startActivity(Intent(requireContext(), IntegrationsActivity::class.java))
}
action_change_theme.setOnClickListener {
maintainScrollPosition {
BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_theme_title))
.setSelectedValue(Preferences.darkThemePreference)
.addItem(
getString(R.string.settings_subtitle_dark_theme_light),
AppCompatDelegate.MODE_NIGHT_NO
)
.addItem(
getString(R.string.settings_subtitle_dark_theme_dark),
AppCompatDelegate.MODE_NIGHT_YES
)
.addItem(
getString(R.string.settings_subtitle_dark_theme_default),
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM else AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY
)
.addOnSelectItemListener { value ->
Preferences.darkThemePreference = value
}.show()
}
}
action_translate.setOnClickListener { action_translate.setOnClickListener {
activity?.openURI("https://github.com/tommasoberlose/another-widget/blob/master/app/src/main/res/values/strings.xml") activity?.openURI("https://github.com/tommasoberlose/another-widget/blob/master/app/src/main/res/values/strings.xml")
} }
@ -187,8 +204,9 @@ class AdvancedSettingsFragment : Fragment() {
} }
action_refresh_widget.setOnClickListener { action_refresh_widget.setOnClickListener {
MainWidget.updateWidget(requireContext()) WeatherHelper.updateWeather(requireContext())
CalendarHelper.updateEventList(requireContext()) CalendarHelper.updateEventList(requireContext())
MediaPlayerHelper.updatePlayingMediaInfo(requireContext())
} }
} }

View File

@ -5,6 +5,7 @@ import android.app.Activity
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
@ -41,10 +42,10 @@ import kotlinx.android.synthetic.main.fragment_weather_settings.scrollView
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class WeatherSettingsFragment : Fragment() { class WeatherTabFragment : Fragment() {
companion object { companion object {
fun newInstance() = WeatherSettingsFragment() fun newInstance() = WeatherTabFragment()
} }
private lateinit var viewModel: MainViewModel private lateinit var viewModel: MainViewModel
@ -79,14 +80,18 @@ class WeatherSettingsFragment : Fragment() {
binding: FragmentWeatherSettingsBinding, binding: FragmentWeatherSettingsBinding,
viewModel: MainViewModel viewModel: MainViewModel
) { ) {
binding.isWeatherVisible = Preferences.showWeather
viewModel.showWeatherWarning.observe(viewLifecycleOwner, Observer { viewModel.showWeatherWarning.observe(viewLifecycleOwner, Observer {
weather_warning?.isVisible = it weather_warning?.isVisible = it
checkLocationPermission()
}) })
viewModel.showWeather.observe(viewLifecycleOwner, Observer { viewModel.showWeather.observe(viewLifecycleOwner, Observer {
maintainScrollPosition { maintainScrollPosition {
show_weather_label?.text = show_weather_label?.text =
if (it) getString(R.string.show_weather_visible) else getString(R.string.show_weather_not_visible) if (it) getString(R.string.show_weather_visible) else getString(R.string.show_weather_not_visible)
checkWeatherProviderConfig()
binding.isWeatherVisible = it binding.isWeatherVisible = it
} }
checkLocationPermission() checkLocationPermission()
@ -94,11 +99,7 @@ class WeatherSettingsFragment : Fragment() {
viewModel.weatherProviderApi.observe(viewLifecycleOwner, Observer { viewModel.weatherProviderApi.observe(viewLifecycleOwner, Observer {
maintainScrollPosition { maintainScrollPosition {
label_weather_provider_api_key?.text = checkWeatherProviderConfig()
if (it == "") getString(R.string.settings_weather_provider_api_key_subtitle_not_set) else getString(
R.string.settings_weather_provider_api_key_subtitle_all_set
)
label_weather_provider_api_key?.setTextColor(ContextCompat.getColor(requireContext(), if (it == "") R.color.errorColorText else R.color.colorSecondaryText))
} }
checkLocationPermission() checkLocationPermission()
}) })
@ -126,6 +127,16 @@ class WeatherSettingsFragment : Fragment() {
checkLocationPermission() checkLocationPermission()
}) })
viewModel.weatherIconPack.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
label_weather_icon_pack?.text = when (it) {
Constants.WeatherIconPack.MINIMAL.value -> getString(R.string.settings_weather_icon_pack_minimal)
else -> getString(R.string.settings_weather_icon_pack_default)
}
}
checkLocationPermission()
})
viewModel.weatherAppName.observe(viewLifecycleOwner, Observer { viewModel.weatherAppName.observe(viewLifecycleOwner, Observer {
maintainScrollPosition { maintainScrollPosition {
weather_app_label?.text = weather_app_label?.text =
@ -148,9 +159,19 @@ class WeatherSettingsFragment : Fragment() {
location_permission_alert?.setOnClickListener { location_permission_alert?.setOnClickListener {
requirePermission() requirePermission()
} }
} else {
location_permission_alert?.isVisible = false
} }
} }
private fun checkWeatherProviderConfig() {
label_weather_provider_api_key?.text =
if (Preferences.weatherProviderApi == "") getString(R.string.settings_weather_provider_api_key_subtitle_not_set) else getString(
R.string.settings_weather_provider_api_key_subtitle_all_set
)
label_weather_provider_api_key?.setTextColor(ContextCompat.getColor(requireContext(), if (Preferences.weatherProviderApi == "" && Preferences.showWeather) R.color.errorColorText else R.color.colorSecondaryText))
}
private fun setupListener() { private fun setupListener() {
action_hide_weather_warning.setOnClickListener { action_hide_weather_warning.setOnClickListener {
Preferences.showWeatherWarning = false Preferences.showWeatherWarning = false
@ -160,6 +181,10 @@ class WeatherSettingsFragment : Fragment() {
Preferences.showWeather = !Preferences.showWeather Preferences.showWeather = !Preferences.showWeather
} }
show_weather_switch.setOnCheckedChangeListener { _, enabled: Boolean ->
Preferences.showWeather = enabled
}
action_weather_provider_api_key.setOnClickListener { action_weather_provider_api_key.setOnClickListener {
if (Preferences.showWeather) { if (Preferences.showWeather) {
startActivityForResult( startActivityForResult(
@ -203,6 +228,17 @@ class WeatherSettingsFragment : Fragment() {
} }
} }
action_weather_icon_pack.setOnClickListener {
if (Preferences.showWeather) {
BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_weather_icon_pack_title)).setSelectedValue(Preferences.weatherIconPack)
.addItem(getString(R.string.settings_weather_icon_pack_default), Constants.WeatherIconPack.DEFAULT.value)
.addItem(getString(R.string.settings_weather_icon_pack_minimal), Constants.WeatherIconPack.MINIMAL.value)
.addOnSelectItemListener { value ->
Preferences.weatherIconPack = value
}.show()
}
}
action_weather_app.setOnClickListener { action_weather_app.setOnClickListener {
if (Preferences.showWeather) { if (Preferences.showWeather) {
startActivityForResult( startActivityForResult(

View File

@ -3,8 +3,10 @@ package com.tommasoberlose.anotherwidget.ui.viewmodels
import android.app.Application import android.app.Application
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.chibatching.kotpref.livedata.asLiveData
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
class CustomDateViewModel(application: Application) : AndroidViewModel(application) { class CustomDateViewModel(application: Application) : AndroidViewModel(application) {
val dateInput: MutableLiveData<String> = MutableLiveData(if (Preferences.dateFormat == "") "EEEE, MMM dd" else Preferences.dateFormat) val dateInput: MutableLiveData<String> = MutableLiveData(if (Preferences.dateFormat == "") "EEEE, MMM dd" else Preferences.dateFormat)
val isDateCapitalize = Preferences.asLiveData(Preferences::isDateCapitalize)
} }

View File

@ -0,0 +1,7 @@
package com.tommasoberlose.anotherwidget.ui.viewmodels
import android.app.Application
import androidx.lifecycle.AndroidViewModel
class IntegrationsViewModel(application: Application) : AndroidViewModel(application) {
}

View File

@ -9,6 +9,8 @@ class MainViewModel : ViewModel() {
// General Settings // General Settings
val textGlobalColor = Preferences.asLiveData(Preferences::textGlobalColor) val textGlobalColor = Preferences.asLiveData(Preferences::textGlobalColor)
val textGlobalAlpha = Preferences.asLiveData(Preferences::textGlobalAlpha) val textGlobalAlpha = Preferences.asLiveData(Preferences::textGlobalAlpha)
val textSecondaryColor = Preferences.asLiveData(Preferences::textSecondaryColor)
val textSecondaryAlpha = Preferences.asLiveData(Preferences::textSecondaryAlpha)
val backgroundCardColor = Preferences.asLiveData(Preferences::backgroundCardColor) val backgroundCardColor = Preferences.asLiveData(Preferences::backgroundCardColor)
val backgroundCardAlpha = Preferences.asLiveData(Preferences::backgroundCardAlpha) val backgroundCardAlpha = Preferences.asLiveData(Preferences::backgroundCardAlpha)
val textMainSize = Preferences.asLiveData(Preferences::textMainSize) val textMainSize = Preferences.asLiveData(Preferences::textMainSize)
@ -16,6 +18,7 @@ class MainViewModel : ViewModel() {
val textShadow = Preferences.asLiveData(Preferences::textShadow) val textShadow = Preferences.asLiveData(Preferences::textShadow)
val customFont = Preferences.asLiveData(Preferences::customFont) val customFont = Preferences.asLiveData(Preferences::customFont)
val secondRowInformation = Preferences.asLiveData(Preferences::secondRowInformation) val secondRowInformation = Preferences.asLiveData(Preferences::secondRowInformation)
val showDividers = Preferences.asLiveData(Preferences::showDividers)
// Calendar Settings // Calendar Settings
val showEvents = Preferences.asLiveData(Preferences::showEvents) val showEvents = Preferences.asLiveData(Preferences::showEvents)
@ -26,14 +29,16 @@ class MainViewModel : ViewModel() {
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)
// 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 clockTextAlpha = Preferences.asLiveData(Preferences::clockTextAlpha)
val showAMPMIndicator = Preferences.asLiveData(Preferences::showAMPMIndicator)
val clockAppName = Preferences.asLiveData(Preferences::clockAppName) val clockAppName = Preferences.asLiveData(Preferences::clockAppName)
val showNextAlarm = Preferences.asLiveData(Preferences::showNextAlarm)
val dateFormat = Preferences.asLiveData(Preferences::dateFormat) val dateFormat = Preferences.asLiveData(Preferences::dateFormat)
val clockBottomMargin = Preferences.asLiveData(Preferences::clockBottomMargin) val clockBottomMargin = Preferences.asLiveData(Preferences::clockBottomMargin)
@ -50,9 +55,19 @@ class MainViewModel : ViewModel() {
val customLocationAdd = Preferences.asLiveData(Preferences::customLocationAdd) val customLocationAdd = Preferences.asLiveData(Preferences::customLocationAdd)
val showWeatherWarning = Preferences.asLiveData(Preferences::showWeatherWarning) val showWeatherWarning = Preferences.asLiveData(Preferences::showWeatherWarning)
val weatherIconPack = Preferences.asLiveData(Preferences::weatherIconPack)
// 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)
// 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)
} }

View File

@ -12,6 +12,7 @@ import android.graphics.Color
import android.graphics.Typeface import android.graphics.Typeface
import android.os.Bundle import android.os.Bundle
import android.text.format.DateUtils import android.text.format.DateUtils
import android.util.Log
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
@ -19,7 +20,6 @@ import android.widget.RemoteViews
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.db.EventRepository import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.global.Actions import com.tommasoberlose.anotherwidget.global.Actions
@ -35,6 +35,7 @@ import java.lang.Exception
import java.text.DateFormat import java.text.DateFormat
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.math.min
class MainWidget : AppWidgetProvider() { class MainWidget : AppWidgetProvider() {
@ -53,6 +54,7 @@ class MainWidget : AppWidgetProvider() {
override fun onEnabled(context: Context) { override fun onEnabled(context: Context) {
CalendarHelper.updateEventList(context) CalendarHelper.updateEventList(context)
WeatherReceiver.setUpdates(context) WeatherReceiver.setUpdates(context)
MediaPlayerHelper.updatePlayingMediaInfo(context)
if (Preferences.showEvents) { if (Preferences.showEvents) {
CalendarHelper.setEventUpdatesAndroidN(context) CalendarHelper.setEventUpdatesAndroidN(context)
@ -84,9 +86,10 @@ class MainWidget : AppWidgetProvider() {
appWidgetId: Int) { appWidgetId: Int) {
val displayMetrics = Resources.getSystem().displayMetrics val displayMetrics = Resources.getSystem().displayMetrics
val width = displayMetrics.widthPixels val width = displayMetrics.widthPixels
val height = displayMetrics.heightPixels
val dimensions = WidgetHelper.WidgetSizeProvider(context, appWidgetManager).getWidgetsSize(appWidgetId) val dimensions = WidgetHelper.WidgetSizeProvider(context, appWidgetManager).getWidgetsSize(appWidgetId)
generateWidgetView(context, appWidgetId, appWidgetManager, dimensions.first - 8.toPixel(context) /*width - 16.toPixel(context)*/) generateWidgetView(context, appWidgetId, appWidgetManager, min(dimensions.first - 8.toPixel(context), min(width, height) - 16.toPixel(context)))
} }
private fun generateWidgetView(context: Context, appWidgetId: Int, appWidgetManager: AppWidgetManager, w: Int) { private fun generateWidgetView(context: Context, appWidgetId: Int, appWidgetManager: AppWidgetManager, w: Int) {
@ -148,6 +151,7 @@ class MainWidget : AppWidgetProvider() {
views.setViewVisibility(R.id.empty_layout_rect, View.VISIBLE) views.setViewVisibility(R.id.empty_layout_rect, View.VISIBLE)
views.setViewVisibility(R.id.calendar_layout_rect, View.GONE) views.setViewVisibility(R.id.calendar_layout_rect, View.GONE)
views.setViewVisibility(R.id.second_row_rect, View.GONE) views.setViewVisibility(R.id.second_row_rect, View.GONE)
views.setViewVisibility(R.id.next_event_difference_time_rect, View.GONE)
val calPIntent = PendingIntent.getActivity( val calPIntent = PendingIntent.getActivity(
context, context,
@ -259,14 +263,64 @@ class MainWidget : AppWidgetProvider() {
views.setViewVisibility(R.id.empty_layout_rect, View.GONE) views.setViewVisibility(R.id.empty_layout_rect, View.GONE)
views.setViewVisibility(R.id.calendar_layout_rect, View.VISIBLE) views.setViewVisibility(R.id.calendar_layout_rect, View.VISIBLE)
} else if (Preferences.showNextAlarm && nextAlarm != "") { } else if (GlanceProviderHelper.showGlanceProviders(context)) {
val clockIntent = PendingIntent.getActivity( loop@ for (provider:Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(context)) {
context, when (provider) {
widgetID, Constants.GlanceProviderId.PLAYING_SONG -> {
IntentHelper.getClockIntent(context), if (MediaPlayerHelper.isSomeonePlaying(context)) {
0 val musicIntent = PendingIntent.getActivity(
) context,
views.setOnClickPendingIntent(R.id.second_row_rect, clockIntent) widgetID,
IntentHelper.getMusicIntent(context),
0
)
views.setOnClickPendingIntent(R.id.second_row_rect, musicIntent)
break@loop
}
}
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
if (Preferences.showNextAlarm && nextAlarm != "") {
val alarmIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getClockIntent(context),
0
)
views.setOnClickPendingIntent(R.id.second_row_rect, alarmIntent)
break@loop
}
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
if (Preferences.isBatteryLevelLow) {
val alarmIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getClockIntent(context),
0
)
views.setOnClickPendingIntent(R.id.second_row_rect, alarmIntent)
break@loop
}
}
Constants.GlanceProviderId.CUSTOM_INFO -> {
if (Preferences.customNotes.isNotEmpty()) {
break@loop
}
}
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
if (Preferences.showDailySteps && Preferences.googleFitSteps > 0) {
val fitIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getFitIntent(context),
0
)
views.setOnClickPendingIntent(R.id.second_row_rect, fitIntent)
break@loop
}
}
}
}
views.setImageViewBitmap( views.setImageViewBitmap(
R.id.next_event_rect, R.id.next_event_rect,
@ -277,10 +331,11 @@ class MainWidget : AppWidgetProvider() {
R.id.second_row_rect, R.id.second_row_rect,
BitmapHelper.getBitmapFromView(v.second_row, draw = false) BitmapHelper.getBitmapFromView(v.second_row, draw = false)
) )
views.setViewVisibility(R.id.second_row_rect, View.VISIBLE)
views.setViewVisibility(R.id.second_row_rect, View.VISIBLE)
views.setViewVisibility(R.id.empty_layout_rect, View.GONE) views.setViewVisibility(R.id.empty_layout_rect, View.GONE)
views.setViewVisibility(R.id.calendar_layout_rect, View.VISIBLE) views.setViewVisibility(R.id.calendar_layout_rect, View.VISIBLE)
views.setOnClickPendingIntent(R.id.next_event_rect, calPIntent)
} }
} catch (ex: Exception) { } catch (ex: Exception) {
ex.printStackTrace() ex.printStackTrace()
@ -295,6 +350,7 @@ class MainWidget : AppWidgetProvider() {
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)
views.setViewVisibility(R.id.calendar_weather_rect, View.VISIBLE) views.setViewVisibility(R.id.calendar_weather_rect, View.VISIBLE)
views.setViewVisibility(R.id.special_weather_rect, View.VISIBLE)
val i = Intent(context, WidgetClickListenerReceiver::class.java) val i = Intent(context, WidgetClickListenerReceiver::class.java)
i.action = Actions.ACTION_OPEN_WEATHER_INTENT i.action = Actions.ACTION_OPEN_WEATHER_INTENT
@ -302,6 +358,7 @@ class MainWidget : AppWidgetProvider() {
views.setOnClickPendingIntent(R.id.weather_rect, weatherPIntent) views.setOnClickPendingIntent(R.id.weather_rect, weatherPIntent)
views.setOnClickPendingIntent(R.id.calendar_weather_rect, weatherPIntent) views.setOnClickPendingIntent(R.id.calendar_weather_rect, weatherPIntent)
views.setOnClickPendingIntent(R.id.special_weather_rect, weatherPIntent)
views.setImageViewBitmap( views.setImageViewBitmap(
R.id.weather_rect, R.id.weather_rect,
@ -312,9 +369,21 @@ class MainWidget : AppWidgetProvider() {
R.id.calendar_weather_rect, R.id.calendar_weather_rect,
BitmapHelper.getBitmapFromView(v.calendar_weather, draw = false) BitmapHelper.getBitmapFromView(v.calendar_weather, draw = false)
) )
views.setImageViewBitmap(
R.id.special_weather_rect,
BitmapHelper.getBitmapFromView(v.calendar_weather, draw = false)
)
if (GlanceProviderHelper.showGlanceProviders(context)) {
views.setViewVisibility(R.id.calendar_weather_rect, View.GONE)
} else {
views.setViewVisibility(R.id.special_weather_rect, View.GONE)
}
} else { } else {
views.setViewVisibility(R.id.weather_rect, View.GONE) views.setViewVisibility(R.id.weather_rect, View.GONE)
views.setViewVisibility(R.id.calendar_weather_rect, View.GONE) views.setViewVisibility(R.id.calendar_weather_rect, View.GONE)
views.setViewVisibility(R.id.special_weather_rect, View.GONE)
} }
} catch (ex: Exception) { } catch (ex: Exception) {
ex.printStackTrace() ex.printStackTrace()
@ -333,8 +402,8 @@ class MainWidget : AppWidgetProvider() {
views.setViewVisibility(R.id.clock_bottom_margin_medium, View.GONE) views.setViewVisibility(R.id.clock_bottom_margin_medium, View.GONE)
views.setViewVisibility(R.id.clock_bottom_margin_large, View.GONE) views.setViewVisibility(R.id.clock_bottom_margin_large, View.GONE)
} else { } else {
views.setTextColor(R.id.time, ColorHelper.getFontColor()) views.setTextColor(R.id.time, ColorHelper.getClockFontColor())
views.setTextColor(R.id.time_am_pm, ColorHelper.getFontColor()) views.setTextColor(R.id.time_am_pm, ColorHelper.getClockFontColor())
views.setTextViewTextSize( views.setTextViewTextSize(
R.id.time, R.id.time,
TypedValue.COMPLEX_UNIT_SP, TypedValue.COMPLEX_UNIT_SP,
@ -354,7 +423,7 @@ class MainWidget : AppWidgetProvider() {
views.setOnClickPendingIntent(R.id.time, clockPIntent) views.setOnClickPendingIntent(R.id.time, clockPIntent)
views.setOnClickPendingIntent(R.id.time_am_pm, clockPIntent) views.setOnClickPendingIntent(R.id.time_am_pm, clockPIntent)
views.setViewVisibility(R.id.time, View.VISIBLE) views.setViewVisibility(R.id.time, View.VISIBLE)
views.setViewVisibility(R.id.time_am_pm, View.VISIBLE) views.setViewVisibility(R.id.time_am_pm, if (Preferences.showAMPMIndicator) View.VISIBLE else View.GONE)
views.setViewVisibility( views.setViewVisibility(
R.id.clock_bottom_margin_none, R.id.clock_bottom_margin_none,
@ -387,10 +456,14 @@ class MainWidget : AppWidgetProvider() {
val eventRepository = EventRepository(context) val eventRepository = EventRepository(context)
val v = View.inflate(context, R.layout.the_widget, null) val v = View.inflate(context, R.layout.the_widget, null)
val now = Calendar.getInstance() val now = Calendar.getInstance().apply {
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
v.empty_layout.visibility = View.VISIBLE v.empty_layout.visibility = View.VISIBLE
v.calendar_layout.visibility = View.GONE v.calendar_layout.visibility = View.GONE
v.next_event_difference_time.visibility = View.GONE
v.action_next.isVisible = false v.action_next.isVisible = false
v.action_previous.isVisible = false v.action_previous.isVisible = false
@ -406,7 +479,7 @@ class MainWidget : AppWidgetProvider() {
v.next_event.text = nextEvent.title v.next_event.text = nextEvent.title
if (Preferences.showDiffTime && now.timeInMillis < (nextEvent.startDate - 1000 * 60 * 60)) { if (Preferences.showDiffTime && now.timeInMillis < nextEvent.startDate) {
v.next_event_difference_time.text = if (!nextEvent.allDay) { v.next_event_difference_time.text = if (!nextEvent.allDay) {
SettingsStringHelper.getDifferenceText( SettingsStringHelper.getDifferenceText(
context, context,
@ -428,8 +501,8 @@ class MainWidget : AppWidgetProvider() {
} else { } else {
v.second_row_icon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.round_today)) v.second_row_icon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.round_today))
if (!nextEvent.allDay) { if (!nextEvent.allDay) {
val startHour = DateFormat.getTimeInstance(DateFormat.SHORT).format(nextEvent.startDate) val startHour = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()).format(nextEvent.startDate)
val endHour = DateFormat.getTimeInstance(DateFormat.SHORT).format(nextEvent.endDate) val endHour = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()).format(nextEvent.endDate)
var dayDiff = TimeUnit.MILLISECONDS.toDays(nextEvent.endDate - nextEvent.startDate) var dayDiff = TimeUnit.MILLISECONDS.toDays(nextEvent.endDate - nextEvent.startDate)
@ -452,45 +525,118 @@ class MainWidget : AppWidgetProvider() {
} else { } else {
val flags: Int = DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_ABBREV_MONTH val flags: Int = DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_ABBREV_MONTH
v.next_event_date.text = DateUtils.formatDateTime(context, now.timeInMillis, flags).getCapWordString() v.next_event_date.text = DateUtils.formatDateTime(context, now.timeInMillis, flags)
} }
} }
v.empty_layout.visibility = View.GONE v.empty_layout.visibility = View.GONE
v.calendar_layout.visibility = View.VISIBLE v.calendar_layout.visibility = View.VISIBLE
} else if (Preferences.showNextAlarm && nextAlarm != "") { } else if (GlanceProviderHelper.showGlanceProviders(context)) {
v.second_row_icon.setImageDrawable( v.second_row_icon.isVisible = true
ContextCompat.getDrawable( loop@ for (provider:Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(context)) {
context, when (provider) {
R.drawable.round_alarm Constants.GlanceProviderId.PLAYING_SONG -> {
) if (MediaPlayerHelper.isSomeonePlaying(context)) {
) v.second_row_icon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_music_note
)
)
v.next_event_date.text = MediaPlayerHelper.getMediaInfo()
break@loop
}
}
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
if (Preferences.showNextAlarm && nextAlarm != "") {
v.second_row_icon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_alarm
)
)
v.next_event_date.text = AlarmHelper.getNextAlarm(context)
break@loop
}
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
if (Preferences.isBatteryLevelLow) {
v.second_row_icon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_battery_charging_full
)
)
v.next_event_date.text = context.getString(R.string.battery_low_warning)
break@loop
}
}
Constants.GlanceProviderId.CUSTOM_INFO -> {
if (Preferences.customNotes.isNotEmpty()) {
v.second_row_icon.isVisible = false
v.next_event_date.text = Preferences.customNotes
v.next_event_date.maxLines = 2
break@loop
}
}
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
if (Preferences.showDailySteps && Preferences.googleFitSteps > 0) {
v.second_row_icon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_steps
)
)
v.next_event_date.text = context.getString(R.string.daily_steps_counter).format(Preferences.googleFitSteps)
break@loop
}
}
}
}
v.next_event.text = DateHelper.getDateText(context, now) v.next_event.text = DateHelper.getDateText(context, now)
v.next_event_date.text = AlarmHelper.getNextAlarm(context)
v.empty_layout.visibility = View.GONE v.empty_layout.visibility = View.GONE
v.calendar_layout.visibility = View.VISIBLE v.calendar_layout.visibility = View.VISIBLE
} }
// Color // Color
listOf<TextView>(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.next_event_date, v.divider2, v.calendar_temp).forEach { listOf<TextView>(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.divider3, v.special_temp).forEach {
it.setTextColor(ColorHelper.getFontColor()) it.setTextColor(ColorHelper.getFontColor())
} }
listOf<ImageView>(v.second_row_icon, v.action_next, v.action_previous).forEach { if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) {
listOf<ImageView>(v.action_next, v.action_previous)
} else {
listOf<ImageView>(v.action_next, v.action_previous, v.empty_weather_icon, v.special_weather_icon)
}.forEach {
it.setColorFilter(ColorHelper.getFontColor()) it.setColorFilter(ColorHelper.getFontColor())
} }
listOf<TextView>(v.next_event_date, v.divider2, v.calendar_temp).forEach {
it.setTextColor(ColorHelper.getSecondaryFontColor())
}
if (Preferences.weatherIconPack == Constants.WeatherIconPack.DEFAULT.value) {
listOf<ImageView>(v.second_row_icon)
} else {
listOf<ImageView>(v.second_row_icon, v.weather_icon)
}.forEach {
it.setColorFilter(ColorHelper.getSecondaryFontColor())
}
// Text Size // Text Size
listOf<Pair<TextView, Float>>( listOf<Pair<TextView, Float>>(
v.empty_date to Preferences.textMainSize, v.empty_date to Preferences.textMainSize,
v.divider1 to Preferences.textMainSize, v.divider1 to (Preferences.textMainSize - 2),
v.temp to Preferences.textMainSize, v.temp to Preferences.textMainSize,
v.next_event to Preferences.textMainSize, v.next_event to Preferences.textMainSize,
v.next_event_difference_time to Preferences.textMainSize, v.next_event_difference_time to Preferences.textMainSize,
v.next_event_date to Preferences.textSecondSize, v.next_event_date to Preferences.textSecondSize,
v.divider2 to Preferences.textSecondSize, v.divider2 to (Preferences.textSecondSize - 2),
v.calendar_temp to Preferences.textSecondSize v.calendar_temp to Preferences.textSecondSize,
v.divider3 to (Preferences.textMainSize - 2),
v.special_temp to Preferences.textMainSize
).forEach { ).forEach {
it.first.setTextSize(TypedValue.COMPLEX_UNIT_SP, it.second) it.first.setTextSize(TypedValue.COMPLEX_UNIT_SP, it.second)
} }
@ -499,11 +645,11 @@ class MainWidget : AppWidgetProvider() {
v.second_row_icon.scaleX = Preferences.textSecondSize / 18f v.second_row_icon.scaleX = Preferences.textSecondSize / 18f
v.second_row_icon.scaleY = Preferences.textSecondSize / 18f v.second_row_icon.scaleY = Preferences.textSecondSize / 18f
v.weather_icon.scaleX = Preferences.textSecondSize / 16f v.weather_icon.scaleX = Preferences.textSecondSize / 14f
v.weather_icon.scaleY = Preferences.textSecondSize / 16f v.weather_icon.scaleY = Preferences.textSecondSize / 14f
v.empty_weather_icon.scaleX = Preferences.textMainSize / 20f v.empty_weather_icon.scaleX = Preferences.textMainSize / 18f
v.empty_weather_icon.scaleY = Preferences.textMainSize / 20f v.empty_weather_icon.scaleY = Preferences.textMainSize / 18f
v.action_next.scaleX = Preferences.textMainSize / 28f v.action_next.scaleX = Preferences.textMainSize / 28f
v.action_next.scaleY = Preferences.textMainSize / 28f v.action_next.scaleY = Preferences.textMainSize / 28f
@ -511,6 +657,9 @@ class MainWidget : AppWidgetProvider() {
v.action_previous.scaleX = Preferences.textMainSize / 28f v.action_previous.scaleX = Preferences.textMainSize / 28f
v.action_previous.scaleY = Preferences.textMainSize / 28f v.action_previous.scaleY = Preferences.textMainSize / 28f
v.special_weather_icon.scaleX = Preferences.textMainSize / 20f
v.special_weather_icon.scaleY = Preferences.textMainSize / 20f
// Shadows // Shadows
val shadowRadius = when (Preferences.textShadow) { val shadowRadius = when (Preferences.textShadow) {
@ -532,14 +681,14 @@ class MainWidget : AppWidgetProvider() {
else -> 0f else -> 0f
} }
listOf<TextView>(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.next_event_date, v.divider2, v.calendar_temp).forEach { listOf<TextView>(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.next_event_date, v.divider2, v.calendar_temp, v.divider3, v.special_temp).forEach {
it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor) it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor)
} }
// Custom Font // Custom Font
if (Preferences.customFont == Constants.CUSTOM_FONT_PRODUCT_SANS) { if (Preferences.customFont == Constants.CUSTOM_FONT_PRODUCT_SANS) {
val productSans: Typeface = Typeface.createFromAsset(context.assets, "fonts/product_sans_regular.ttf") val productSans: Typeface = Typeface.createFromAsset(context.assets, "fonts/product_sans_regular.ttf")
listOf<TextView>(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.next_event_date, v.divider2, v.calendar_temp).forEach { listOf<TextView>(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.next_event_date, v.divider2, v.calendar_temp, v.divider3, v.special_temp).forEach {
it.typeface = productSans it.typeface = productSans
} }
} }
@ -548,24 +697,41 @@ class MainWidget : AppWidgetProvider() {
if (Preferences.showWeather && Preferences.weatherIcon != "") { if (Preferences.showWeather && Preferences.weatherIcon != "") {
v.weather.visibility = View.VISIBLE v.weather.visibility = View.VISIBLE
v.calendar_weather.visibility = View.VISIBLE v.calendar_weather.visibility = View.VISIBLE
v.special_weather.visibility = View.VISIBLE
val currentTemp = String.format(Locale.getDefault(), "%.0f °%s", Preferences.weatherTemp, Preferences.weatherRealTempUnit) val currentTemp = String.format(Locale.getDefault(), "%.0f °%s", Preferences.weatherTemp, Preferences.weatherRealTempUnit)
val icon: String = Preferences.weatherIcon val icon: String = Preferences.weatherIcon
if (icon == "") { if (icon == "") {
v.weather_icon.visibility = View.GONE v.weather_icon.visibility = View.GONE
v.empty_weather_icon.visibility = View.GONE v.empty_weather_icon.visibility = View.GONE
v.special_weather_icon.visibility = View.GONE
} else { } else {
v.weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(icon)) v.weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(icon))
v.empty_weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(icon)) v.empty_weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(icon))
v.special_weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(icon))
v.weather_icon.visibility = View.VISIBLE v.weather_icon.visibility = View.VISIBLE
v.empty_weather_icon.visibility = View.VISIBLE v.empty_weather_icon.visibility = View.VISIBLE
v.special_weather_icon.visibility = View.VISIBLE
} }
v.temp.text = currentTemp v.temp.text = currentTemp
v.calendar_temp.text = currentTemp v.calendar_temp.text = currentTemp
v.special_temp.text = currentTemp
if (GlanceProviderHelper.showGlanceProviders(context)) {
v.calendar_weather.visibility = View.GONE
} else {
v.special_weather.visibility = View.GONE
}
} else { } else {
v.weather.visibility = View.GONE v.weather.visibility = View.GONE
v.calendar_weather.visibility = View.GONE v.calendar_weather.visibility = View.GONE
v.special_weather.visibility = View.GONE
}
// Dividers
arrayOf(v.divider1, v.divider2, v.divider3).forEach {
it.isVisible = Preferences.showDividers
} }
return v return v

View File

@ -212,4 +212,13 @@ fun String.getCapWordString(): String {
} catch (e: Exception) { } catch (e: Exception) {
this this
} }
}
fun Context.checkIfFitInstalled(): Boolean {
return try {
packageManager.getPackageInfo("com.google.android.apps.fitness", PackageManager.GET_ACTIVITIES)
true
} catch (e: Exception) {
false
}
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

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