Compare commits

...

47 Commits

Author SHA1 Message Date
90f0d7de00 Build v2.0.10 2020-05-19 20:07:08 +02:00
66ad5e0839 Fix #133 2020-05-19 20:01:11 +02:00
2ac7e072e5 Close #134 2020-05-19 19:49:21 +02:00
a3392dabcf Add a new cool icon pack 2020-05-19 19:48:33 +02:00
c2c54a04d2 Merge pull request #131 from LastInLine/patch-1
Update strings.xml
2020-05-19 10:28:33 +02:00
2c237058b3 Close #132 2020-05-19 10:27:54 +02:00
aa2b4b0510 Update strings.xml
Singular/plural switch for weather info visibility and changed awkward phrasing around time remaining until an event.
2020-05-18 21:38:56 -04:00
4c27d1dd9b Fix future events 2020-05-18 20:36:59 +02:00
eba5575ee2 Fix event not found error 2020-05-18 14:14:32 +02:00
aff7e407ca Merge pull request #127 from chreddy/patch-2
Adding Danish translation
2020-05-18 12:47:39 +02:00
f7fc31d968 Fix all day events widget date 2020-05-18 12:44:46 +02:00
90b588603d Update strings.xml
settings_general_title was a mistake indeed. This has been changed.

As for settings_second_row_info_subtitle_0, this has been translated correctly already. However I noticed that I must've overlooked next_alarm_warning previously, so I've added a translation of this as well.
2020-05-18 12:38:57 +02:00
5d9bd11abb Fix #129 2020-05-18 12:19:15 +02:00
01005ec443 Clean the project 2020-05-18 12:07:04 +02:00
eaf6400e8b Merge pull request #84 from zmni/translation
Create Indonesian translation
2020-05-18 12:05:21 +02:00
30339c7375 Merge pull request #124 from shtraus12/master
Added Russian translation (values-ru)
2020-05-18 12:00:08 +02:00
87da284be4 Update strings 2020-05-18 11:49:57 +02:00
573c6d03e5 Adding Danish translation
I re-did my previous translation, based on the newly updated string.xml.
Because of this, I've closed the previous pull request as well.
2020-05-18 00:27:53 +02:00
79e87b0648 Add files via upload 2020-05-17 16:51:41 +03:00
9e9a91690e Update Indonesian translation 2020-05-17 19:12:38 +07:00
5f699af509 Show translated in-app purchases 2020-05-17 13:25:19 +02:00
aae40b9dd3 Merge branch 'master' into translation
* master:
  Update strings
  Add settings header
  Add battery level handler
  Fix #120
2020-05-17 18:22:52 +07:00
cebd679856 Update strings 2020-05-17 13:19:55 +02:00
06443ddddb Add settings header 2020-05-16 20:24:11 +02:00
233761a169 Add battery level handler 2020-05-16 14:34:04 +02:00
b81461f725 Fix #120 2020-05-16 12:47:29 +02:00
26b1948b70 Update Indonesian translation 2020-05-14 22:49:58 +07:00
af64818dff Merge branch 'master' into translation
* master:
  Update the build version
  Update event repository
  Update the events selector. Fix #95
  Fix #113, #111, #79, #107, #109
  Add weather icon pack, updates frequency and fix google fit
  Update ui
  Update icons
  Fix dark theme toggle issue
  Bugfixes
  Update google fit integration
  Update beta release
  update gitignore
  Clean project files
  Add google fit connection
  Add google fit integration
2020-05-14 22:14:50 +07:00
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
f28596c194 Merge branch 'master' into translation
* master:
  Gix switcher, update glance, add glance order, add ampm toggle
  Merge crud-device
  Add dividers toggle
  Add integreations activity, removed long time until intervals
  Change music fragment to at a glance
  Update build version
  Fix #83
  Add current song
  Add clock text color
2020-05-10 04:27:13 +07:00
7f41a92a91 Update Indonesian translation 2020-05-10 04:06:26 +07:00
9a63b9bde2 Gix switcher, update glance, add glance order, add ampm toggle 2020-05-09 22:37:59 +02:00
40c9253159 Create Indonesian translation 2020-05-08 19:04:48 +07:00
477 changed files with 3626 additions and 1842 deletions

5
.gitignore vendored
View File

@ -1,11 +1,10 @@
*.iml *.iml
.gradle .gradle
/local.properties /local.properties
/.idea/workspace.xml /.idea/*
/.idea/libraries
.DS_Store .DS_Store
/build /build
/captures /captures
.externalNativeBuild .externalNativeBuild
/tasksintegration/build /tasksintegration/build
apikey.properties /app/google-services.json

Binary file not shown.

2
.idea/gradle.xml generated
View File

@ -11,8 +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$/googlefit" />
<option value="$PROJECT_DIR$/tasksintegration" />
</set> </set>
</option> </option>
<option name="resolveModulePerSourceSet" value="false" /> <option name="resolveModulePerSourceSet" value="false" />

2
.idea/modules.xml generated
View File

@ -4,8 +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$/googlefit/googlefit.iml" filepath="$PROJECT_DIR$/googlefit/googlefit.iml" group="Another Widget/googlefit" />
<module fileurl="file://$PROJECT_DIR$/tasksintegration/tasksintegration.iml" filepath="$PROJECT_DIR$/tasksintegration/tasksintegration.iml" group="Another Widget/tasksintegration" />
</modules> </modules>
</component> </component>
</project> </project>

View File

@ -13,6 +13,13 @@ Also, as much as possible, there are always updates and new features in the shor
Help me developing with feedback and support me on how you can. Help me developing with feedback and support me on how you can.
<div style="text-align:center"><a href="https://play.google.com/store/apps/details?id=com.tommasoberlose.anotherwidget" target="_blank"><img src="google-play-badge.png" height="100" /></a></div> <div style="text-align:center"><a href="https://play.google.com/store/apps/details?id=com.tommasoberlose.anotherwidget" target="_blank"><img src="google-play-badge.png" height="100" /></a></div>
Help with translations
-------
Hey! You could view the file strings.xml ([here](https://github.com/tommasoberlose/another-widget/blob/master/app/src/main/res/values/strings.xml)) that contains the English version of the app strings.
You have to copy the file, create a copy of it inside the folder values-[LANGUAGE-SUFFIX] with the translated strings and create a pull request to submit your changes.
License License
------- -------
Copyright (C) 2017-2020 Tommaso Berlose (http://tommasoberlose.com) Copyright (C) 2017-2020 Tommaso Berlose (http://tommasoberlose.com)

View File

@ -18,8 +18,8 @@ android {
applicationId "com.tommasoberlose.anotherwidget" applicationId "com.tommasoberlose.anotherwidget"
minSdkVersion 23 minSdkVersion 23
targetSdkVersion 29 targetSdkVersion 29
versionCode 81 versionCode 98
versionName "2.0.6" versionName "2.0.10"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
@ -52,10 +52,6 @@ android {
} }
viewBinding.enabled = true viewBinding.enabled = true
dynamicFeatures = [":tasksintegration", ":googlefit"]
} }
dependencies { dependencies {
@ -98,6 +94,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'

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,10 +10,13 @@
<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"
android:fullBackupContent="@xml/my_backup_rules"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:name=".AWApplication" android:name=".AWApplication"
@ -82,44 +85,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"
@ -140,12 +115,36 @@
<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=".services.BatteryListenerJob" android:permission="android.permission.BIND_JOB_SERVICE" />
<service android:name=".receivers.MusicNotificationListener" <service android:name=".receivers.MusicNotificationListener"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"> android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter> <intent-filter>
<action android:name="android.service.notification.NotificationListenerService" /> <action android:name="android.service.notification.NotificationListenerService" />
</intent-filter> </intent-filter>
</service> </service>
<receiver android:name=".receivers.BatteryLevelReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
<action android:name="android.intent.action.BATTERY_LOW"/>
<action android:name="android.intent.action.BATTERY_OKAY"/>
<action android:name="android.intent.action.BATTERY_CHANGED"/>
</intent-filter>
</receiver>
<receiver android:name=".receivers.ActivityDetectionReceiver"
android:exported="false"
android:permission="com.google.android.gms.permission.ACTIVITY_RECOGNITION">
<intent-filter>
<action android:name="com.mypackage.ACTION_PROCESS_ACTIVITY_TRANSITIONS" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</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

@ -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

@ -16,17 +16,19 @@ 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 clearEvents() {
realm.executeTransactionAsync { realm.executeTransaction { realm ->
it.where(Event::class.java).findAll().deleteAllFromRealm() realm.where(Event::class.java).findAll().deleteAllFromRealm()
} }
}
fun resetNextEventData() {
Preferences.bulk { Preferences.bulk {
remove(Preferences::nextEventId) remove(Preferences::nextEventId)
remove(Preferences::nextEventName) remove(Preferences::nextEventName)
@ -39,12 +41,27 @@ class EventRepository(val context: Context) {
} }
fun saveNextEventData(event: Event) { fun saveNextEventData(event: Event) {
Preferences.nextEventId = event.id Preferences.nextEventId = event.eventID
} }
fun getNextEvent(): Event? { fun getNextEvent(): Event? {
val nextEvent = getEventByEventId(Preferences.nextEventId) val nextEvent = getEventByEventId(Preferences.nextEventId)
return if (nextEvent != null && nextEvent.endDate > Calendar.getInstance().timeInMillis) { val now = Calendar.getInstance().timeInMillis
val limit = Calendar.getInstance().apply {
timeInMillis = now
when (Preferences.showUntil) {
0 -> add(Calendar.HOUR, 3)
1 -> add(Calendar.HOUR, 6)
2 -> add(Calendar.HOUR, 12)
3 -> add(Calendar.DAY_OF_MONTH, 1)
4 -> add(Calendar.DAY_OF_MONTH, 3)
5 -> add(Calendar.DAY_OF_MONTH, 7)
6 -> add(Calendar.MINUTE, 30)
7 -> add(Calendar.HOUR, 1)
else -> add(Calendar.HOUR, 6)
}
}
val event = if (nextEvent != null && nextEvent.endDate > now && nextEvent.startDate < limit.timeInMillis) {
nextEvent nextEvent
} else { } else {
val events = getEvents() val events = getEvents()
@ -57,18 +74,30 @@ class EventRepository(val context: Context) {
null 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 = getEvents() 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()
@ -80,11 +109,11 @@ class EventRepository(val context: Context) {
fun goToPreviousEvent() { fun goToPreviousEvent() {
val eventList = getEvents() 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()
@ -93,14 +122,35 @@ class EventRepository(val context: Context) {
MainWidget.updateWidget(context) MainWidget.updateWidget(context)
} }
fun getEvents(): RealmResults<Event> { fun getFutureEvents(): RealmResults<Event> {
val now = Calendar.getInstance().timeInMillis val now = Calendar.getInstance().timeInMillis
val list = realm.where(Event::class.java).greaterThan("endDate", now).findAll() realm.refresh()
realm.executeTransactionAsync { return realm.where(Event::class.java).greaterThan("endDate", now).findAll()
it.where(Event::class.java).lessThanOrEqualTo("endDate", now).findAll().deleteAllFromRealm() }
private fun getEvents(): RealmResults<Event> {
val now = Calendar.getInstance().timeInMillis
val limit = Calendar.getInstance().apply {
timeInMillis = now
when (Preferences.showUntil) {
0 -> add(Calendar.HOUR, 3)
1 -> add(Calendar.HOUR, 6)
2 -> add(Calendar.HOUR, 12)
3 -> add(Calendar.DAY_OF_MONTH, 1)
4 -> add(Calendar.DAY_OF_MONTH, 3)
5 -> add(Calendar.DAY_OF_MONTH, 7)
6 -> add(Calendar.MINUTE, 30)
7 -> add(Calendar.HOUR, 1)
else -> add(Calendar.HOUR, 6)
}
} }
return list realm.refresh()
return realm.where(Event::class.java).greaterThan("endDate", now).lessThanOrEqualTo("startDate", limit.timeInMillis).findAll()
} }
fun getEventsCount(): Int = getEvents().size fun getEventsCount(): Int = getEvents().size
fun close() {
realm.close()
}
} }

View File

@ -18,8 +18,20 @@ object Constants {
enum class GlanceProviderId(val id: String) { enum class GlanceProviderId(val id: String) {
PLAYING_SONG("PLAYING_SONG"), PLAYING_SONG("PLAYING_SONG"),
NEXT_CLOCK_ALARM("NEXT_CLOCK_ALARM"), NEXT_CLOCK_ALARM("NEXT_CLOCK_ALARM"),
// BATTERY_LEVEL_LOW("BATTERY_LEVEL_LOW"), BATTERY_LEVEL_LOW("BATTERY_LEVEL_LOW"),
// CUSTOM_INFO("CUSTOM_INFO"), CUSTOM_INFO("CUSTOM_INFO"),
// GOOGLE_FIT_STEPS("GOOGLE_FIT_STEPS") 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),
COOL(2)
} }
} }

View File

@ -30,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 = "")
@ -40,14 +41,23 @@ 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 clockTextColor by stringPref(default = "#FFFFFF")
var clockTextAlpha by stringPref(default = "FF") var clockTextAlpha by stringPref(default = "FF")
var showAMPMIndicator by booleanPref(default = true)
var weatherIconPack by intPref(default = Constants.WeatherIconPack.DEFAULT.value)
// Global // Global
var textMainSize by floatPref(key = "PREF_TEXT_MAIN_SIZE", default = 26f) var textMainSize by floatPref(key = "PREF_TEXT_MAIN_SIZE", default = 26f)
@ -77,10 +87,13 @@ object Preferences : KotprefModel() {
// Glance // Glance
var showGlance by booleanPref(default = true) var showGlance by booleanPref(default = true)
var enabledGlanceProviderOrder by stringPref(default = "") var enabledGlanceProviderOrder by stringPref(default = "")
var customInfo by stringPref(default = "") var customNotes by stringPref(default = "")
var showNextAlarm by booleanPref(default = false) var showNextAlarm by booleanPref(default = true)
var showBatteryCharging by booleanPref(default = false)
var isBatteryLevelLow by booleanPref(default = false) var isBatteryLevelLow by booleanPref(default = false)
var isCharging by booleanPref(default = false)
var googleFitSteps by longPref(default = -1) var googleFitSteps by longPref(default = -1)
var showDailySteps by booleanPref(default = false)
var showMusic by booleanPref(default = false) var showMusic by booleanPref(default = false)
var mediaInfoFormat by stringPref(default = "") var mediaInfoFormat by stringPref(default = "")

View File

@ -0,0 +1,23 @@
package com.tommasoberlose.anotherwidget.helpers
import android.content.Context
import android.content.Context.BATTERY_SERVICE
import android.os.BatteryManager
import androidx.core.content.ContextCompat.getSystemService
import com.tommasoberlose.anotherwidget.global.Preferences
object BatteryHelper {
fun updateBatteryInfo(context: Context) {
with(context.getSystemService(BATTERY_SERVICE) as BatteryManager) {
Preferences.isBatteryLevelLow = getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) <= 15
Preferences.isCharging = isCharging
}
}
fun getBatteryLevel(context: Context): Int {
with(context.getSystemService(BATTERY_SERVICE) as BatteryManager) {
return getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
}
}
}

View File

@ -4,6 +4,7 @@ import android.Manifest
import android.content.ContentUris import android.content.ContentUris
import android.content.Context import android.content.Context
import android.provider.CalendarContract import android.provider.CalendarContract
import android.util.Log
import com.tommasoberlose.anotherwidget.services.EventListenerJob import com.tommasoberlose.anotherwidget.services.EventListenerJob
import com.tommasoberlose.anotherwidget.db.EventRepository import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.models.Event import com.tommasoberlose.anotherwidget.models.Event
@ -30,22 +31,16 @@ object CalendarHelper {
val eventList = ArrayList<Event>() val eventList = ArrayList<Event>()
val now = Calendar.getInstance() val now = Calendar.getInstance()
val limit = Calendar.getInstance() val begin = Calendar.getInstance().apply {
when (Preferences.showUntil) { set(Calendar.MILLISECOND, 0)
0 -> limit.add(Calendar.HOUR, 3) set(Calendar.SECOND, 0)
1 -> limit.add(Calendar.HOUR, 6) set(Calendar.MINUTE, 0)
2 -> limit.add(Calendar.HOUR, 12) set(Calendar.HOUR_OF_DAY, 0)
3 -> limit.add(Calendar.DAY_OF_MONTH, 1) }
4 -> limit.add(Calendar.DAY_OF_MONTH, 3) val limit = Calendar.getInstance().apply {
5 -> limit.add(Calendar.DAY_OF_MONTH, 7) timeInMillis = begin.timeInMillis
6 -> limit.add(Calendar.MINUTE, 30) add(Calendar.DAY_OF_YEAR, 2)
7 -> limit.add(Calendar.HOUR, 1)
else -> limit.add(Calendar.HOUR, 6)
} }
val builder = CalendarContract.Instances.CONTENT_URI.buildUpon()
ContentUris.appendId(builder, now.timeInMillis)
ContentUris.appendId(builder, limit.timeInMillis)
if (!context.checkGrantedPermission( if (!context.checkGrantedPermission(
Manifest.permission.READ_CALENDAR Manifest.permission.READ_CALENDAR
@ -55,13 +50,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)
) { ) {
@ -95,6 +90,7 @@ object CalendarHelper {
if (eventList.isEmpty()) { if (eventList.isEmpty()) {
eventRepository.resetNextEventData() eventRepository.resetNextEventData()
eventRepository.clearEvents()
} else { } else {
eventList.sortWith(Comparator { event: Event, event1: Event -> eventList.sortWith(Comparator { event: Event, event1: Event ->
if (event.allDay && event1.allDay) { if (event.allDay && event1.allDay) {
@ -127,6 +123,7 @@ object CalendarHelper {
MainWidget.updateWidget(context) MainWidget.updateWidget(context)
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent()) EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
eventRepository.close()
} }
fun getCalendarList(context: Context): List<me.everything.providers.android.calendar.Calendar> { fun getCalendarList(context: Context): List<me.everything.providers.android.calendar.Calendar> {

View File

@ -30,6 +30,31 @@ object ColorHelper {
Color.parseColor("#000000") Color.parseColor("#000000")
} }
} }
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 { fun getClockFontColor(): Int {
return try { return try {
Color.parseColor("#%s%s".format(Preferences.clockTextAlpha, Preferences.clockTextColor.replace("#", ""))) Color.parseColor("#%s%s".format(Preferences.clockTextAlpha, Preferences.clockTextColor.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

@ -2,15 +2,21 @@ package com.tommasoberlose.anotherwidget.helpers
import android.content.Context import android.content.Context
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.Constants
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.models.GlanceProvider import com.tommasoberlose.anotherwidget.models.GlanceProvider
import com.tommasoberlose.anotherwidget.utils.checkIfFitInstalled
import java.util.ArrayList import java.util.ArrayList
object GlanceProviderHelper { object GlanceProviderHelper {
fun getGlanceProviders(): ArrayList<Constants.GlanceProviderId> { fun getGlanceProviders(context: Context): ArrayList<Constants.GlanceProviderId> {
val enabledProviders = Preferences.enabledGlanceProviderOrder.split(",").filter { it != "" } val enabledProviders = Preferences.enabledGlanceProviderOrder.split(",").filter { it != "" }
val providers = Constants.GlanceProviderId.values() val providers = Constants.GlanceProviderId.values()
.filter {
context.checkIfFitInstalled() || it != Constants.GlanceProviderId.GOOGLE_FIT_STEPS
}.toTypedArray()
providers.sortWith(Comparator { p1, p2 -> providers.sortWith(Comparator { p1, p2 ->
when { when {
@ -37,40 +43,51 @@ object GlanceProviderHelper {
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> { Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
GlanceProvider(providerId.id, GlanceProvider(providerId.id,
context.getString(R.string.settings_show_next_alarm_title), context.getString(R.string.settings_show_next_alarm_title),
R.drawable.round_alarm, R.drawable.round_alarm
context.getString(R.string.settings_show_next_alarm_subtitle)
) )
} }
Constants.GlanceProviderId.PLAYING_SONG -> { Constants.GlanceProviderId.PLAYING_SONG -> {
GlanceProvider(providerId.id, GlanceProvider(providerId.id,
context.getString(R.string.settings_show_music_title), context.getString(R.string.settings_show_music_title),
R.drawable.round_music_note, R.drawable.round_music_note
context.getString(R.string.settings_show_music_enabled_subtitle) )
}
Constants.GlanceProviderId.CUSTOM_INFO -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_custom_notes_title),
R.drawable.round_notes
)
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_low_battery_level_title),
R.drawable.round_battery_charging_full
)
}
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_daily_steps_title),
R.drawable.round_directions_walk
) )
} }
// Constants.GlanceProviderId.CUSTOM_INFO -> {
// GlanceProvider(providerId.id,
// context.getString(R.string.settings_show_next_alarm_title),
// R.drawable.round_event_note
// )
// }
// Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
// GlanceProvider(providerId.id,
// context.getString(R.string.settings_show_next_alarm_title),
// R.drawable.round_battery_charging_full
// )
// }
// Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
// GlanceProvider(providerId.id,
// context.getString(R.string.settings_show_next_alarm_title),
// R.drawable.round_directions_walk
// )
// }
else -> null
} }
} }
fun saveGlanceProviderOrder(list: ArrayList<Constants.GlanceProviderId>) { fun saveGlanceProviderOrder(list: ArrayList<Constants.GlanceProviderId>) {
Preferences.enabledGlanceProviderOrder = list.joinToString(separator = ",") Preferences.enabledGlanceProviderOrder = list.joinToString(separator = ",")
} }
fun showGlanceProviders(context: Context): Boolean {
val eventRepository = EventRepository(context)
BatteryHelper.updateBatteryInfo(context)
val showGlance = Preferences.showGlance && eventRepository.getEventsCount() == 0 && (
(Preferences.showNextAlarm && AlarmHelper.getNextAlarm(context) != "") ||
(MediaPlayerHelper.isSomeonePlaying(context)) ||
(Preferences.showBatteryCharging && Preferences.isCharging || Preferences.isBatteryLevelLow) ||
(Preferences.customNotes.isNotEmpty()) ||
(Preferences.showDailySteps && Preferences.googleFitSteps > 0)
)
eventRepository.close()
return showGlance
}
} }

View File

@ -114,19 +114,41 @@ 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) flags = (Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED or Intent.FLAG_ACTIVITY_CLEAR_TOP)
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate) if (!e.allDay) {
// putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, if (e.allDay) 1 else 0) putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate)
// type = "vnd.android.cursor.item/event" 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_ALL_DAY, if (e.allDay) 1 else 0) putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate)
// type = "vnd.android.cursor.item/event" } 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)
}
} }
} }
} }
@ -161,6 +183,10 @@ object IntentHelper {
} }
} }
fun getBatteryIntent(): Intent {
return Intent(Intent.ACTION_POWER_USAGE_SUMMARY)
}
fun getMusicIntent(context: Context): Intent { fun getMusicIntent(context: Context): Intent {
return when (Preferences.mediaPlayerPackage) { return when (Preferences.mediaPlayerPackage) {
"" -> { "" -> {
@ -178,4 +204,15 @@ object IntentHelper {
} }
} }
} }
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

@ -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
@ -68,11 +70,38 @@ 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() val minutes = TimeUnit.MILLISECONDS.toMinutes(difference) - 60 * TimeUnit.MILLISECONDS.toHours(difference)
return if (minutes < 1 || minutes > 30) {
DateUtils.getRelativeTimeSpanString(
start,
now - 1000 * 60 * 40,
DateUtils.HOUR_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE
).toString()
} else {
DateUtils.getRelativeTimeSpanString(
start,
now,
DateUtils.HOUR_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE
).toString()
}
} }
eventDate.dayOfYear == nowDate.plusDays(1).dayOfYear -> { eventDate.dayOfYear == nowDate.plusDays(1).dayOfYear -> {
return String.format("%s", context.getString(R.string.tomorrow)) return String.format("%s", context.getString(R.string.tomorrow))

View File

@ -5,7 +5,7 @@ import android.content.Context
import android.os.Build import android.os.Build
import com.google.android.gms.location.LocationServices import com.google.android.gms.location.LocationServices
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.fragments.MainFragment import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
@ -47,81 +47,177 @@ object WeatherHelper {
} }
fun getWeatherIconResource(icon: String): Int { fun getWeatherIconResource(icon: String): Int {
when (icon) { return when (icon) {
"01d" -> { "01d" -> {
return R.drawable.clear_day when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.clear_day_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.clear_day_2
else -> R.drawable.clear_day
}
} }
"02d" -> { "02d" -> {
return R.drawable.partly_cloudy when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.partly_cloudy_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.partly_cloudy_2
else -> R.drawable.partly_cloudy
}
} }
"03d" -> { "03d" -> {
return R.drawable.mostly_cloudy when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.mostly_cloudy_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.mostly_cloudy_2
else -> R.drawable.mostly_cloudy
}
} }
"04d" -> { "04d" -> {
return R.drawable.cloudy_weather when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.cloudy_weather_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.cloudy_weather_2
else -> R.drawable.cloudy_weather
}
} }
"09d" -> { "09d" -> {
return R.drawable.storm_weather_day when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.storm_weather_day_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.storm_weather_day_2
else -> R.drawable.storm_weather_day
}
} }
"10d" -> { "10d" -> {
return R.drawable.rainy_day when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.rainy_day_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.rainy_day_2
else -> R.drawable.rainy_day
}
} }
"11d" -> { "11d" -> {
return R.drawable.thunder_day when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.thunder_day_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.thunder_day_2
else -> R.drawable.thunder_day
}
} }
"13d" -> { "13d" -> {
return R.drawable.snow_day when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.snow_day_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.snow_day_2
else -> R.drawable.snow_day
}
} }
"50d" -> { "50d" -> {
return R.drawable.haze_day when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.haze_day_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.haze_day_2
else -> R.drawable.haze_day
}
} }
"80d" -> { "80d" -> {
return R.drawable.windy_day when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.windy_day_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.windy_day_2
else -> R.drawable.windy_day
}
} }
"81d" -> { "81d" -> {
return R.drawable.rain_snow_day when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.rain_snow_day_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.rain_snow_day_2
else -> R.drawable.rain_snow_day
}
} }
"82d" -> { "82d" -> {
return R.drawable.haze_weather when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.haze_weather_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.haze_weather_2
else -> R.drawable.haze_weather
}
} }
"01n" -> { "01n" -> {
return R.drawable.clear_night when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.clear_night_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.clear_night_2
else -> R.drawable.clear_night
}
} }
"02n" -> { "02n" -> {
return R.drawable.partly_cloudy_night when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.partly_cloudy_night_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.partly_cloudy_night_2
else -> R.drawable.partly_cloudy_night
}
} }
"03n" -> { "03n" -> {
return R.drawable.mostly_cloudy_night when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.mostly_cloudy_night_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.mostly_cloudy_night_2
else -> R.drawable.mostly_cloudy_night
}
} }
"04n" -> { "04n" -> {
return R.drawable.cloudy_weather when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.cloudy_weather_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.cloudy_weather_2
else -> R.drawable.cloudy_weather
}
} }
"09n" -> { "09n" -> {
return R.drawable.storm_weather_night when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.storm_weather_night_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.storm_weather_night_2
else -> R.drawable.storm_weather_night
}
} }
"10n" -> { "10n" -> {
return R.drawable.rainy_night when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.rainy_night_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.rainy_night_2
else -> R.drawable.rainy_night
}
} }
"11n" -> { "11n" -> {
return R.drawable.thunder_night when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.thunder_night_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.thunder_night_2
else -> R.drawable.thunder_night
}
} }
"13n" -> { "13n" -> {
return R.drawable.snow_night when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.snow_night_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.snow_night_2
else -> R.drawable.snow_night
}
} }
"50n" -> { "50n" -> {
return R.drawable.haze_night when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.haze_night_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.haze_night_2
else -> R.drawable.haze_night
}
} }
"80n" -> { "80n" -> {
return R.drawable.windy_night when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.windy_night_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.windy_night_2
else -> R.drawable.windy_night
}
} }
"81n" -> { "81n" -> {
return R.drawable.rain_snow_night when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.rain_snow_night_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.rain_snow_night_2
else -> R.drawable.rain_snow_night
}
} }
"82n" -> { "82n" -> {
return R.drawable.haze_weather when (Preferences.weatherIconPack) {
Constants.WeatherIconPack.COOL.value -> R.drawable.haze_weather_3
Constants.WeatherIconPack.MINIMAL.value -> R.drawable.haze_weather_2
else -> R.drawable.haze_weather
}
} }
else -> { else -> {
return R.drawable.unknown return R.drawable.unknown

View File

@ -3,6 +3,7 @@ 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.google.firebase.crashlytics.FirebaseCrashlytics
import com.tommasoberlose.anotherwidget.db.EventRepository import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
@ -24,19 +25,9 @@ object WidgetHelper {
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)
@ -53,11 +44,4 @@ object WidgetHelper {
width to second * factor width to second * factor
} }
} }
fun showSpecialWeather(context: Context): Boolean {
return EventRepository(context).getEventsCount() == 0 && (
(Preferences.showNextAlarm && AlarmHelper.getNextAlarm(context) != "") ||
(MediaPlayerHelper.isSomeonePlaying(context))
)
}
} }

View File

@ -3,10 +3,5 @@ package com.tommasoberlose.anotherwidget.models
class GlanceProvider( class GlanceProvider(
val id: String, val id: String,
val title: String, val title: String,
val icon: Int, val icon: Int
val label: String = "",
val enabled: Boolean = false,
val isPermissionRequired: Boolean = false,
val isPermissionGranted: (() -> Boolean)? = null,
val requestPermission: (() -> Unit)? = null
) )

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,23 @@
package com.tommasoberlose.anotherwidget.receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.BatteryManager
import android.util.Log
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.toast
class BatteryLevelReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when(intent.action) {
Intent.ACTION_BATTERY_LOW -> Preferences.isBatteryLevelLow = true
Intent.ACTION_BATTERY_OKAY -> Preferences.isBatteryLevelLow = false
Intent.ACTION_POWER_CONNECTED -> Preferences.isCharging = true
Intent.ACTION_POWER_DISCONNECTED -> Preferences.isCharging = false
}
MainWidget.updateWidget(context)
}
}

View File

@ -24,5 +24,6 @@ class NewCalendarEventReceiver : BroadcastReceiver() {
eventRepository.goToPreviousEvent() eventRepository.goToPreviousEvent()
} }
} }
eventRepository.close()
} }
} }

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,6 +10,9 @@ 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.BatteryHelper
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
import com.tommasoberlose.anotherwidget.models.Event import com.tommasoberlose.anotherwidget.models.Event
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
@ -26,10 +29,12 @@ class UpdatesReceiver : BroadcastReceiver() {
Intent.ACTION_TIME_CHANGED, Intent.ACTION_TIME_CHANGED,
Intent.ACTION_TIMEZONE_CHANGED, Intent.ACTION_TIMEZONE_CHANGED,
Intent.ACTION_LOCALE_CHANGED, Intent.ACTION_LOCALE_CHANGED,
Actions.ACTION_CALENDAR_UPDATE -> CalendarHelper.updateEventList(context) Intent.ACTION_DATE_CHANGED,
Actions.ACTION_CALENDAR_UPDATE -> {
CalendarHelper.updateEventList(context)
}
"com.sec.android.widgetapp.APPWIDGET_RESIZE", "com.sec.android.widgetapp.APPWIDGET_RESIZE",
Intent.ACTION_DATE_CHANGED,
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)
@ -48,7 +53,7 @@ class UpdatesReceiver : BroadcastReceiver() {
if (eventId == null) { if (eventId == null) {
removeUpdates(context) removeUpdates(context)
eventRepository.getEvents().forEach { event -> eventRepository.getFutureEvents().forEach { event ->
setEventUpdate(context, event) setEventUpdate(context, event)
} }
} else { } else {
@ -57,6 +62,7 @@ class UpdatesReceiver : BroadcastReceiver() {
setEventUpdate(context, event) setEventUpdate(context, event)
} }
} }
eventRepository.close()
} }
private fun setEventUpdate(context: Context, event: Event) { private fun setEventUpdate(context: Context, event: Event) {
@ -66,11 +72,84 @@ class UpdatesReceiver : BroadcastReceiver() {
set(Calendar.MILLISECOND, 0) set(Calendar.MILLISECOND, 0)
} }
val diff = Period(now.timeInMillis, event.startDate) val diff = Period(now.timeInMillis, event.startDate)
if (event.startDate > now.timeInMillis) { val limit = when (Preferences.showUntil) {
// Update the widget every hour till the event 0 -> 1000 * 60 * 60 * 3
setExactAndAllowWhileIdle( 1 -> 1000 * 60 * 60 * 6
2 -> 1000 * 60 * 60 * 12
3 -> 1000 * 60 * 60 * 24
4 -> 1000 * 60 * 60 * 24 * 3
5 -> 1000 * 60 * 60 * 24 * 7
6 -> 1000 * 60 * 30
7 -> 1000 * 60 * 60
else -> 1000 * 60 * 60 * 6
}
if (event.startDate <= limit) {
if (event.startDate > now.timeInMillis) {
// Update the widget every hour till the event
if (diff.hours == 0) {
var minutes = 0
when (Preferences.widgetUpdateFrequency) {
Constants.WidgetUpdateFrequency.DEFAULT.value -> {
minutes = when {
diff.minutes > 50 -> 50
diff.minutes > 30 -> 30
diff.minutes > 15 -> 15
else -> 0
}
}
Constants.WidgetUpdateFrequency.HIGH.value -> {
minutes = diff.minutes - (diff.minutes % 5)
}
}
setExact(
AlarmManager.RTC,
if (event.startDate - minutes * 1000 * 60 > (now.timeInMillis + 120 * 1000)) event.startDate - 60 * 1000 * minutes else now.timeInMillis + 120000,
PendingIntent.getBroadcast(
context,
event.eventID.toInt(),
Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE
putExtra(EVENT_ID, event.eventID)
},
PendingIntent.FLAG_UPDATE_CURRENT
)
)
} else {
setExact(
AlarmManager.RTC,
event.startDate - diff.hours * 1000 * 60 * 60 + if (diff.minutes > 30) (-30) else (+30),
PendingIntent.getBroadcast(
context,
event.eventID.toInt(),
Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE
putExtra(EVENT_ID, event.eventID)
},
PendingIntent.FLAG_UPDATE_CURRENT
)
)
}
} else {
// Update the widget one second after the event is finished
val fireTime =
if (event.endDate > now.timeInMillis + 120 * 1000) event.endDate else now.timeInMillis + 120000
setExact(
AlarmManager.RTC,
fireTime,
PendingIntent.getBroadcast(
context,
event.eventID.toInt(),
Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE
},
0
)
)
}
} else {
setExact(
AlarmManager.RTC, AlarmManager.RTC,
if (event.startDate - diff.hours * 1000 * 60 * 60 > (now.timeInMillis + 120 * 1000)) event.startDate - diff.hours * 1000 * 60 * 60 else now.timeInMillis + 120000, if (event.startDate - limit > now.timeInMillis + 120 * 1000) event.startDate - limit else now.timeInMillis + 120000,
PendingIntent.getBroadcast( PendingIntent.getBroadcast(
context, context,
event.eventID.toInt(), event.eventID.toInt(),
@ -78,23 +157,7 @@ class UpdatesReceiver : BroadcastReceiver() {
action = Actions.ACTION_TIME_UPDATE action = Actions.ACTION_TIME_UPDATE
putExtra(EVENT_ID, event.eventID) putExtra(EVENT_ID, event.eventID)
}, },
0 PendingIntent.FLAG_UPDATE_CURRENT
)
)
} else {
// Update the widget one second after the event is finished
val fireTime =
if (event.endDate > now.timeInMillis + 120 * 1000) event.endDate else now.timeInMillis + 120000
setExactAndAllowWhileIdle(
AlarmManager.RTC,
fireTime,
PendingIntent.getBroadcast(
context,
event.eventID.toInt(),
Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE
},
0
) )
) )
} }
@ -103,9 +166,11 @@ 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) {
EventRepository(context).getEvents().forEach { val eventRepository = EventRepository(context)
eventRepository.getFutureEvents().forEach {
cancel(PendingIntent.getBroadcast(context, it.eventID.toInt(), Intent(context, UpdatesReceiver::class.java), 0)) cancel(PendingIntent.getBroadcast(context, it.eventID.toInt(), Intent(context, UpdatesReceiver::class.java), 0))
} }
eventRepository.close()
} }
} }
} }

View File

@ -0,0 +1,61 @@
package com.tommasoberlose.anotherwidget.services
import android.app.job.JobInfo
import android.app.job.JobParameters
import android.app.job.JobScheduler
import android.app.job.JobService
import android.content.ComponentName
import android.content.Context
import android.os.Build
import android.provider.CalendarContract
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
class BatteryListenerJob : JobService() {
override fun onStartJob(params: JobParameters): Boolean {
MainWidget.updateWidget(this)
schedule(
this
)
return false
}
@Synchronized
override fun onStopJob(params: JobParameters): Boolean {
return false
}
companion object {
private const val chargingJobId = 1006
private const val notChargingJobId = 1007
fun schedule(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
remove(context)
val componentName = ComponentName(
context,
EventListenerJob::class.java
)
with(context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler) {
schedule(
JobInfo.Builder(chargingJobId, componentName)
.setRequiresCharging(true)
.setPersisted(true)
.build()
)
schedule(
JobInfo.Builder(notChargingJobId, componentName)
.setRequiresCharging(false)
.setPersisted(true)
.build()
)
}
}
}
fun remove(context: Context) {
val js = context.getSystemService(JobScheduler::class.java)
js?.cancel(chargingJobId)
js?.cancel(notChargingJobId)
}
}
}

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

@ -26,6 +26,7 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.Navigation import androidx.navigation.Navigation
import com.chibatching.kotpref.Kotpref
import com.google.android.material.badge.BadgeDrawable import com.google.android.material.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.Dexter
@ -75,7 +76,9 @@ class MainActivity : AppCompatActivity() {
viewModel = ViewModelProvider(this).get(MainViewModel::class.java) viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
controlExtras(intent) controlExtras(intent)
requirePermission() if (Preferences.showWallpaper) {
requirePermission()
}
} }
override fun onBackPressed() { override fun onBackPressed() {

View File

@ -6,6 +6,7 @@ import android.location.Address
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -45,8 +46,17 @@ class SupportDevActivity : AppCompatActivity(), PurchasesUpdatedListener {
adapter = SlimAdapter.create() adapter = SlimAdapter.create()
adapter adapter
.register<SkuDetails>(R.layout.inapp_product_layout) { item, injector -> .register<SkuDetails>(R.layout.inapp_product_layout) { item, injector ->
item.sku
injector injector
.text(R.id.product_title, item.title.replace("(Another Widget)", "")) .with<TextView>(R.id.product_title) {
it.text = when (item.sku) {
"donation_coffee" -> getString(R.string.donation_coffee)
"donation_donuts" -> getString(R.string.donation_donuts)
"donation_breakfast" -> getString(R.string.donation_breakfast)
"donation_lunch" -> getString(R.string.donation_lunch)
else -> ""
}
}
.text(R.id.product_price, item.price) .text(R.id.product_price, item.price)
.clicked(R.id.item) { .clicked(R.id.item) {
viewModel.purchase(this, item) viewModel.purchase(this, item)

View File

@ -32,11 +32,6 @@ class WeatherProviderActivity : AppCompatActivity() {
openURI("https://home.openweathermap.org/users/sign_up") openURI("https://home.openweathermap.org/users/sign_up")
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
last_info.text = Html.fromHtml(getString(R.string.api_key_info_all_set), Html.FROM_HTML_MODE_LEGACY)
} else {
last_info.text = Html.fromHtml(getString(R.string.api_key_info_all_set))
}
api_key.editText?.setText(Preferences.weatherProviderApi) api_key.editText?.setText(Preferences.weatherProviderApi)
} }
} }

View File

@ -81,6 +81,8 @@ class CalendarTabFragment : 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
@ -97,7 +99,7 @@ class CalendarTabFragment : Fragment() {
viewModel.calendarAllDay.observe(viewLifecycleOwner, Observer { viewModel.calendarAllDay.observe(viewLifecycleOwner, Observer {
maintainScrollPosition { maintainScrollPosition {
all_day_label?.text = all_day_label?.text =
if (it) getString(R.string.settings_all_day_subtitle_visible) else getString(R.string.settings_all_day_subtitle_gone) if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
} }
checkReadEventsPermission() checkReadEventsPermission()
}) })
@ -121,6 +123,17 @@ class CalendarTabFragment : 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 CalendarTabFragment : 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)
} }
}) })
@ -205,7 +215,7 @@ class CalendarTabFragment : Fragment() {
} }
dialog.addItem( dialog.addItem(
if (calendarSelectorList[index].name == calendarSelectorList[index].accountName) getString(R.string.account_events) else calendarSelectorList[index].name, if (calendarSelectorList[index].name == calendarSelectorList[index].accountName) getString(R.string.main_calendar) else calendarSelectorList[index].name,
calendarSelectorList[index].id calendarSelectorList[index].id
) )
} }
@ -222,8 +232,8 @@ class CalendarTabFragment : Fragment() {
action_show_all_day.setOnClickListener { action_show_all_day.setOnClickListener {
if (Preferences.showEvents) { if (Preferences.showEvents) {
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_all_day_title)).setSelectedValue(Preferences.calendarAllDay) BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_all_day_title)).setSelectedValue(Preferences.calendarAllDay)
.addItem(getString(R.string.settings_all_day_subtitle_visible), true) .addItem(getString(R.string.settings_visible), true)
.addItem(getString(R.string.settings_all_day_subtitle_gone), false) .addItem(getString(R.string.settings_not_visible), false)
.addOnSelectItemListener { value -> .addOnSelectItemListener { value ->
Preferences.calendarAllDay = value Preferences.calendarAllDay = value
}.show() }.show()
@ -263,6 +273,18 @@ class CalendarTabFragment : 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)
@ -278,7 +300,7 @@ class CalendarTabFragment : 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).forEach { intArrayOf(6,7,0,1,2,3, 4, 5).forEach {
dialog.addItem(getString(SettingsStringHelper.getShowUntilString(it)), it) dialog.addItem(getString(SettingsStringHelper.getShowUntilString(it)), it)
} }
dialog.addOnSelectItemListener { value -> dialog.addOnSelectItemListener { value ->
@ -287,27 +309,6 @@ class CalendarTabFragment : Fragment() {
} }
} }
action_date_format.setOnClickListener {
if (Preferences.showEvents) {
val now = Calendar.getInstance()
val dialog = BottomSheetMenu<String>(requireContext(), header = getString(R.string.settings_date_format_title)).setSelectedValue(Preferences.dateFormat)
dialog.addItem(DateHelper.getDefaultDateText(requireContext(), now), "")
if (Preferences.dateFormat != "") {
dialog.addItem(DateHelper.getDateText(requireContext(), now), Preferences.dateFormat)
}
dialog.addItem(getString(R.string.custom_date_format), "-")
dialog.addOnSelectItemListener { value ->
if (value == "-") {
startActivity(Intent(requireContext(), CustomDateActivity::class.java))
} else {
Preferences.dateFormat = value
}
}.show()
}
}
action_open_event_details.setOnClickListener { 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)

View File

@ -85,6 +85,8 @@ class ClockTabFragment : Fragment() {
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
@ -104,6 +106,12 @@ class ClockTabFragment : Fragment() {
} }
}) })
viewModel.showAMPMIndicator.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
ampm_indicator_label?.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
}
})
viewModel.clockTextColor.observe(viewLifecycleOwner, Observer { viewModel.clockTextColor.observe(viewLifecycleOwner, Observer {
maintainScrollPosition { maintainScrollPosition {
if (Preferences.clockTextAlpha == "00") { if (Preferences.clockTextAlpha == "00") {
@ -168,6 +176,15 @@ class ClockTabFragment : 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 { action_clock_text_color.setOnClickListener {
BottomSheetColorPicker(requireContext(), BottomSheetColorPicker(requireContext(),
colors = colors, colors = colors,

View File

@ -21,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.*
@ -29,6 +31,7 @@ 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 GeneralTabFragment : Fragment() { class GeneralTabFragment : Fragment() {
@ -113,6 +116,28 @@ class GeneralTabFragment : 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") {
@ -141,6 +166,12 @@ class GeneralTabFragment : 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))
@ -148,7 +179,10 @@ class GeneralTabFragment : Fragment() {
}) })
viewModel.showDividers.observe(viewLifecycleOwner, Observer { viewModel.showDividers.observe(viewLifecycleOwner, Observer {
show_dividers_label?.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible) maintainScrollPosition {
show_dividers_label?.text =
if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
}
}) })
} }
@ -164,7 +198,7 @@ class GeneralTabFragment : 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 ->
@ -174,7 +208,7 @@ class GeneralTabFragment : 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 ->
@ -199,6 +233,42 @@ class GeneralTabFragment : 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 {
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,
@ -249,7 +319,7 @@ class GeneralTabFragment : Fragment() {
} }
action_show_dividers.setOnClickListener { action_show_dividers.setOnClickListener {
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_show_multiple_events_title)).setSelectedValue(Preferences.showDividers) BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_show_dividers_title)).setSelectedValue(Preferences.showDividers)
.addItem(getString(R.string.settings_visible), true) .addItem(getString(R.string.settings_visible), true)
.addItem(getString(R.string.settings_not_visible), false) .addItem(getString(R.string.settings_not_visible), false)
.addOnSelectItemListener { value -> .addOnSelectItemListener { value ->

View File

@ -1,44 +1,55 @@
package com.tommasoberlose.anotherwidget.ui.fragments package com.tommasoberlose.anotherwidget.ui.fragments
import android.Manifest
import android.app.Activity
import android.app.AlarmManager import android.app.AlarmManager
import android.content.BroadcastReceiver 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.PackageManager import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
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
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ItemTouchHelper import com.google.android.gms.auth.api.signin.GoogleSignIn
import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import androidx.recyclerview.widget.RecyclerView import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import androidx.recyclerview.widget.RecyclerView.ViewHolder 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.R
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu 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.databinding.FragmentGlanceSettingsBinding
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.AlarmHelper import com.tommasoberlose.anotherwidget.helpers.AlarmHelper
import com.tommasoberlose.anotherwidget.helpers.GlanceProviderHelper
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
import com.tommasoberlose.anotherwidget.models.GlanceProvider import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver
import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver.Companion.FITNESS_OPTIONS
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.checkIfFitInstalled
import com.tommasoberlose.anotherwidget.utils.toast
import kotlinx.android.synthetic.main.fragment_calendar_settings.*
import kotlinx.android.synthetic.main.fragment_glance_settings.* import kotlinx.android.synthetic.main.fragment_glance_settings.*
import kotlinx.android.synthetic.main.fragment_glance_settings.scrollView
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.idik.lib.slimadapter.SlimAdapter
import java.util.*
class GlanceTabFragment : Fragment() { class GlanceTabFragment : Fragment() {
@ -48,7 +59,6 @@ class GlanceTabFragment : Fragment() {
} }
private lateinit var viewModel: MainViewModel private lateinit var viewModel: MainViewModel
private lateinit var adapter: SlimAdapter
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -73,64 +83,7 @@ class GlanceTabFragment : Fragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
list.setHasFixedSize(true) action_show_steps.isVisible = requireContext().checkIfFitInstalled()
val mLayoutManager = LinearLayoutManager(requireContext())
list.layoutManager = mLayoutManager
adapter = SlimAdapter.create()
adapter
.register<String>(R.layout.glance_provider_item) { item, injector ->
injector
.text(R.id.title, item)
}
.register<GlanceProvider>(R.layout.glance_provider_item) { item, injector ->
injector
.text(R.id.title, item.title)
.with<ImageView>(R.id.icon) {
it.setImageDrawable(ContextCompat.getDrawable(requireContext(), item.icon))
}
.with<TextView>(R.id.label) {
it.isVisible = item.label != ""
it.text = item.label
}
}
.attachTo(list)
adapter.updateData(
GlanceProviderHelper.getGlanceProviders()
.mapNotNull { GlanceProviderHelper.getGlanceProviderById(requireContext(), it) }
)
val mIth = ItemTouchHelper(
object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
ItemTouchHelper.LEFT
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: ViewHolder, target: ViewHolder
): Boolean {
val fromPos = viewHolder.adapterPosition
val toPos = target.adapterPosition
// move item in `fromPos` to `toPos` in adapter.
adapter.notifyItemMoved(fromPos, toPos)
val list = GlanceProviderHelper.getGlanceProviders()
Collections.swap(list, fromPos, toPos)
GlanceProviderHelper.saveGlanceProviderOrder(list)
return true
}
override fun onSwiped(
viewHolder: ViewHolder,
direction: Int
) {
// remove from adapter
}
})
mIth.attachToRecyclerView(list)
setupListener() setupListener()
updateNextAlarmWarningUi() updateNextAlarmWarningUi()
@ -140,17 +93,44 @@ class GlanceTabFragment : Fragment() {
binding: FragmentGlanceSettingsBinding, binding: FragmentGlanceSettingsBinding,
viewModel: MainViewModel viewModel: MainViewModel
) { ) {
binding.isGlanceVisible = Preferences.showGlance
viewModel.showGlance.observe(viewLifecycleOwner, Observer { viewModel.showGlance.observe(viewLifecycleOwner, Observer {
binding.isGlanceVisible = it maintainScrollPosition {
binding.isGlanceVisible = it
show_glance_label.text = if (it) getString(R.string.description_show_glance_visible) else getString(R.string.description_show_glance_not_visible)
}
}) })
viewModel.showMusic.observe(viewLifecycleOwner, Observer { viewModel.showMusic.observe(viewLifecycleOwner, Observer {
checkNotificationPermission() maintainScrollPosition {
checkNotificationPermission()
}
}) })
viewModel.showNextAlarm.observe(viewLifecycleOwner, Observer { viewModel.showNextAlarm.observe(viewLifecycleOwner, Observer {
updateNextAlarmWarningUi() maintainScrollPosition {
updateNextAlarmWarningUi()
}
})
viewModel.showBatteryCharging.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
show_low_battery_level_warning_label?.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
}
})
viewModel.showDailySteps.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
show_steps_label?.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
}
checkFitnessPermission()
})
viewModel.customInfo.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
show_custom_notes_label?.text = if (it == "") getString(R.string.settings_not_visible) else it
}
}) })
} }
@ -165,6 +145,11 @@ class GlanceTabFragment : Fragment() {
Preferences.showGlance = enabled Preferences.showGlance = enabled
} }
action_sort_glance_providers.setOnClickListener {
GlanceProviderSortMenu(requireContext())
.show()
}
action_show_music.setOnClickListener { action_show_music.setOnClickListener {
if (Preferences.showGlance) { if (Preferences.showGlance) {
BottomSheetMenu<Boolean>( BottomSheetMenu<Boolean>(
@ -192,6 +177,66 @@ class GlanceTabFragment : Fragment() {
}.show() }.show()
} }
} }
action_show_next_alarm.setOnLongClickListener {
with(requireContext().getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
val alarm = nextAlarmClock
if (alarm != null && alarm.showIntent != null) {
val pm = requireContext().packageManager as PackageManager
val appNameOrPackage = try {
pm.getApplicationLabel(pm.getApplicationInfo(alarm.showIntent?.creatorPackage ?: "", 0))
} catch (e: Exception) {
alarm.showIntent?.creatorPackage ?: ""
}
activity?.toast(getString(R.string.next_alarm_warning).format(appNameOrPackage), long = true)
}
}
true
}
action_show_low_battery_level_warning.setOnClickListener {
if (Preferences.showGlance) {
BottomSheetMenu<Boolean>(
requireContext(),
header = getString(R.string.settings_low_battery_level_title)
).setSelectedValue(Preferences.showBatteryCharging)
.addItem(getString(R.string.settings_visible), true)
.addItem(getString(R.string.settings_not_visible), false)
.addOnSelectItemListener { value ->
Preferences.showBatteryCharging = value
}.show()
}
}
action_show_steps.setOnClickListener {
if (Preferences.showGlance) {
BottomSheetMenu<Boolean>(
requireContext(),
header = getString(R.string.settings_daily_steps_title)
).setSelectedValue(Preferences.showDailySteps)
.addItem(getString(R.string.settings_visible), true)
.addItem(getString(R.string.settings_not_visible), false)
.addOnSelectItemListener { value ->
if (value) {
val account: GoogleSignInAccount? = GoogleSignIn.getLastSignedInAccount(requireContext())
if (!GoogleSignIn.hasPermissions(account, FITNESS_OPTIONS)) {
val mGoogleSignInClient = GoogleSignIn.getClient(requireActivity(), GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).addExtension(FITNESS_OPTIONS).build())
startActivityForResult(mGoogleSignInClient.signInIntent, 2)
} else {
Preferences.showDailySteps = true
}
} else {
Preferences.showDailySteps = false
}
}.show()
}
}
action_show_custom_notes.setOnClickListener {
if (Preferences.showGlance) {
CustomNotesDialog(requireContext()).show()
}
}
} }
private fun updateNextAlarmWarningUi() { private fun updateNextAlarmWarningUi() {
@ -204,15 +249,10 @@ class GlanceTabFragment : Fragment() {
} catch (e: Exception) { } catch (e: Exception) {
alarm.showIntent?.creatorPackage ?: "" alarm.showIntent?.creatorPackage ?: ""
} }
maintainScrollPosition { show_next_alarm_warning.text = getString(R.string.next_alarm_warning).format(appNameOrPackage)
show_next_alarm_warning.text =
getString(R.string.next_alarm_warning).format(appNameOrPackage)
}
} else { } else {
maintainScrollPosition { show_next_alarm_label?.text = if (Preferences.showNextAlarm) getString(R.string.settings_visible) else getString(
show_next_alarm_label?.text = if (Preferences.showNextAlarm) getString(R.string.settings_visible) else getString(
R.string.settings_not_visible) R.string.settings_not_visible)
}
} }
} }
} }
@ -237,7 +277,7 @@ class GlanceTabFragment : Fragment() {
if (NotificationManagerCompat.getEnabledListenerPackages(requireContext()).contains(requireContext().packageName)) { if (NotificationManagerCompat.getEnabledListenerPackages(requireContext()).contains(requireContext().packageName)) {
notification_permission_alert?.isVisible = false notification_permission_alert?.isVisible = false
MediaPlayerHelper.updatePlayingMediaInfo(requireContext()) MediaPlayerHelper.updatePlayingMediaInfo(requireContext())
show_music_label?.text = if (Preferences.showMusic) getString(R.string.settings_show_music_enabled_subtitle) else getString(R.string.settings_show_music_disabled_subtitle) show_music_label?.text = if (Preferences.showMusic) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
} else if (Preferences.showMusic) { } else if (Preferences.showMusic) {
notification_permission_alert?.isVisible = true notification_permission_alert?.isVisible = true
show_music_label?.text = getString(R.string.settings_request_notification_access) show_music_label?.text = getString(R.string.settings_request_notification_access)
@ -245,11 +285,93 @@ class GlanceTabFragment : Fragment() {
activity?.startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS")) activity?.startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"))
} }
} else { } else {
show_music_label?.text = getString(R.string.settings_show_music_disabled_subtitle) show_music_label?.text = getString(R.string.settings_not_visible)
notification_permission_alert?.isVisible = false 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) { private fun maintainScrollPosition(callback: () -> Unit) {
val scrollPosition = scrollView.scrollY val scrollPosition = scrollView.scrollY
callback.invoke() callback.invoke()

View File

@ -14,6 +14,7 @@ import android.util.TypedValue
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.RelativeLayout import android.widget.RelativeLayout
import androidx.core.animation.addListener import androidx.core.animation.addListener
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
@ -35,6 +36,7 @@ import com.tommasoberlose.anotherwidget.helpers.BitmapHelper
import com.tommasoberlose.anotherwidget.helpers.ColorHelper import com.tommasoberlose.anotherwidget.helpers.ColorHelper
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.activities.SupportDevActivity
import com.tommasoberlose.anotherwidget.ui.adapters.ViewPagerAdapter import com.tommasoberlose.anotherwidget.ui.adapters.ViewPagerAdapter
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
@ -125,8 +127,11 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
private fun updateUI() { private fun updateUI() {
uiJob?.cancel() uiJob?.cancel()
preview?.clearAnimation()
time_container?.clearAnimation()
if (Preferences.showPreview) { if (Preferences.showPreview) {
preview.setCardBackgroundColor( preview?.setCardBackgroundColor(
ContextCompat.getColor( ContextCompat.getColor(
requireContext(), requireContext(),
if (ColorHelper.getFontColor() if (ColorHelper.getFontColor()
@ -134,51 +139,62 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
) android.R.color.white else R.color.colorAccent ) android.R.color.white else R.color.colorAccent
) )
) )
widget_shape_background.setImageDrawable(BitmapHelper.getTintedDrawable(requireContext(), R.drawable.card_background, ColorHelper.getBackgroundColor())) widget_shape_background?.setImageDrawable(
uiJob = viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { BitmapHelper.getTintedDrawable(
requireContext(),
R.drawable.card_background,
ColorHelper.getBackgroundColor()
)
)
uiJob = lifecycleScope.launch(Dispatchers.IO) {
val generatedView = MainWidget.generateWidgetView(requireContext()) val generatedView = MainWidget.generateWidgetView(requireContext())
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
generatedView.measure(0, 0) generatedView.measure(0, 0)
preview.measure(0, 0) preview?.measure(0, 0)
} }
val bitmap = BitmapHelper.getBitmapFromView( val bitmap = if (preview != null) {
generatedView, BitmapHelper.getBitmapFromView(
if (preview.width > 0) preview.width else generatedView.measuredWidth, generatedView,
generatedView.measuredHeight if (preview.width > 0) preview.width else generatedView.measuredWidth,
) generatedView.measuredHeight
)
} else {
null
}
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
// Clock // Clock
time.setTextColor(ColorHelper.getClockFontColor()) time?.setTextColor(ColorHelper.getClockFontColor())
time_am_pm.setTextColor(ColorHelper.getClockFontColor()) time_am_pm?.setTextColor(ColorHelper.getClockFontColor())
time.setTextSize( time?.setTextSize(
TypedValue.COMPLEX_UNIT_SP, TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(requireContext()) Preferences.clockTextSize.toPixel(requireContext())
) )
time_am_pm.setTextSize( time_am_pm?.setTextSize(
TypedValue.COMPLEX_UNIT_SP, TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(requireContext()) / 5 * 2 Preferences.clockTextSize.toPixel(requireContext()) / 5 * 2
) )
time_am_pm?.isVisible = Preferences.showAMPMIndicator
// Clock bottom margin // Clock bottom margin
clock_bottom_margin_none.isVisible = clock_bottom_margin_none?.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.NONE.value Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.NONE.value
clock_bottom_margin_small.isVisible = clock_bottom_margin_small?.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.SMALL.value Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.SMALL.value
clock_bottom_margin_medium.isVisible = clock_bottom_margin_medium?.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.MEDIUM.value Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.MEDIUM.value
clock_bottom_margin_large.isVisible = clock_bottom_margin_large?.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.LARGE.value Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.LARGE.value
if ((Preferences.showClock && !time_container.isVisible) || (!Preferences.showClock && time_container.isVisible)) { if ((Preferences.showClock && (time?.alpha ?: 1f < 1f)) || (!Preferences.showClock && (time?.alpha ?: 0f > 0f))) {
if (Preferences.showClock) { if (Preferences.showClock) {
time_container.layoutParams = time_container.layoutParams.apply { time_container?.layoutParams = time_container.layoutParams.apply {
height = RelativeLayout.LayoutParams.WRAP_CONTENT height = RelativeLayout.LayoutParams.WRAP_CONTENT
} }
time_container.measure(0, 0) time_container?.measure(0, 0)
} }
val initialHeight = time_container.measuredHeight val initialHeight = time_container?.measuredHeight ?: 0
ValueAnimator.ofFloat( ValueAnimator.ofFloat(
if (Preferences.showClock) 0f else 1f, if (Preferences.showClock) 0f else 1f,
if (Preferences.showClock) 1f else 0f if (Preferences.showClock) 1f else 0f
@ -186,46 +202,52 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
duration = 500L duration = 500L
addUpdateListener { addUpdateListener {
val animatedValue = animatedValue as Float val animatedValue = animatedValue as Float
time_container.layoutParams = time_container.layoutParams.apply { time_container?.layoutParams =
height = (initialHeight * animatedValue).toInt() time_container.layoutParams.apply {
} height = (initialHeight * animatedValue).toInt()
}
time?.alpha = animatedValue
} }
addListener( addListener(
onStart = { onStart = {
if (Preferences.showClock) { if (Preferences.showClock) {
time_container.isVisible = true time_container?.isVisible = true
} }
}, },
onEnd = { onEnd = {
if (!Preferences.showClock) { if (!Preferences.showClock) {
time_container.isVisible = false time_container?.isVisible = false
} }
} }
) )
}.start() }.start()
ValueAnimator.ofInt( if (preview != null) {
preview.height, ValueAnimator.ofInt(
PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel( preview.height,
requireContext() PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel(
) else 0 requireContext()
).apply { ) else 0
duration = 500L ).apply {
addUpdateListener { duration = 500L
val animatedValue = animatedValue as Int addUpdateListener {
val layoutParams = preview.layoutParams if (preview != null) {
layoutParams.height = animatedValue val animatedValue = animatedValue as Int
preview.layoutParams = layoutParams val layoutParams = preview.layoutParams
} layoutParams.height = animatedValue
}.start() preview.layoutParams = layoutParams
}
}
}.start()
}
} else { } else {
time_container.layoutParams = time_container.layoutParams.apply { time_container?.layoutParams = time_container.layoutParams.apply {
height = RelativeLayout.LayoutParams.WRAP_CONTENT height = RelativeLayout.LayoutParams.WRAP_CONTENT
} }
time_container.measure(0, 0) time_container?.measure(0, 0)
} }
if (preview.height == 0) { if (preview != null && preview.height == 0) {
ValueAnimator.ofInt( ValueAnimator.ofInt(
preview.height, preview.height,
PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel( PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel(
@ -234,32 +256,42 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
).apply { ).apply {
duration = 300L duration = 300L
addUpdateListener { addUpdateListener {
val animatedValue = animatedValue as Int if (preview != null) {
val layoutParams = preview.layoutParams val animatedValue = animatedValue as Int
layoutParams.height = animatedValue val layoutParams = preview.layoutParams
preview.layoutParams = layoutParams layoutParams.height = animatedValue
preview?.layoutParams = layoutParams
}
} }
}.start() }.start()
} }
widget_loader.animate().scaleX(0f).scaleY(0f).alpha(0f).setDuration(200L).start() widget_loader?.animate()?.scaleX(0f)?.scaleY(0f)?.alpha(0f)?.setDuration(200L)?.start()
bitmap_container.setImageBitmap(bitmap) bitmap_container?.apply {
widget.animate().alpha(1f).start() setImageBitmap(bitmap)
scaleX = 0.9f
scaleY = 0.9f
}
widget?.animate()?.alpha(1f)?.start()
} }
} }
} else { } else {
ValueAnimator.ofInt( if (preview != null) {
preview.height, ValueAnimator.ofInt(
0 preview.height,
).apply { 0
duration = 300L ).apply {
addUpdateListener { duration = 300L
val animatedValue = animatedValue as Int addUpdateListener {
val layoutParams = preview.layoutParams if (preview != null) {
layoutParams.height = animatedValue val animatedValue = animatedValue as Int
preview.layoutParams = layoutParams val layoutParams = preview.layoutParams
} layoutParams.height = animatedValue
}.start() preview.layoutParams = layoutParams
}
}
}.start()
}
} }
showErrorBadge() showErrorBadge()
@ -271,13 +303,27 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
activity?.let { act -> activity?.let { act ->
val wallpaper = act.getCurrentWallpaper() val wallpaper = act.getCurrentWallpaper()
widget_bg.setImageDrawable(if (it) wallpaper else null) widget_bg.setImageDrawable(if (it) wallpaper else null)
widget_bg.layoutParams = widget_bg.layoutParams.apply { if (wallpaper != null) {
widget_bg.layoutParams =
(widget_bg.layoutParams as ViewGroup.MarginLayoutParams).apply {
val metrics = DisplayMetrics() val metrics = DisplayMetrics()
act.windowManager.defaultDisplay.getMetrics(metrics) act.windowManager.defaultDisplay.getMetrics(metrics)
height = metrics.heightPixels val dimensions: Pair<Int, Int> = if (wallpaper.intrinsicWidth >= wallpaper.intrinsicHeight) {
width = (wallpaper?.intrinsicWidth ?: 1) * metrics.heightPixels / (wallpaper?.intrinsicWidth ?: 1) 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
}
} }
} }
}) })
@ -312,11 +358,6 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
}?.isVisible = Preferences.showMusic && !NotificationManagerCompat.getEnabledListenerPackages(requireContext()).contains(requireContext().packageName) }?.isVisible = Preferences.showMusic && !NotificationManagerCompat.getEnabledListenerPackages(requireContext()).contains(requireContext().packageName)
} }
override fun onSharedPreferenceChanged(preferences: SharedPreferences, p1: String) {
updateUI()
MainWidget.updateWidget(requireContext())
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
Preferences.preferences.registerOnSharedPreferenceChangeListener(this) Preferences.preferences.registerOnSharedPreferenceChangeListener(this)
@ -331,10 +372,29 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
super.onPause() 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 class UpdateUiMessageEvent
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(ignore: UpdateUiMessageEvent?) { fun onMessageEvent(ignore: UpdateUiMessageEvent?) {
updateUI() delayJob?.cancel()
delayJob = lifecycleScope.launch(Dispatchers.IO) {
delay(200)
withContext(Dispatchers.Main) {
updateUI()
}
}
} }
} }

View File

@ -4,6 +4,7 @@ import android.Manifest
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup

View File

@ -5,10 +5,12 @@ 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
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.os.BuildCompat
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
@ -27,7 +29,9 @@ import com.tommasoberlose.anotherwidget.databinding.FragmentWeatherSettingsBindi
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.ColorHelper
import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
import com.tommasoberlose.anotherwidget.receivers.WeatherReceiver import com.tommasoberlose.anotherwidget.receivers.WeatherReceiver
import com.tommasoberlose.anotherwidget.ui.activities.ChooseApplicationActivity import com.tommasoberlose.anotherwidget.ui.activities.ChooseApplicationActivity
import com.tommasoberlose.anotherwidget.ui.activities.CustomLocationActivity import com.tommasoberlose.anotherwidget.ui.activities.CustomLocationActivity
@ -79,6 +83,8 @@ class WeatherTabFragment : 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() checkLocationPermission()
@ -124,6 +130,19 @@ class WeatherTabFragment : Fragment() {
checkLocationPermission() checkLocationPermission()
}) })
viewModel.weatherIconPack.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
label_weather_icon_pack?.text = getString(R.string.settings_weather_icon_pack_default).format((it + 1))
weather_icon_pack.setImageDrawable(ContextCompat.getDrawable(requireContext(), WeatherHelper.getWeatherIconResource("01d")))
if (it == Constants.WeatherIconPack.MINIMAL.value) {
weather_icon_pack.setColorFilter(ContextCompat.getColor(requireContext(), R.color.colorPrimaryText))
} else {
weather_icon_pack.setColorFilter(ContextCompat.getColor(requireContext(), android.R.color.transparent))
}
}
checkLocationPermission()
})
viewModel.weatherAppName.observe(viewLifecycleOwner, Observer { viewModel.weatherAppName.observe(viewLifecycleOwner, Observer {
maintainScrollPosition { maintainScrollPosition {
weather_app_label?.text = weather_app_label?.text =
@ -215,6 +234,18 @@ class WeatherTabFragment : Fragment() {
} }
} }
action_weather_icon_pack.setOnClickListener {
if (Preferences.showWeather) {
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_weather_icon_pack_title)).setSelectedValue(Preferences.weatherIconPack)
Constants.WeatherIconPack.values().forEach {
dialog.addItem(getString(R.string.settings_weather_icon_pack_default).format(it.value + 1), it.value)
}
dialog.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

@ -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)
@ -27,12 +29,14 @@ 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 clockTextColor = Preferences.asLiveData(Preferences::clockTextColor)
val clockTextAlpha = Preferences.asLiveData(Preferences::clockTextAlpha) 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 dateFormat = Preferences.asLiveData(Preferences::dateFormat) val dateFormat = Preferences.asLiveData(Preferences::dateFormat)
@ -51,11 +55,15 @@ 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 // Glance
val showGlance = Preferences.asLiveData(Preferences::showGlance) val showGlance = Preferences.asLiveData(Preferences::showGlance)
val showMusic = Preferences.asLiveData(Preferences::showMusic) val showMusic = Preferences.asLiveData(Preferences::showMusic)
val showNextAlarm = Preferences.asLiveData(Preferences::showNextAlarm) 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)

View File

@ -35,6 +35,8 @@ 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
import kotlin.math.roundToInt
class MainWidget : AppWidgetProvider() { class MainWidget : AppWidgetProvider() {
@ -85,9 +87,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) {
@ -109,7 +112,7 @@ class MainWidget : AppWidgetProvider() {
context, context,
appWidgetId, appWidgetId,
IntentHelper.getWidgetUpdateIntent(context), IntentHelper.getWidgetUpdateIntent(context),
0 PendingIntent.FLAG_UPDATE_CURRENT
) )
views.setOnClickPendingIntent(R.id.widget_shape_background, refreshIntent) views.setOnClickPendingIntent(R.id.widget_shape_background, refreshIntent)
} catch (ex: Exception) { } catch (ex: Exception) {
@ -138,9 +141,8 @@ class MainWidget : AppWidgetProvider() {
} }
private fun updateCalendarView(context: Context, v: View, views: RemoteViews, widgetID: Int): RemoteViews { private fun updateCalendarView(context: Context, v: View, views: RemoteViews, widgetID: Int): RemoteViews {
val eventRepository = EventRepository(context)
try { try {
val eventRepository = EventRepository(context)
views.setImageViewBitmap( views.setImageViewBitmap(
R.id.empty_date_rect, R.id.empty_date_rect,
BitmapHelper.getBitmapFromView(v.empty_date, draw = false) BitmapHelper.getBitmapFromView(v.empty_date, draw = false)
@ -155,7 +157,7 @@ class MainWidget : AppWidgetProvider() {
context, context,
widgetID, widgetID,
IntentHelper.getCalendarIntent(context), IntentHelper.getCalendarIntent(context),
0 PendingIntent.FLAG_UPDATE_CURRENT
) )
views.setOnClickPendingIntent(R.id.empty_date_rect, calPIntent) views.setOnClickPendingIntent(R.id.empty_date_rect, calPIntent)
@ -178,7 +180,7 @@ class MainWidget : AppWidgetProvider() {
context, context,
NewCalendarEventReceiver::class.java NewCalendarEventReceiver::class.java
).apply { action = Actions.ACTION_GO_TO_NEXT_EVENT }, ).apply { action = Actions.ACTION_GO_TO_NEXT_EVENT },
0 PendingIntent.FLAG_UPDATE_CURRENT
) )
) )
@ -196,7 +198,7 @@ class MainWidget : AppWidgetProvider() {
context, context,
NewCalendarEventReceiver::class.java NewCalendarEventReceiver::class.java
).apply { action = Actions.ACTION_GO_TO_PREVIOUS_EVENT }, ).apply { action = Actions.ACTION_GO_TO_PREVIOUS_EVENT },
0 PendingIntent.FLAG_UPDATE_CURRENT
) )
) )
} else { } else {
@ -208,7 +210,7 @@ class MainWidget : AppWidgetProvider() {
context, context,
widgetID, widgetID,
IntentHelper.getEventIntent(context, nextEvent), IntentHelper.getEventIntent(context, nextEvent),
0 PendingIntent.FLAG_UPDATE_CURRENT
) )
views.setOnClickPendingIntent(R.id.next_event_rect, pIntent) views.setOnClickPendingIntent(R.id.next_event_rect, pIntent)
views.setOnClickPendingIntent(R.id.next_event_difference_time_rect, pIntent) views.setOnClickPendingIntent(R.id.next_event_difference_time_rect, pIntent)
@ -231,7 +233,7 @@ class MainWidget : AppWidgetProvider() {
context, context,
widgetID, widgetID,
IntentHelper.getGoogleMapsIntentFromAddress(context, nextEvent.address), IntentHelper.getGoogleMapsIntentFromAddress(context, nextEvent.address),
0 PendingIntent.FLAG_UPDATE_CURRENT
) )
views.setOnClickPendingIntent(R.id.second_row_rect, mapIntent) views.setOnClickPendingIntent(R.id.second_row_rect, mapIntent)
} else { } else {
@ -243,7 +245,7 @@ class MainWidget : AppWidgetProvider() {
nextEvent, nextEvent,
forceEventDetails = true forceEventDetails = true
), ),
0 PendingIntent.FLAG_UPDATE_CURRENT
) )
views.setOnClickPendingIntent(R.id.second_row_rect, pIntentDetail) views.setOnClickPendingIntent(R.id.second_row_rect, pIntentDetail)
} }
@ -261,14 +263,67 @@ 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 (MediaPlayerHelper.isSomeonePlaying(context)) { } else if (GlanceProviderHelper.showGlanceProviders(context) && v.calendar_layout.isVisible) {
val musicIntent = PendingIntent.getActivity( loop@ for (provider:Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(context)) {
context, when (provider) {
widgetID, Constants.GlanceProviderId.PLAYING_SONG -> {
IntentHelper.getMusicIntent(context), if (MediaPlayerHelper.isSomeonePlaying(context)) {
0 val musicIntent = PendingIntent.getActivity(
) context,
views.setOnClickPendingIntent(R.id.second_row_rect, musicIntent) widgetID,
IntentHelper.getMusicIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
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),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.second_row_rect, alarmIntent)
break@loop
}
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
if (Preferences.showBatteryCharging) {
BatteryHelper.updateBatteryInfo(context)
if (Preferences.isCharging || Preferences.isBatteryLevelLow) {
val batteryIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getBatteryIntent(),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.second_row_rect, batteryIntent)
break@loop
}
}
}
Constants.GlanceProviderId.CUSTOM_INFO -> {
if (Preferences.customNotes.isNotEmpty()) {
break@loop
}
}
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
if (Preferences.showDailySteps && Preferences.googleFitSteps > 0) {
val fitIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getFitIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.second_row_rect, fitIntent)
break@loop
}
}
}
}
views.setImageViewBitmap( views.setImageViewBitmap(
R.id.next_event_rect, R.id.next_event_rect,
@ -279,31 +334,8 @@ 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.calendar_layout_rect, View.VISIBLE)
views.setOnClickPendingIntent(R.id.next_event_rect, calPIntent)
} else if (Preferences.showNextAlarm && nextAlarm != "") {
val alarmIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getClockIntent(context),
0
)
views.setOnClickPendingIntent(R.id.second_row_rect, alarmIntent)
views.setImageViewBitmap(
R.id.next_event_rect,
BitmapHelper.getBitmapFromView(v.next_event, draw = false)
)
views.setImageViewBitmap(
R.id.second_row_rect,
BitmapHelper.getBitmapFromView(v.second_row, draw = false)
)
views.setViewVisibility(R.id.second_row_rect, View.VISIBLE)
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) views.setOnClickPendingIntent(R.id.next_event_rect, calPIntent)
@ -311,6 +343,8 @@ class MainWidget : AppWidgetProvider() {
} catch (ex: Exception) { } catch (ex: Exception) {
ex.printStackTrace() ex.printStackTrace()
CrashlyticsReceiver.sendCrash(context, ex) CrashlyticsReceiver.sendCrash(context, ex)
} finally {
eventRepository.close()
} }
return views return views
@ -346,7 +380,7 @@ class MainWidget : AppWidgetProvider() {
BitmapHelper.getBitmapFromView(v.calendar_weather, draw = false) BitmapHelper.getBitmapFromView(v.calendar_weather, draw = false)
) )
if (WidgetHelper.showSpecialWeather(context)) { if (GlanceProviderHelper.showGlanceProviders(context)) {
views.setViewVisibility(R.id.calendar_weather_rect, View.GONE) views.setViewVisibility(R.id.calendar_weather_rect, View.GONE)
} else { } else {
views.setViewVisibility(R.id.special_weather_rect, View.GONE) views.setViewVisibility(R.id.special_weather_rect, View.GONE)
@ -394,7 +428,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,
@ -427,7 +461,10 @@ 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
@ -447,7 +484,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,
@ -469,8 +506,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)
@ -493,46 +530,121 @@ 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, nextEvent.startDate, 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 (MediaPlayerHelper.isSomeonePlaying(context)) { } else if (GlanceProviderHelper.showGlanceProviders(context)) {
v.second_row_icon.setImageDrawable( v.second_row_icon.isVisible = true
ContextCompat.getDrawable( var showSomething = false
context, loop@ for (provider:Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(context)) {
R.drawable.round_music_note when (provider) {
) Constants.GlanceProviderId.PLAYING_SONG -> {
) if (MediaPlayerHelper.isSomeonePlaying(context)) {
v.next_event.text = DateHelper.getDateText(context, now) v.second_row_icon.setImageDrawable(
v.next_event_date.text = MediaPlayerHelper.getMediaInfo() ContextCompat.getDrawable(
v.empty_layout.visibility = View.GONE context,
v.calendar_layout.visibility = View.VISIBLE R.drawable.round_music_note
} else if (Preferences.showNextAlarm && nextAlarm != "") { )
v.second_row_icon.setImageDrawable( )
ContextCompat.getDrawable( v.next_event_date.text = MediaPlayerHelper.getMediaInfo()
context, showSomething = true
R.drawable.round_alarm break@loop
) }
) }
v.next_event.text = DateHelper.getDateText(context, now) Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
v.next_event_date.text = AlarmHelper.getNextAlarm(context) if (Preferences.showNextAlarm && nextAlarm != "") {
v.empty_layout.visibility = View.GONE v.second_row_icon.setImageDrawable(
v.calendar_layout.visibility = View.VISIBLE ContextCompat.getDrawable(
context,
R.drawable.round_alarm
)
)
v.next_event_date.text = AlarmHelper.getNextAlarm(context)
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
if (Preferences.showBatteryCharging) {
BatteryHelper.updateBatteryInfo(context)
if (Preferences.isCharging) {
v.second_row_icon.isVisible = false
val batteryLevel = BatteryHelper.getBatteryLevel(context)
if (batteryLevel == 100) {
v.next_event_date.text = "%s - %d%%".format(context.getString(R.string.charging), batteryLevel)
} else {
v.next_event_date.text = context.getString(R.string.charging)
}
showSomething = true
break@loop
} else if (Preferences.isBatteryLevelLow) {
v.second_row_icon.isVisible = false
v.next_event_date.text =
context.getString(R.string.battery_low_warning)
showSomething = true
break@loop
}
}
}
Constants.GlanceProviderId.CUSTOM_INFO -> {
if (Preferences.customNotes.isNotEmpty()) {
v.second_row_icon.isVisible = false
v.next_event_date.text = Preferences.customNotes
v.next_event_date.gravity
v.next_event_date.maxLines = 2
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
if (Preferences.showDailySteps && Preferences.googleFitSteps > 0) {
v.second_row_icon.isVisible = false
v.next_event_date.text = context.getString(R.string.daily_steps_counter).format(Preferences.googleFitSteps)
showSomething = true
break@loop
}
}
}
}
if (showSomething) {
v.next_event.text = DateHelper.getDateText(context, now)
v.empty_layout.visibility = View.GONE
v.calendar_layout.visibility = View.VISIBLE
} else {
v.second_row_icon.isVisible = false
}
} }
// 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, v.divider3, v.special_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 || Preferences.weatherIconPack == Constants.WeatherIconPack.COOL.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 || Preferences.weatherIconPack == Constants.WeatherIconPack.COOL.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,
@ -553,11 +665,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
@ -606,7 +718,7 @@ class MainWidget : AppWidgetProvider() {
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 v.special_weather.visibility = View.VISIBLE
val currentTemp = String.format(Locale.getDefault(), "%.0f °%s", Preferences.weatherTemp, Preferences.weatherRealTempUnit) val currentTemp = String.format(Locale.getDefault(), "%d °%s", Preferences.weatherTemp.roundToInt(), Preferences.weatherRealTempUnit)
val icon: String = Preferences.weatherIcon val icon: String = Preferences.weatherIcon
if (icon == "") { if (icon == "") {
@ -626,7 +738,7 @@ class MainWidget : AppWidgetProvider() {
v.calendar_temp.text = currentTemp v.calendar_temp.text = currentTemp
v.special_temp.text = currentTemp v.special_temp.text = currentTemp
if (WidgetHelper.showSpecialWeather(context)) { if (GlanceProviderHelper.showGlanceProviders(context)) {
v.calendar_weather.visibility = View.GONE v.calendar_weather.visibility = View.GONE
} else { } else {
v.special_weather.visibility = View.GONE v.special_weather.visibility = View.GONE
@ -642,6 +754,8 @@ class MainWidget : AppWidgetProvider() {
it.isVisible = Preferences.showDividers it.isVisible = Preferences.showDividers
} }
eventRepository.close()
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: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 494 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 900 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 617 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 947 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

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