Compare commits

...

22 Commits

Author SHA1 Message Date
26428b65da Merge crud-device 2020-05-09 17:58:03 +02:00
97d1caeabc Add dividers toggle 2020-05-09 15:11:12 +02:00
f013be5a7a Add integreations activity, removed long time until intervals 2020-05-08 20:16:48 +02:00
4cc55edb15 Merge music 2020-05-08 18:57:14 +02:00
d08ad6171e Change music fragment to at a glance 2020-05-08 18:55:08 +02:00
9e2eacef73 Update build version 2020-05-08 17:43:47 +02:00
f8f8a8f051 Merge calendar fix 2020-05-08 15:15:49 +02:00
60531061d7 Fix #83 2020-05-08 15:14:01 +02:00
654ec3fe66 Add current song 2020-05-08 15:10:19 +02:00
d86d2cadd4 Add clock text color 2020-05-08 11:55:42 +02:00
cb6c2c7764 Move app settings to a different fragment 2020-05-08 01:39:31 +02:00
6a4cfdf22b Update bitmap generator 2020-05-07 18:30:28 +02:00
0b9e9e081e Merge branch 'master' of github.com:tommasoberlose/another-widget 2020-05-07 16:19:53 +02:00
15884bd9b2 Fix helper 2020-05-07 16:19:30 +02:00
0adf192965 Fix #72 2020-05-07 15:51:09 +02:00
d72ba08960 Update issue templates 2020-05-07 14:14:26 +02:00
1848951a74 Update issue templates 2020-05-07 14:13:14 +02:00
5ca06e817e update wallpaper ui, update color picker ui, update intents and add custom logger for the widget 2020-05-07 12:34:48 +02:00
37cb43dc80 Add media player receiver 2020-05-06 21:40:51 +02:00
ea372dd76d Remove workers. Fix #71 2020-05-06 21:25:23 +02:00
7076311e94 Add workers 2020-05-06 20:58:32 +02:00
2e684e0902 Fix #74, fix #69 2020-05-06 16:16:31 +02:00
659 changed files with 3547 additions and 1229 deletions

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

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

View File

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

3
.gitignore vendored
View File

@ -7,4 +7,5 @@
/build
/captures
.externalNativeBuild
/tasksintegration/build
/tasksintegration/build
apikey.properties

Binary file not shown.

1
.idea/gradle.xml generated
View File

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

1
.idea/modules.xml generated
View File

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

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

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

View File

@ -18,8 +18,8 @@ android {
applicationId "com.tommasoberlose.anotherwidget"
minSdkVersion 23
targetSdkVersion 29
versionCode 69
versionName "2.0.5"
versionCode 81
versionName "2.0.6"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@ -53,7 +53,9 @@ android {
viewBinding.enabled = true
dynamicFeatures = [":tasksintegration"]
dynamicFeatures = [":tasksintegration", ":googlefit"]
}
dependencies {
@ -110,9 +112,6 @@ dependencies {
implementation "androidx.palette:palette-ktx:1.0.0"
implementation 'androidx.core:core-ktx:1.2.0'
// Recommended: Add the Firebase SDK for Google Analytics.
implementation 'com.google.firebase:firebase-analytics:17.4.0'
// Add the Firebase SDK for Crashlytics.
implementation 'com.google.firebase:firebase-crashlytics:17.0.0'

Binary file not shown.

View File

@ -13,6 +13,7 @@
<application
android:allowBackup="true"
android:fullBackupContent="@xml/my_backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:name=".AWApplication"
@ -32,6 +33,7 @@
<activity android:name=".ui.activities.WeatherProviderActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.SupportDevActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.CustomDateActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.IntegrationsActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<receiver android:name=".ui.widgets.MainWidget">
@ -90,6 +92,34 @@
</intent-filter>
</receiver>
<receiver
android:name=".receivers.PlayerReceiver"
android:enabled="true"
android:exported="true"
tools:ignore="ExportedReceiver">
<intent-filter>
<action android:name="com.android.music.metachanged" />
<action android:name="com.android.music.playstatechanged" />
<action android:name="com.android.music.playbackcomplete" />
<action android:name="com.android.music.queuechanged" />
<action android:name="com.htc.music.metachanged" />
<action android:name="fm.last.android.metachanged" />
<action android:name="com.sec.android.app.music.metachanged" />
<action android:name="com.nullsoft.winamp.metachanged" />
<action android:name="com.amazon.mp3.metachanged" />
<action android:name="com.miui.player.metachanged" />
<action android:name="com.real.IMP.metachanged" />
<action android:name="com.sonyericsson.music.metachanged" />
<action android:name="com.rdio.android.metachanged" />
<action android:name="com.samsung.sec.android.MusicPlayer.metachanged" />
<action android:name="com.andrew.apollo.metachanged" />
<action android:name="com.spotify.music.playbackstatechanged"/>
<action android:name="com.spotify.music.metadatachanged"/>
<action android:name="com.spotify.music.queuechanged"/>
</intent-filter>
</receiver>
<receiver
android:name=".receivers.WidgetClickListenerReceiver"
android:enabled="true"
@ -99,8 +129,23 @@
</intent-filter>
</receiver>
<receiver
android:name=".receivers.CrashlyticsReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="com.tommasoberlose.anotherwidget.action.ACTION_REPORT_CRASH" />
</intent-filter>
</receiver>
<service android:name=".services.EventListenerJob" android:permission="android.permission.BIND_JOB_SERVICE" />
<service android:name=".receivers.MusicNotificationListener"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
</application>
</manifest>

View File

@ -8,18 +8,24 @@ import android.text.TextWatcher
import android.util.Log
import android.view.View
import android.widget.GridLayout
import android.widget.ImageView
import android.widget.SeekBar
import androidx.annotation.ColorInt
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.GridLayoutManager
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.helpers.ColorHelper
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark
import com.tommasoberlose.anotherwidget.utils.expand
import com.tommasoberlose.anotherwidget.utils.reveal
import com.tommasoberlose.anotherwidget.utils.toPixel
import com.warkiz.widget.IndicatorSeekBar
import com.warkiz.widget.OnSeekChangeListener
import com.warkiz.widget.SeekParams
@ -28,13 +34,14 @@ import kotlinx.android.synthetic.main.bottom_sheet_menu_hor.view.*
import kotlinx.android.synthetic.main.bottom_sheet_menu_hor.view.color_loader
import kotlinx.android.synthetic.main.color_picker_menu_item.view.*
import kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter
import java.lang.Exception
import java.util.prefs.Preferences
class BottomSheetColorPicker(
context: Context,
private val colors: IntArray = intArrayOf(),
private val selected: Int? = null,
private val getSelected: (() -> Int)? = null,
private val header: String? = null,
private val onColorSelected: ((selectedValue: Int) -> Unit)? = null,
private val showAlphaSelector: Boolean = false,
@ -42,15 +49,19 @@ class BottomSheetColorPicker(
private val onAlphaChangeListener: ((alpha: Int) -> Unit)? = null
) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
private val loadingJob: Job? = null
private var loadingJobs: ArrayList<Job> = ArrayList()
private lateinit var adapter: SlimAdapter
override fun show() {
val view = View.inflate(context, R.layout.bottom_sheet_menu_hor, null)
window?.setDimAmount(0f)
// Header
view.header.isVisible = header != null
view.header_text.text = header ?: ""
// Alpha
view.alpha_selector_container.isVisible = showAlphaSelector
view.alpha_selector.setProgress(alpha.toFloat())
view.text_alpha.text = "%s: %s%%".format(context.getString(R.string.alpha), alpha)
@ -67,44 +78,62 @@ class BottomSheetColorPicker(
}
}
val itemViews: ArrayList<View> = ArrayList()
// List
GlobalScope.launch(Dispatchers.IO) {
for (@ColorInt color: Int in colors) {
val itemView = View.inflate(context, R.layout.color_picker_menu_item, null)
itemView.color.setCardBackgroundColor(ColorStateList.valueOf(color))
itemView.check.setColorFilter(ContextCompat.getColor(context,
if (color.isColorDark()) android.R.color.white else android.R.color.black
), android.graphics.PorterDuff.Mode.MULTIPLY)
itemView.check.isVisible = selected == color
itemView.color.setOnClickListener {
onColorSelected?.invoke(color)
this@BottomSheetColorPicker.dismiss()
adapter = SlimAdapter.create()
loadingJobs.add(GlobalScope.launch(Dispatchers.IO) {
val listView = View.inflate(context, R.layout.bottom_sheet_menu_list, null) as RecyclerView
listView.setHasFixedSize(true)
val mLayoutManager = GridLayoutManager(context, 6)
listView.layoutManager = mLayoutManager
adapter
.register<Int>(R.layout.color_picker_menu_item) { item, injector ->
injector
.with<MaterialCardView>(R.id.color) {
it.setCardBackgroundColor(ColorStateList.valueOf(item))
}
.with<AppCompatImageView>(R.id.check) {
if (getSelected?.invoke() == item) {
it.setColorFilter(
ContextCompat.getColor(
context,
if (item.isColorDark()) android.R.color.white else android.R.color.black
),
android.graphics.PorterDuff.Mode.MULTIPLY
)
it.isVisible = true
} else {
it.isVisible = false
}
}
.clicked(R.id.color) {
adapter.notifyItemChanged(adapter.data.indexOf(getSelected?.invoke()))
onColorSelected?.invoke(item)
val position = adapter.data.indexOf(item)
adapter.notifyItemChanged(position)
(listView.layoutManager as GridLayoutManager).scrollToPositionWithOffset(position,0)
}
}
itemViews.add(itemView)
}
.attachTo(listView)
adapter.updateData(colors.toList())
withContext(Dispatchers.Main) {
itemViews.forEach {
view.menu.addView(it, GridLayout.LayoutParams(
GridLayout.spec(GridLayout.UNDEFINED, 1f),
GridLayout.spec(GridLayout.UNDEFINED, 1f)
))
}
color_loader.isVisible = false
view.menu.isVisible = true
view.color_loader.isVisible = false
view.list_container.addView(listView)
this@BottomSheetColorPicker.behavior.state = BottomSheetBehavior.STATE_EXPANDED
view.list_container.isVisible = true
}
}
// Menu
})
setContentView(view)
super.show()
}
override fun onStop() {
loadingJob?.cancel()
loadingJobs.forEach { it.cancel() }
super.onStop()
}

View File

@ -1,17 +1,11 @@
package com.tommasoberlose.anotherwidget.components
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import androidx.annotation.MenuRes
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.card.MaterialCardView
import com.tommasoberlose.anotherwidget.R
import kotlinx.android.synthetic.main.bottom_sheet_menu.view.*
import kotlinx.android.synthetic.main.bottom_sheet_menu_item.view.*
@ -21,7 +15,7 @@ import kotlinx.android.synthetic.main.bottom_sheet_menu_item.view.*
* theme which sets a rounded background to the dialog
* and doesn't dim the navigation bar
*/
open class BottomSheetMenu<T>(context: Context, private val header: String? = null, private val isMultiSelection: Boolean = false) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
open class BottomSheetMenu<T>(context: Context, private val header: String? = null, private val message: String? = null, private val isMessageWarning: Boolean = false, private val isMultiSelection: Boolean = false) : BottomSheetDialog(context, R.style.BottomSheetDialogTheme) {
private val items: ArrayList<MenuItem<T>> = ArrayList()
private var selectedRes: ArrayList<T> = ArrayList()
@ -60,6 +54,10 @@ open class BottomSheetMenu<T>(context: Context, private val header: String? = nu
view.header.isVisible = header != null
view.header_text.text = header ?: ""
view.warning_text.isVisible = message != null
view.warning_text.text = message ?: ""
view.warning_text.setTextColor(ContextCompat.getColor(context, if (isMessageWarning) R.color.warningColorText else R.color.colorSecondaryText))
// Menu
for (item in items) {
if (item.value != null) {

View File

@ -1,6 +1,7 @@
package com.tommasoberlose.anotherwidget.db
import android.content.Context
import android.util.Log
import com.chibatching.kotpref.bulk
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.models.Event
@ -8,6 +9,8 @@ import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import io.realm.Realm
import io.realm.RealmResults
import java.util.*
import kotlin.collections.ArrayList
class EventRepository(val context: Context) {
private val realm by lazy { Realm.getDefaultInstance() }
@ -39,13 +42,27 @@ class EventRepository(val context: Context) {
Preferences.nextEventId = event.id
}
fun getNextEvent(): Event? = realm.where(Event::class.java).equalTo("id", Preferences.nextEventId).findFirst() ?: realm.where(Event::class.java).findFirst()
fun getNextEvent(): Event? {
val nextEvent = getEventByEventId(Preferences.nextEventId)
return if (nextEvent != null && nextEvent.endDate > Calendar.getInstance().timeInMillis) {
nextEvent
} else {
val events = getEvents()
if (events.isNotEmpty()) {
val newNextEvent = events.first()
Preferences.nextEventId = newNextEvent!!.eventID
newNextEvent
} else {
resetNextEventData()
null
}
}
}
fun getEventById(id: Long): Event? = realm.where(Event::class.java).equalTo("id", id).findFirst()
fun getEventByEventId(id: Long): Event? = realm.where(Event::class.java).equalTo("eventID", id).findFirst()
fun goToNextEvent() {
val eventList = realm.where(Event::class.java).findAll()
val eventList = getEvents()
if (eventList.isNotEmpty()) {
val index = eventList.indexOfFirst { it.id == Preferences.nextEventId }
if (index > -1 && index < eventList.size - 1) {
@ -61,8 +78,7 @@ class EventRepository(val context: Context) {
}
fun goToPreviousEvent() {
val eventList = realm.where(Event::class.java).findAll()
val eventList = getEvents()
if (eventList.isNotEmpty()) {
val index = eventList.indexOfFirst { it.id == Preferences.nextEventId }
if (index > 0) {
@ -77,7 +93,14 @@ class EventRepository(val context: Context) {
MainWidget.updateWidget(context)
}
fun getEvents(): RealmResults<Event> = realm.where(Event::class.java).findAll()
fun getEvents(): RealmResults<Event> {
val now = Calendar.getInstance().timeInMillis
val list = realm.where(Event::class.java).greaterThan("endDate", now).findAll()
realm.executeTransactionAsync {
it.where(Event::class.java).lessThanOrEqualTo("endDate", now).findAll().deleteAllFromRealm()
}
return list
}
fun getEventsCount(): Int = realm.where(Event::class.java).findAll().size
fun getEventsCount(): Int = getEvents().size
}

View File

@ -4,10 +4,11 @@ object Actions {
const val ACTION_EXTRA_OPEN_WEATHER_PROVIDER = "ACTION_EXTRA_OPEN_WEATHER_PROVIDER"
const val ACTION_EXTRA_DISABLE_GPS_NOTIFICATION = "ACTION_EXTRA_DISABLE_GPS_NOTIFICATION"
const val ACTION_TIME_UPDATE = "com.tommasoberlose.anotherwidget.action.ACTION_TIME_UPDATE"
const val ACTION_CALENDAR_UPDATE = "com.tommasoberlose.anotherwidget.action.ACTION_CALENDAR_UPDATE"
const val ACTION_WEATHER_UPDATE = "com.tommasoberlose.anotherwidget.action.ACTION_WEATHER_UPDATE"
const val ACTION_OPEN_WEATHER_INTENT = "com.tommasoberlose.anotherwidget.action.ACTION_OPEN_WEATHER_INTENT"
const val ACTION_TIME_UPDATE = "com.tommasoberlose.anotherwidget.action.TIME_UPDATE"
const val ACTION_CALENDAR_UPDATE = "com.tommasoberlose.anotherwidget.action.CALENDAR_UPDATE"
const val ACTION_WEATHER_UPDATE = "com.tommasoberlose.anotherwidget.action.WEATHER_UPDATE"
const val ACTION_OPEN_WEATHER_INTENT = "com.tommasoberlose.anotherwidget.action.OPEN_WEATHER_INTENT"
const val ACTION_GO_TO_NEXT_EVENT = "com.tommasoberlose.anotherwidget.action.GO_TO_NEXT_EVENT"
const val ACTION_GO_TO_PREVIOUS_EVENT = "com.tommasoberlose.anotherwidget.action.GO_TO_PREVIOUS_EVENT"
const val ACTION_REPORT_CRASH = "com.tommasoberlose.anotherwidget.action.REPORT_CRASH"
}

View File

@ -14,4 +14,12 @@ object Constants {
MEDIUM(2),
LARGE(3)
}
enum class GlanceProviderId(val id: String) {
PLAYING_SONG("PLAYING_SONG"),
NEXT_CLOCK_ALARM("NEXT_CLOCK_ALARM"),
// BATTERY_LEVEL_LOW("BATTERY_LEVEL_LOW"),
// CUSTOM_INFO("CUSTOM_INFO"),
// GOOGLE_FIT_STEPS("GOOGLE_FIT_STEPS")
}
}

View File

@ -7,8 +7,9 @@ import com.chibatching.kotpref.KotprefModel
object Preferences : KotprefModel() {
override val commitAllPropertiesByDefault: Boolean = true
var darkThemePreference by intPref(default = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) MODE_NIGHT_FOLLOW_SYSTEM else MODE_NIGHT_AUTO_BATTERY)
var darkThemePreference by intPref(default = MODE_NIGHT_FOLLOW_SYSTEM)
// Calendar and weather
var showEvents by booleanPref(key = "PREF_SHOW_EVENTS", default = false)
var showWeather by booleanPref(key = "PREF_SHOW_WEATHER", default = false)
var weatherIcon by stringPref(key = "PREF_WEATHER_ICON", default = "")
@ -45,6 +46,10 @@ object Preferences : KotprefModel() {
var backgroundCardColor by stringPref(default = "#000000")
var backgroundCardAlpha by stringPref(default = "00")
var clockTextColor by stringPref(default = "#FFFFFF")
var clockTextAlpha by stringPref(default = "FF")
// Global
var textMainSize by floatPref(key = "PREF_TEXT_MAIN_SIZE", default = 26f)
var textSecondSize by floatPref(key = "PREF_TEXT_SECOND_SIZE", default = 18f)
var clockTextSize by floatPref(key = "PREF_TEXT_CLOCK_SIZE", default = 90f)
@ -52,7 +57,6 @@ object Preferences : KotprefModel() {
var showClock by booleanPref(key = "PREF_SHOW_CLOCK", default = false)
var clockAppName by stringPref(key = "PREF_CLOCK_APP_NAME", default = "")
var clockAppPackage by stringPref(key = "PREF_CLOCK_APP_PACKAGE", default = "")
var showNextAlarm by booleanPref(default = false)
var textShadow by intPref(key = "PREF_TEXT_SHADOW", default = 1)
var showDiffTime by booleanPref(key = "PREF_SHOW_DIFF_TIME", default = true)
var showDeclinedEvents by booleanPref(key = "PREF_SHOW_DECLINED_EVENTS", default = false)
@ -61,9 +65,30 @@ object Preferences : KotprefModel() {
var customFontFile by stringPref(key = "PREF_CUSTOM_FONT_FILE")
var showNextEvent by booleanPref(key = "PREF_SHOW_NEXT_EVENT", default = true)
var showDividers by booleanPref(default = true)
// Settings
var showWallpaper by booleanPref(default = true)
var showBigClockWarning by booleanPref(default = true)
var showWeatherWarning by booleanPref(default = true)
var showPreview by booleanPref(default = true)
var showXiaomiWarning by booleanPref(default = true)
// Glance
var showGlance by booleanPref(default = true)
var enabledGlanceProviderOrder by stringPref(default = "")
var customInfo by stringPref(default = "")
var showNextAlarm by booleanPref(default = false)
var isBatteryLevelLow by booleanPref(default = false)
var googleFitSteps by longPref(default = -1)
var showMusic by booleanPref(default = false)
var mediaInfoFormat by stringPref(default = "")
var mediaPlayerTitle by stringPref(default = "")
var mediaPlayerAlbum by stringPref(default = "")
var mediaPlayerArtist by stringPref(default = "")
var mediaPlayerPackage by stringPref(default = "")
// Integrations
var installedIntegrations by intPref(default = 0)
}

View File

@ -23,4 +23,14 @@ object AlarmHelper {
""
}
}
fun isAlarmProbablyWrong(context: Context): Boolean {
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
val alarm = nextAlarmClock
return (
alarm != null
&& alarm.triggerTime - Calendar.getInstance().timeInMillis < 5 * 60 * 1000
)
}
}
}

View File

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

View File

@ -4,13 +4,12 @@ import android.Manifest
import android.content.ContentUris
import android.content.Context
import android.provider.CalendarContract
import android.util.Log
import com.tommasoberlose.anotherwidget.services.EventListenerJob
import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.models.Event
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import me.everything.providers.android.calendar.CalendarProvider
@ -125,10 +124,9 @@ object CalendarHelper {
}
UpdatesReceiver.setUpdates(context)
Log.d("ciao", "force update? 7")
MainWidget.updateWidget(context)
EventBus.getDefault().post(MainActivity.UpdateUiMessageEvent())
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
}
fun getCalendarList(context: Context): List<me.everything.providers.android.calendar.Calendar> {

View File

@ -15,6 +15,45 @@ object ColorHelper {
}
}
fun getFontColorAlpha(): Int {
return try {
Preferences.textGlobalAlpha.toIntValue().toDouble() * 255 / 100
} catch (e: Exception) {
"FF".toIntValue().toDouble() * 255 / 100
}.roundToInt()
}
fun getFontColorRgb(): Int {
return try {
Color.parseColor(Preferences.textGlobalColor)
} catch (e: Exception) {
Color.parseColor("#000000")
}
}
fun getClockFontColor(): Int {
return try {
Color.parseColor("#%s%s".format(Preferences.clockTextAlpha, Preferences.clockTextColor.replace("#", "")))
} catch (e: Exception) {
Color.parseColor("#FFFFFFFF")
}
}
fun getClockFontColorAlpha(): Int {
return try {
Preferences.clockTextAlpha.toIntValue().toDouble() * 255 / 100
} catch (e: Exception) {
"FF".toIntValue().toDouble() * 255 / 100
}.roundToInt()
}
fun getClockFontColorRgb(): Int {
return try {
Color.parseColor(Preferences.clockTextColor)
} catch (e: Exception) {
Color.parseColor("#000000")
}
}
fun getBackgroundColor(): Int {
return try {
Color.parseColor("#%s%s".format(Preferences.backgroundCardAlpha, Preferences.backgroundCardColor.replace("#", "")))

View File

@ -0,0 +1,76 @@
package com.tommasoberlose.anotherwidget.helpers
import android.content.Context
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.models.GlanceProvider
import java.util.ArrayList
object GlanceProviderHelper {
fun getGlanceProviders(): ArrayList<Constants.GlanceProviderId> {
val enabledProviders = Preferences.enabledGlanceProviderOrder.split(",").filter { it != "" }
val providers = Constants.GlanceProviderId.values()
providers.sortWith(Comparator { p1, p2 ->
when {
enabledProviders.contains(p1.id) && enabledProviders.contains(p2.id) -> {
enabledProviders.indexOf(p1.id).compareTo(enabledProviders.indexOf(p2.id))
}
enabledProviders.contains(p1.id) -> {
-1
}
enabledProviders.contains(p2.id) -> {
1
}
else -> {
p1.id.compareTo(p2.id)
}
}
})
return ArrayList(providers.toList())
}
fun getGlanceProviderById(context: Context, providerId: Constants.GlanceProviderId): GlanceProvider? {
return when(providerId) {
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_show_next_alarm_title),
R.drawable.round_alarm,
context.getString(R.string.settings_show_next_alarm_subtitle)
)
}
Constants.GlanceProviderId.PLAYING_SONG -> {
GlanceProvider(providerId.id,
context.getString(R.string.settings_show_music_title),
R.drawable.round_music_note,
context.getString(R.string.settings_show_music_enabled_subtitle)
)
}
// Constants.GlanceProviderId.CUSTOM_INFO -> {
// GlanceProvider(providerId.id,
// context.getString(R.string.settings_show_next_alarm_title),
// R.drawable.round_event_note
// )
// }
// Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
// GlanceProvider(providerId.id,
// context.getString(R.string.settings_show_next_alarm_title),
// R.drawable.round_battery_charging_full
// )
// }
// Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
// GlanceProvider(providerId.id,
// context.getString(R.string.settings_show_next_alarm_title),
// R.drawable.round_directions_walk
// )
// }
else -> null
}
}
fun saveGlanceProviderOrder(list: ArrayList<Constants.GlanceProviderId>) {
Preferences.enabledGlanceProviderOrder = list.joinToString(separator = ",")
}
}

View File

@ -1,5 +1,6 @@
package com.tommasoberlose.anotherwidget.helpers
import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.ContentUris
import android.content.Context
@ -12,11 +13,23 @@ import android.provider.CalendarContract.Events
import android.util.Log
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.models.Event
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import java.util.*
object IntentHelper {
fun getGoogleMapsIntentFromAddress(context: Context, address:String): Intent {
fun getWidgetUpdateIntent(context: Context): Intent {
val widgetManager = AppWidgetManager.getInstance(context)
val widgetComponent = ComponentName(context, MainWidget::class.java)
val widgetIds = widgetManager.getAppWidgetIds(widgetComponent)
return Intent(context, MainWidget::class.java).apply {
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIds)
action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
}
}
fun getGoogleMapsIntentFromAddress(context: Context, address: String): Intent {
val gmmIntentUri: Uri = Uri.parse("geo:0,0?q=$address")
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
mapIntent.`package` = "com.google.android.apps.maps"
@ -63,11 +76,15 @@ object IntentHelper {
}
fun getCalendarIntent(context: Context): Intent {
val calendarUri = CalendarContract.CONTENT_URI
.buildUpon()
.appendPath("time")
.appendPath(Calendar.getInstance().timeInMillis.toString())
.build()
return when (Preferences.calendarAppPackage) {
"" -> {
Intent(Intent.ACTION_MAIN).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
addCategory(Intent.CATEGORY_APP_CALENDAR)
Intent(Intent.ACTION_VIEW).apply {
data = calendarUri
}
}
"_" -> {
@ -77,14 +94,13 @@ object IntentHelper {
val pm: PackageManager = context.packageManager
try {
pm.getLaunchIntentForPackage(Preferences.calendarAppPackage)!!.apply {
addCategory(Intent.CATEGORY_LAUNCHER)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
action = Intent.ACTION_VIEW
data = calendarUri
}
} catch (e: Exception) {
e.printStackTrace()
Intent(Intent.ACTION_MAIN).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
addCategory(Intent.CATEGORY_APP_CALENDAR)
Intent(Intent.ACTION_VIEW).apply {
data = calendarUri
}
}
}
@ -98,9 +114,10 @@ object IntentHelper {
if (Preferences.calendarAppPackage == "") {
Intent(Intent.ACTION_VIEW).apply {
data = uri
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate)
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate)
// putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, if (e.allDay) 1 else 0)
// type = "vnd.android.cursor.item/event"
}
} else {
getCalendarIntent(context).apply {
@ -108,6 +125,8 @@ object IntentHelper {
data = uri
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, e.startDate)
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, e.endDate)
// putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, if (e.allDay) 1 else 0)
// type = "vnd.android.cursor.item/event"
}
}
}
@ -141,4 +160,22 @@ object IntentHelper {
}
}
}
fun getMusicIntent(context: Context): Intent {
return when (Preferences.mediaPlayerPackage) {
"" -> {
Intent()
}
else -> {
val pm: PackageManager = context.packageManager
try {
pm.getLaunchIntentForPackage(Preferences.mediaPlayerPackage)!!.apply {
addCategory(Intent.CATEGORY_LAUNCHER)
}
} catch (e: Exception) {
Intent()
}
}
}
}
}

View File

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

View File

@ -39,7 +39,6 @@ object SettingsStringHelper {
return when (info) {
0 -> R.string.settings_second_row_info_subtitle_0
1 -> R.string.settings_second_row_info_subtitle_1
2 -> R.string.settings_second_row_info_subtitle_2
else -> R.string.settings_second_row_info_subtitle_0
}
}
@ -73,7 +72,7 @@ object SettingsStringHelper {
return ""
}
TimeUnit.MILLISECONDS.toHours(difference) < 12 -> {
return DateUtils.getRelativeTimeSpanString(start, now, DateUtils.HOUR_IN_MILLIS).toString()
return DateUtils.getRelativeTimeSpanString(start, now, DateUtils.HOUR_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString()
}
eventDate.dayOfYear == nowDate.plusDays(1).dayOfYear -> {
return String.format("%s", context.getString(R.string.tomorrow))
@ -82,7 +81,7 @@ object SettingsStringHelper {
return String.format("%s", context.getString(R.string.today))
}
else -> {
return DateUtils.getRelativeTimeSpanString(start, now, DateUtils.DAY_IN_MILLIS).toString()
return DateUtils.getRelativeTimeSpanString(start, now, DateUtils.DAY_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE).toString()
}
}

View File

@ -3,17 +3,15 @@ package com.tommasoberlose.anotherwidget.helpers
import android.Manifest
import android.content.Context
import android.os.Build
import android.util.Log
import com.google.android.gms.location.LocationServices
import com.kwabenaberko.openweathermaplib.constants.Units
import com.kwabenaberko.openweathermaplib.implementation.OpenWeatherMapHelper
import com.kwabenaberko.openweathermaplib.implementation.callbacks.CurrentWeatherCallback
import com.kwabenaberko.openweathermaplib.models.currentweather.CurrentWeather
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi
import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import org.greenrobot.eventbus.EventBus
/**
@ -35,6 +33,7 @@ object WeatherHelper {
Preferences.customLocationLon = location.longitude.toString()
networkApi.updateWeather()
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
}
}
}

View File

@ -3,6 +3,9 @@ package com.tommasoberlose.anotherwidget.helpers
import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.res.Configuration.ORIENTATION_PORTRAIT
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.global.Preferences
object WidgetHelper {
class WidgetSizeProvider(
@ -16,6 +19,8 @@ object WidgetHelper {
val height = getWidgetHeight(isPortrait, widgetId)
val widthInPx = context.dip(width)
val heightInPx = context.dip(height)
FirebaseCrashlytics.getInstance().setCustomKey("widthInPx", widthInPx)
FirebaseCrashlytics.getInstance().setCustomKey("heightInPx", heightInPx)
return widthInPx to heightInPx
}
@ -48,4 +53,11 @@ object WidgetHelper {
width to second * factor
}
}
fun showSpecialWeather(context: Context): Boolean {
return EventRepository(context).getEventsCount() == 0 && (
(Preferences.showNextAlarm && AlarmHelper.getNextAlarm(context) != "") ||
(MediaPlayerHelper.isSomeonePlaying(context))
)
}
}

View File

@ -16,6 +16,6 @@ open class Event(var id: Long = 0,
var allDay: Boolean = false,
var address: String = "") : RealmObject() {
override fun toString(): String {
return "Event:\nID: " + eventID + "\nTITLE: " + title + "\nSTART DATE: " + Date(startDate) + "\nEND DATE: " + Date(endDate) + "\nCAL ID: " + calendarID + "\nADDRESS: " + address
return "Event:\nEVENT ID: " + eventID + "\nTITLE: " + title + "\nSTART DATE: " + Date(startDate) + "\nEND DATE: " + Date(endDate) + "\nCAL ID: " + calendarID + "\nADDRESS: " + address
}
}

View File

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

View File

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

View File

@ -0,0 +1,32 @@
package com.tommasoberlose.anotherwidget.receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tommasoberlose.anotherwidget.global.Actions
import java.lang.Exception
class CrashlyticsReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Actions.ACTION_REPORT_CRASH) {
val exception: Exception = intent.getSerializableExtra(EXCEPTION) as Exception
FirebaseCrashlytics.getInstance().recordException(exception)
FirebaseCrashlytics.getInstance().sendUnsentReports()
}
}
companion object {
private const val EXCEPTION = "EXCEPTION"
fun sendCrash(context: Context, exception: Exception) {
context.sendBroadcast(Intent(context, CrashlyticsReceiver::class.java).apply {
action = Actions.ACTION_REPORT_CRASH
putExtra(EXCEPTION, exception)
})
}
}
}

View File

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

View File

@ -0,0 +1,22 @@
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

@ -11,6 +11,7 @@ import androidx.core.content.ContextCompat.getSystemService
import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.global.Actions
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper
import com.tommasoberlose.anotherwidget.models.Event
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import org.joda.time.Period
import java.util.*
@ -19,7 +20,6 @@ import java.util.*
class UpdatesReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("ciao", "che palle - ${intent.action}")
when (intent.action) {
Intent.ACTION_BOOT_COMPLETED,
Intent.ACTION_MY_PACKAGE_REPLACED,
@ -32,57 +32,80 @@ class UpdatesReceiver : BroadcastReceiver() {
Intent.ACTION_DATE_CHANGED,
AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED,
Actions.ACTION_TIME_UPDATE -> {
Log.d("ciao", "force update? 4 - ${intent.action}")
MainWidget.updateWidget(context)
if (intent.hasExtra(EVENT_ID)) {
setUpdates(context, intent.getLongExtra(EVENT_ID, -1))
}
}
}
}
companion object {
const val EVENT_ID = "EVENT_ID"
fun setUpdates(context: Context) {
removeUpdates(context)
fun setUpdates(context: Context, eventId: Long? = null) {
val eventRepository = EventRepository(context)
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
eventRepository.getEvents().forEach { event ->
val now = Calendar.getInstance().apply {
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
val diff = Period(now.timeInMillis, event.startDate)
if (event.startDate > now.timeInMillis) {
// Update the widget every hour till the event
(0..diff.hours).forEach {
AlarmManagerCompat.setExactAndAllowWhileIdle(
this,
AlarmManager.RTC_WAKEUP,
if (event.startDate - it * 1000 * 60 * 60 > 60 * 1000) event.startDate - it * 1000 * 60 * 60 else 120000,
PendingIntent.getBroadcast(
context,
0,
Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE
},
0
)
)
}
}
if (eventId == null) {
removeUpdates(context)
eventRepository.getEvents().forEach { event ->
setEventUpdate(context, event)
}
} else {
val event = eventRepository.getEventByEventId(eventId)
if (event != null) {
setEventUpdate(context, event)
}
}
}
private fun setEventUpdate(context: Context, event: Event) {
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
val now = Calendar.getInstance().apply {
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
val diff = Period(now.timeInMillis, event.startDate)
if (event.startDate > now.timeInMillis) {
// Update the widget every hour till the event
setExactAndAllowWhileIdle(
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,
PendingIntent.getBroadcast(
context,
event.eventID.toInt(),
Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE
putExtra(EVENT_ID, event.eventID)
},
0
)
)
} else {
// Update the widget one second after the event is finished
AlarmManagerCompat.setExactAndAllowWhileIdle(this,
AlarmManager.RTC_WAKEUP,
if (event.endDate > 60 *1000) event.endDate else 120000,
PendingIntent.getBroadcast(context, 0, Intent(context, UpdatesReceiver::class.java).apply { action = Actions.ACTION_TIME_UPDATE }, 0))
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
)
)
}
}
}
fun removeUpdates(context: Context) {
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
cancel(PendingIntent.getBroadcast(context, 0, Intent(context, UpdatesReceiver::class.java), 0))
EventRepository(context).getEvents().forEach {
cancel(PendingIntent.getBroadcast(context, it.eventID.toInt(), Intent(context, UpdatesReceiver::class.java), 0))
}
}
}
}

View File

@ -5,10 +5,10 @@ import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import com.tommasoberlose.anotherwidget.global.Actions
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
import com.tommasoberlose.anotherwidget.services.WeatherWorker
import java.util.*
@ -20,7 +20,61 @@ class WeatherReceiver : BroadcastReceiver() {
Intent.ACTION_MY_PACKAGE_REPLACED,
Intent.ACTION_TIMEZONE_CHANGED,
Intent.ACTION_LOCALE_CHANGED,
Intent.ACTION_TIME_CHANGED -> WeatherWorker.setUpdates(context)
Intent.ACTION_TIME_CHANGED -> setUpdates(context)
Actions.ACTION_WEATHER_UPDATE -> {
WeatherHelper.updateWeather(context)
}
}
}
companion object {
private const val MINUTE = 60 * 1000L
fun setUpdates(context: Context) {
removeUpdates(context)
if (Preferences.showWeather && Preferences.weatherProviderApi != "") {
val interval = MINUTE * when (Preferences.weatherRefreshPeriod) {
0 -> 30
1 -> 60
2 -> 60L * 3
3 -> 60L * 6
4 -> 60L * 12
5 -> 60L * 24
else -> 60
}
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
setRepeating(
AlarmManager.RTC,
Calendar.getInstance().timeInMillis,
interval,
PendingIntent.getBroadcast(context, 0, Intent(context, WeatherReceiver::class.java).apply { action = Actions.ACTION_WEATHER_UPDATE }, 0)
)
}
}
}
fun setOneTimeUpdate(context: Context) {
if (Preferences.showWeather && Preferences.weatherProviderApi != "") {
listOf(10, 20, 30).forEach {
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
setExactAndAllowWhileIdle(
AlarmManager.RTC,
it * MINUTE,
PendingIntent.getBroadcast(context, it, Intent(context, WeatherReceiver::class.java).apply { action = Actions.ACTION_WEATHER_UPDATE }, 0)
)
}
}
}
}
fun removeUpdates(context: Context) {
with(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
cancel(PendingIntent.getBroadcast(context, 0, Intent(context, WeatherReceiver::class.java).apply { action = Actions.ACTION_WEATHER_UPDATE }, 0))
listOf(10, 20, 30).forEach {
cancel(PendingIntent.getBroadcast(context, it, Intent(context, WeatherReceiver::class.java).apply { action = Actions.ACTION_WEATHER_UPDATE }, 0))
}
}
}
}
}

View File

@ -1,64 +0,0 @@
package com.tommasoberlose.anotherwidget.services
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.work.*
import com.tommasoberlose.anotherwidget.global.Actions
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.WeatherHelper
import com.tommasoberlose.anotherwidget.receivers.WeatherReceiver
import java.util.*
import java.util.concurrent.TimeUnit
class WeatherWorker(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
override fun doWork(): Result {
Log.d("ciao1", "weather ok")
WeatherHelper.updateWeather(applicationContext)
return Result.success()
}
companion object {
private const val JOB_TAG = "WEATHER_WORKER"
fun setUpdates(context: Context) {
removeUpdates(context)
if (Preferences.showWeather && Preferences.weatherProviderApi != "") {
WeatherHelper.updateWeather(context)
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"WEATHER_JOB_PERIODIC",
ExistingPeriodicWorkPolicy.KEEP,
PeriodicWorkRequestBuilder<WeatherWorker>(
when (Preferences.weatherRefreshPeriod) {
0 -> 30
1 -> 60
2 -> 60L * 3
3 -> 60L * 6
4 -> 60L * 12
5 -> 60L * 24
else -> 60
}
, TimeUnit.MINUTES
)
.addTag(JOB_TAG)
.build()
)
}
}
fun setOneTimeUpdate(context: Context) {
val workManager = WorkManager.getInstance(context)
listOf(10L, 20L, 30L).forEach {
workManager.enqueueUniqueWork("WEATHER_JOB_ONE_TIME_$it", ExistingWorkPolicy.KEEP, OneTimeWorkRequestBuilder<WeatherWorker>().setInitialDelay(it, TimeUnit.MINUTES).addTag(JOB_TAG).build())
}
}
fun removeUpdates(context: Context) {
WorkManager.getInstance(context).cancelAllWorkByTag(JOB_TAG)
}
}
}

View File

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

View File

@ -6,12 +6,15 @@ import android.app.Activity
import android.appwidget.AppWidgetManager
import android.content.Intent
import android.content.SharedPreferences
import android.graphics.Bitmap
import android.graphics.Matrix
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.util.DisplayMetrics
import android.util.Log
import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.widget.RelativeLayout
import androidx.appcompat.app.AppCompatActivity
@ -21,8 +24,15 @@ import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.Navigation
import com.google.android.material.badge.BadgeDrawable
import com.google.android.material.tabs.TabLayoutMediator
import com.karumi.dexter.Dexter
import com.karumi.dexter.MultiplePermissionsReport
import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.MaterialBottomSheetDialog
import com.tommasoberlose.anotherwidget.global.Actions
@ -47,10 +57,16 @@ import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
class MainActivity : AppCompatActivity() {
private var mAppWidgetId: Int = -1
private lateinit var viewModel: MainViewModel
private val mainNavController: NavController? by lazy {
Navigation.findNavController(
this,
R.id.content_fragment
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -59,228 +75,19 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
controlExtras(intent)
// Viewpager
pager.adapter = ViewPagerAdapter(this)
pager.offscreenPageLimit = 4
TabLayoutMediator(tabs, pager) { tab, position ->
tab.text = when (position) {
0 -> getString(R.string.settings_general_title)
1 -> getString(R.string.settings_calendar_title)
2 -> getString(R.string.settings_weather_title)
3 -> getString(R.string.settings_clock_title)
4 -> getString(R.string.advanced_settings_title)
else -> ""
}
}.attach()
// Init clock
time.setTextColor(ColorHelper.getFontColor())
time.setTextSize(TypedValue.COMPLEX_UNIT_SP, Preferences.clockTextSize.toPixel(this@MainActivity))
time_am_pm.setTextColor(ColorHelper.getFontColor())
time_am_pm.setTextSize(TypedValue.COMPLEX_UNIT_SP, Preferences.clockTextSize.toPixel(this@MainActivity) / 5 * 2)
time_container.isVisible = Preferences.showClock
preview.layoutParams = preview.layoutParams.apply {
height = 160.toPixel(this@MainActivity) + if (Preferences.showClock) 100.toPixel(this@MainActivity) else 0
}
Preferences.preferences.registerOnSharedPreferenceChangeListener(this)
subscribeUi(viewModel)
updateUI()
WeatherHelper.updateWeather(this)
// Warnings
if (getString(R.string.xiaomi_manufacturer).equals(Build.MANUFACTURER, ignoreCase = true) && Preferences.showXiaomiWarning) {
MaterialBottomSheetDialog(this, getString(R.string.xiaomi_warning_title), getString(R.string.xiaomi_warning_message))
.setNegativeButton(getString(R.string.action_ignore)) {
Preferences.showXiaomiWarning = false
}
.setPositiveButton(getString(R.string.action_grant_permission)) {
Preferences.showXiaomiWarning = false
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.parse("package:$packageName")
}
startActivity(intent)
}
.show()
}
}
private var uiJob: Job? = null
private fun updateUI() {
uiJob?.cancel()
if (Preferences.showPreview) {
preview.setCardBackgroundColor(
getColor(
if (ColorHelper.getFontColor()
.isColorDark()
) android.R.color.white else R.color.colorAccent
)
)
widget_shape_background.setImageDrawable(BitmapHelper.getTintedDrawable(this, R.drawable.card_background, ColorHelper.getBackgroundColor()))
uiJob = lifecycleScope.launch(Dispatchers.IO) {
delay(200)
val generatedView = MainWidget.generateWidgetView(this@MainActivity)
withContext(Dispatchers.Main) {
generatedView.measure(0, 0)
preview.measure(0, 0)
}
val bitmap = BitmapHelper.getBitmapFromView(
generatedView,
if (preview.width > 0) preview.width else generatedView.measuredWidth,
generatedView.measuredHeight
)
withContext(Dispatchers.Main) {
// Clock
time.setTextColor(ColorHelper.getFontColor())
time_am_pm.setTextColor(ColorHelper.getFontColor())
time.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(this@MainActivity)
)
time_am_pm.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(this@MainActivity) / 5 * 2
)
// Clock bottom margin
clock_bottom_margin_none.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.NONE.value
clock_bottom_margin_small.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.SMALL.value
clock_bottom_margin_medium.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.MEDIUM.value
clock_bottom_margin_large.isVisible =
Preferences.showClock && Preferences.clockBottomMargin == Constants.ClockBottomMargin.LARGE.value
if ((Preferences.showClock && !time_container.isVisible) || (!Preferences.showClock && time_container.isVisible)) {
if (Preferences.showClock) {
time_container.layoutParams = time_container.layoutParams.apply {
height = RelativeLayout.LayoutParams.WRAP_CONTENT
}
time_container.measure(0, 0)
}
val initialHeight = time_container.measuredHeight
ValueAnimator.ofFloat(
if (Preferences.showClock) 0f else 1f,
if (Preferences.showClock) 1f else 0f
).apply {
duration = 500L
addUpdateListener {
val animatedValue = animatedValue as Float
time_container.layoutParams = time_container.layoutParams.apply {
height = (initialHeight * animatedValue).toInt()
}
}
addListener(
onStart = {
if (Preferences.showClock) {
time_container.isVisible = true
}
},
onEnd = {
if (!Preferences.showClock) {
time_container.isVisible = false
}
}
)
}.start()
ValueAnimator.ofInt(
preview.height,
160.toPixel(this@MainActivity) + if (Preferences.showClock) 100.toPixel(
this@MainActivity
) else 0
).apply {
duration = 500L
addUpdateListener {
val animatedValue = animatedValue as Int
val layoutParams = preview.layoutParams
layoutParams.height = animatedValue
preview.layoutParams = layoutParams
}
}.start()
} else {
time_container.layoutParams = time_container.layoutParams.apply {
height = RelativeLayout.LayoutParams.WRAP_CONTENT
}
time_container.measure(0, 0)
}
if (preview.height == 0) {
ValueAnimator.ofInt(
preview.height,
160.toPixel(this@MainActivity) + if (Preferences.showClock) 100.toPixel(
this@MainActivity
) else 0
).apply {
duration = 300L
addUpdateListener {
val animatedValue = animatedValue as Int
val layoutParams = preview.layoutParams
layoutParams.height = animatedValue
preview.layoutParams = layoutParams
}
}.start()
}
bitmap_container.setImageBitmap(bitmap)
widget_loader.animate().scaleX(0f).scaleY(0f).start()
widget.animate().alpha(1f).start()
}
}
} else {
ValueAnimator.ofInt(
preview.height,
0
).apply {
duration = 300L
addUpdateListener {
val animatedValue = animatedValue as Int
val layoutParams = preview.layoutParams
layoutParams.height = animatedValue
preview.layoutParams = layoutParams
}
}.start()
}
// Calendar error indicator
tabs.getTabAt(1)?.orCreateBadge?.apply {
backgroundColor = ContextCompat.getColor(this@MainActivity, R.color.errorColorText)
badgeGravity = BadgeDrawable.TOP_END
}?.isVisible = Preferences.showEvents && !checkGrantedPermission(Manifest.permission.READ_CALENDAR)
// Weather error indicator
tabs.getTabAt(2)?.orCreateBadge?.apply {
backgroundColor = ContextCompat.getColor(this@MainActivity, R.color.errorColorText)
badgeGravity = BadgeDrawable.TOP_END
}?.isVisible = Preferences.showWeather && (Preferences.weatherProviderApi == "" || (Preferences.customLocationAdd == "" && !checkGrantedPermission(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else Manifest.permission.ACCESS_FINE_LOCATION)))
}
private fun subscribeUi(viewModel: MainViewModel) {
viewModel.showWallpaper.observe(this, Observer {
widget_bg.setImageDrawable(if (it) getCurrentWallpaper() else null)
})
logo.setOnClickListener {
// startActivity(Intent(this, SupportDevActivity::class.java))
}
requirePermission()
}
override fun onBackPressed() {
if (mAppWidgetId > 0) {
addNewWidget()
if (mainNavController?.currentDestination?.id == R.id.appMainFragment) {
if (mAppWidgetId > 0) {
addNewWidget()
} else {
setResult(Activity.RESULT_OK)
finish()
}
} else {
setResult(Activity.RESULT_OK)
finish()
super.onBackPressed()
}
}
@ -320,30 +127,27 @@ class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceCh
finish()
}
override fun onDestroy() {
super.onDestroy()
Preferences.preferences.unregisterOnSharedPreferenceChangeListener(this)
}
private fun requirePermission() {
Dexter.withContext(this)
.withPermissions(
Manifest.permission.READ_EXTERNAL_STORAGE
).withListener(object : MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport?) {
report?.let {
Preferences.showWallpaper = false
Preferences.showWallpaper = report.areAllPermissionsGranted()
}
}
override fun onSharedPreferenceChanged(preferences: SharedPreferences, p1: String) {
updateUI()
MainWidget.updateWidget(this)
}
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}
override fun onStop() {
super.onStop()
EventBus.getDefault().unregister(this)
}
class UpdateUiMessageEvent
@Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(ignore: UpdateUiMessageEvent?) {
updateUI()
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?.cancelPermissionRequest()
}
})
.check()
}
}

View File

@ -12,11 +12,11 @@ class ViewPagerAdapter(fragmentActivity: FragmentActivity) :
override fun createFragment(position: Int): Fragment {
return when (position) {
1 -> CalendarSettingsFragment.newInstance()
2 -> WeatherSettingsFragment.newInstance()
3 -> ClockSettingsFragment.newInstance()
4 -> AdvancedSettingsFragment.newInstance()
else -> GeneralSettingsFragment.newInstance()
1 -> CalendarTabFragment.newInstance()
2 -> WeatherTabFragment.newInstance()
3 -> ClockTabFragment.newInstance()
4 -> GlanceTabFragment.newInstance()
else -> GeneralTabFragment.newInstance()
}
}
}

View File

@ -4,10 +4,10 @@ import android.Manifest
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
@ -43,10 +43,10 @@ import kotlinx.coroutines.launch
import java.util.*
import kotlin.Comparator
class CalendarSettingsFragment : Fragment() {
class CalendarTabFragment : Fragment() {
companion object {
fun newInstance() = CalendarSettingsFragment()
fun newInstance() = CalendarTabFragment()
}
private lateinit var viewModel: MainViewModel
@ -156,6 +156,16 @@ class CalendarSettingsFragment : Fragment() {
action_show_events.setOnClickListener {
Preferences.showEvents = !Preferences.showEvents
if (Preferences.showEvents) {
requirePermission()
}
}
show_events_switch.setOnCheckedChangeListener { _, enabled: Boolean ->
Preferences.showEvents = enabled
if (Preferences.showEvents) {
requirePermission()
}
}
action_filter_calendar.setOnClickListener {
@ -268,7 +278,7 @@ class CalendarSettingsFragment : Fragment() {
action_show_until.setOnClickListener {
if (Preferences.showEvents) {
val dialog = BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_show_until_title)).setSelectedValue(Preferences.showUntil)
intArrayOf(6,7,0,1,2,3,4,5).forEach {
intArrayOf(6,7,0,1,2).forEach {
dialog.addItem(getString(SettingsStringHelper.getShowUntilString(it)), it)
}
dialog.addOnSelectItemListener { value ->
@ -337,6 +347,8 @@ class CalendarSettingsFragment : Fragment() {
report?.let {
if (report.areAllPermissionsGranted()){
checkReadEventsPermission()
} else {
Preferences.showEvents = false
}
}
}

View File

@ -1,7 +1,12 @@
package com.tommasoberlose.anotherwidget.ui.fragments
import android.app.Activity
import android.app.AlarmManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -14,25 +19,35 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.chibatching.kotpref.bulk
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.BottomSheetColorPicker
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
import com.tommasoberlose.anotherwidget.databinding.FragmentClockSettingsBinding
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.global.RequestCode
import com.tommasoberlose.anotherwidget.helpers.AlarmHelper
import com.tommasoberlose.anotherwidget.helpers.ColorHelper
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toHexValue
import com.tommasoberlose.anotherwidget.helpers.ColorHelper.toIntValue
import com.tommasoberlose.anotherwidget.ui.activities.ChooseApplicationActivity
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import kotlinx.android.synthetic.main.fragment_clock_settings.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.lang.Exception
class ClockSettingsFragment : Fragment() {
class ClockTabFragment : Fragment() {
companion object {
fun newInstance() = ClockSettingsFragment()
fun newInstance() = ClockTabFragment()
}
private lateinit var viewModel: MainViewModel
private lateinit var colors: IntArray
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -57,6 +72,12 @@ class ClockSettingsFragment : Fragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
lifecycleScope.launch(Dispatchers.IO) {
val lazyColors = requireContext().resources.getIntArray(R.array.material_colors)
withContext(Dispatchers.Main) {
colors = lazyColors
}
}
setupListener()
}
@ -83,6 +104,28 @@ class ClockSettingsFragment : Fragment() {
}
})
viewModel.clockTextColor.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
if (Preferences.clockTextAlpha == "00") {
clock_text_color_label?.text = getString(R.string.transparent)
} else {
clock_text_color_label?.text =
"#%s".format(Integer.toHexString(ColorHelper.getClockFontColor())).toUpperCase()
}
}
})
viewModel.clockTextAlpha.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
if (Preferences.clockTextAlpha == "00") {
clock_text_color_label?.text = getString(R.string.transparent)
} else {
clock_text_color_label?.text =
"#%s".format(Integer.toHexString(ColorHelper.getClockFontColor())).toUpperCase()
}
}
})
viewModel.clockBottomMargin.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
clock_bottom_margin_label?.text = when (it) {
@ -94,12 +137,6 @@ class ClockSettingsFragment : Fragment() {
}
})
viewModel.showNextAlarm.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
show_next_alarm_label?.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
}
})
viewModel.clockAppName.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
clock_app_label?.text =
@ -117,6 +154,10 @@ class ClockSettingsFragment : Fragment() {
Preferences.showClock = !Preferences.showClock
}
show_clock_switch.setOnCheckedChangeListener { _, enabled: Boolean ->
Preferences.showClock = enabled
}
action_clock_text_size.setOnClickListener {
val dialog = BottomSheetMenu<Float>(requireContext(), header = getString(R.string.settings_clock_text_size_title)).setSelectedValue(Preferences.clockTextSize)
(46 downTo 12).filter { it % 2 == 0 }.forEach {
@ -127,8 +168,25 @@ class ClockSettingsFragment : Fragment() {
}.show()
}
action_clock_text_color.setOnClickListener {
BottomSheetColorPicker(requireContext(),
colors = colors,
header = getString(R.string.settings_font_color_title),
getSelected = ColorHelper::getClockFontColorRgb,
onColorSelected = { color: Int ->
val colorString = Integer.toHexString(color)
Preferences.clockTextColor = "#" + if (colorString.length > 6) colorString.substring(2) else colorString
},
showAlphaSelector = true,
alpha = Preferences.clockTextAlpha.toIntValue(),
onAlphaChangeListener = { alpha ->
Preferences.clockTextAlpha = alpha.toHexValue()
}
).show()
}
action_clock_bottom_margin_size.setOnClickListener {
BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_show_next_alarm_title)).setSelectedValue(Preferences.clockBottomMargin)
BottomSheetMenu<Int>(requireContext(), header = getString(R.string.settings_clock_bottom_margin_title)).setSelectedValue(Preferences.clockBottomMargin)
.addItem(getString(R.string.settings_clock_bottom_margin_subtitle_none), Constants.ClockBottomMargin.NONE.value)
.addItem(getString(R.string.settings_clock_bottom_margin_subtitle_small), Constants.ClockBottomMargin.SMALL.value)
.addItem(getString(R.string.settings_clock_bottom_margin_subtitle_medium), Constants.ClockBottomMargin.MEDIUM.value)
@ -145,15 +203,6 @@ class ClockSettingsFragment : Fragment() {
)
}
}
action_show_next_alarm.setOnClickListener {
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_show_next_alarm_title)).setSelectedValue(Preferences.showNextAlarm)
.addItem(getString(R.string.settings_visible), true)
.addItem(getString(R.string.settings_not_visible), false)
.addOnSelectItemListener { value ->
Preferences.showNextAlarm = value
}.show()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

View File

@ -3,9 +3,7 @@ package com.tommasoberlose.anotherwidget.ui.fragments
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -33,10 +31,10 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class GeneralSettingsFragment : Fragment() {
class GeneralTabFragment : Fragment() {
companion object {
fun newInstance() = GeneralSettingsFragment()
fun newInstance() = GeneralTabFragment()
}
private lateinit var viewModel: MainViewModel
@ -148,6 +146,10 @@ class GeneralSettingsFragment : Fragment() {
custom_font_label?.text = getString(SettingsStringHelper.getCustomFontLabel(it))
}
})
viewModel.showDividers.observe(viewLifecycleOwner, Observer {
show_dividers_label?.text = if (it) getString(R.string.settings_visible) else getString(R.string.settings_not_visible)
})
}
private fun maintainScrollPosition(callback: () -> Unit) {
@ -184,7 +186,7 @@ class GeneralSettingsFragment : Fragment() {
BottomSheetColorPicker(requireContext(),
colors = colors,
header = getString(R.string.settings_font_color_title),
selected = ColorHelper.getFontColor(),
getSelected = ColorHelper::getFontColorRgb,
onColorSelected = { color: Int ->
val colorString = Integer.toHexString(color)
Preferences.textGlobalColor = "#" + if (colorString.length > 6) colorString.substring(2) else colorString
@ -201,7 +203,7 @@ class GeneralSettingsFragment : Fragment() {
BottomSheetColorPicker(requireContext(),
colors = colors,
header = getString(R.string.settings_background_color_title),
selected = ColorHelper.getBackgroundColor(),
getSelected = { ColorHelper.getBackgroundColorRgb() },
onColorSelected = { color: Int ->
val colorString = Integer.toHexString(color)
Preferences.backgroundCardColor = "#" + if (colorString.length > 6) colorString.substring(2) else colorString
@ -245,6 +247,15 @@ class GeneralSettingsFragment : Fragment() {
}
*/
}
action_show_dividers.setOnClickListener {
BottomSheetMenu<Boolean>(requireContext(), header = getString(R.string.settings_show_multiple_events_title)).setSelectedValue(Preferences.showDividers)
.addItem(getString(R.string.settings_visible), true)
.addItem(getString(R.string.settings_not_visible), false)
.addOnSelectItemListener { value ->
Preferences.showDividers = value
}.show()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

View File

@ -0,0 +1,266 @@
package com.tommasoberlose.anotherwidget.ui.fragments
import android.app.AlarmManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.components.BottomSheetMenu
import com.tommasoberlose.anotherwidget.databinding.FragmentGlanceSettingsBinding
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.AlarmHelper
import com.tommasoberlose.anotherwidget.helpers.GlanceProviderHelper
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
import com.tommasoberlose.anotherwidget.models.GlanceProvider
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
import com.tommasoberlose.anotherwidget.ui.viewmodels.MainViewModel
import kotlinx.android.synthetic.main.fragment_glance_settings.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.idik.lib.slimadapter.SlimAdapter
import java.util.*
class GlanceTabFragment : Fragment() {
companion object {
fun newInstance() = GlanceTabFragment()
}
private lateinit var viewModel: MainViewModel
private lateinit var adapter: SlimAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewModel = ViewModelProvider(activity as MainActivity).get(MainViewModel::class.java)
val binding = DataBindingUtil.inflate<FragmentGlanceSettingsBinding>(inflater, R.layout.fragment_glance_settings, container, false)
subscribeUi(binding, viewModel)
binding.lifecycleOwner = this
binding.viewModel = viewModel
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
list.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(requireContext())
list.layoutManager = mLayoutManager
adapter = SlimAdapter.create()
adapter
.register<String>(R.layout.glance_provider_item) { item, injector ->
injector
.text(R.id.title, item)
}
.register<GlanceProvider>(R.layout.glance_provider_item) { item, injector ->
injector
.text(R.id.title, item.title)
.with<ImageView>(R.id.icon) {
it.setImageDrawable(ContextCompat.getDrawable(requireContext(), item.icon))
}
.with<TextView>(R.id.label) {
it.isVisible = item.label != ""
it.text = item.label
}
}
.attachTo(list)
adapter.updateData(
GlanceProviderHelper.getGlanceProviders()
.mapNotNull { GlanceProviderHelper.getGlanceProviderById(requireContext(), it) }
)
val mIth = ItemTouchHelper(
object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
ItemTouchHelper.LEFT
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: ViewHolder, target: ViewHolder
): Boolean {
val fromPos = viewHolder.adapterPosition
val toPos = target.adapterPosition
// move item in `fromPos` to `toPos` in adapter.
adapter.notifyItemMoved(fromPos, toPos)
val list = GlanceProviderHelper.getGlanceProviders()
Collections.swap(list, fromPos, toPos)
GlanceProviderHelper.saveGlanceProviderOrder(list)
return true
}
override fun onSwiped(
viewHolder: ViewHolder,
direction: Int
) {
// remove from adapter
}
})
mIth.attachToRecyclerView(list)
setupListener()
updateNextAlarmWarningUi()
}
private fun subscribeUi(
binding: FragmentGlanceSettingsBinding,
viewModel: MainViewModel
) {
viewModel.showGlance.observe(viewLifecycleOwner, Observer {
binding.isGlanceVisible = it
})
viewModel.showMusic.observe(viewLifecycleOwner, Observer {
checkNotificationPermission()
})
viewModel.showNextAlarm.observe(viewLifecycleOwner, Observer {
updateNextAlarmWarningUi()
})
}
private fun setupListener() {
action_show_glance.setOnClickListener {
Preferences.showGlance = !Preferences.showGlance
}
show_glance_switch.setOnCheckedChangeListener { _, enabled: Boolean ->
Preferences.showGlance = enabled
}
action_show_music.setOnClickListener {
if (Preferences.showGlance) {
BottomSheetMenu<Boolean>(
requireContext(),
header = getString(R.string.settings_show_music_title)
).setSelectedValue(Preferences.showMusic)
.addItem(getString(R.string.settings_visible), true)
.addItem(getString(R.string.settings_not_visible), false)
.addOnSelectItemListener { value ->
Preferences.showMusic = value
}.show()
}
}
action_show_next_alarm.setOnClickListener {
if (Preferences.showGlance) {
BottomSheetMenu<Boolean>(
requireContext(),
header = getString(R.string.settings_show_next_alarm_title)
).setSelectedValue(Preferences.showNextAlarm)
.addItem(getString(R.string.settings_visible), true)
.addItem(getString(R.string.settings_not_visible), false)
.addOnSelectItemListener { value ->
Preferences.showNextAlarm = value
}.show()
}
}
}
private fun updateNextAlarmWarningUi() {
with(requireContext().getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
val alarm = nextAlarmClock
if (AlarmHelper.isAlarmProbablyWrong(requireContext()) && alarm != null && alarm.showIntent != null) {
val pm = requireContext().packageManager as PackageManager
val appNameOrPackage = try {
pm.getApplicationLabel(pm.getApplicationInfo(alarm.showIntent?.creatorPackage ?: "", 0))
} catch (e: Exception) {
alarm.showIntent?.creatorPackage ?: ""
}
maintainScrollPosition {
show_next_alarm_warning.text =
getString(R.string.next_alarm_warning).format(appNameOrPackage)
}
} else {
maintainScrollPosition {
show_next_alarm_label?.text = if (Preferences.showNextAlarm) getString(R.string.settings_visible) else getString(
R.string.settings_not_visible)
}
}
}
}
private val nextAlarmChangeBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
updateNextAlarmWarningUi()
}
}
override fun onStart() {
super.onStart()
activity?.registerReceiver(nextAlarmChangeBroadcastReceiver, IntentFilter(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED))
}
override fun onStop() {
activity?.unregisterReceiver(nextAlarmChangeBroadcastReceiver)
super.onStop()
}
private fun checkNotificationPermission() {
if (NotificationManagerCompat.getEnabledListenerPackages(requireContext()).contains(requireContext().packageName)) {
notification_permission_alert?.isVisible = false
MediaPlayerHelper.updatePlayingMediaInfo(requireContext())
show_music_label?.text = if (Preferences.showMusic) getString(R.string.settings_show_music_enabled_subtitle) else getString(R.string.settings_show_music_disabled_subtitle)
} else if (Preferences.showMusic) {
notification_permission_alert?.isVisible = true
show_music_label?.text = getString(R.string.settings_request_notification_access)
notification_permission_alert?.setOnClickListener {
activity?.startActivity(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"))
}
} else {
show_music_label?.text = getString(R.string.settings_show_music_disabled_subtitle)
notification_permission_alert?.isVisible = false
}
}
private fun maintainScrollPosition(callback: () -> Unit) {
val scrollPosition = scrollView.scrollY
callback.invoke()
lifecycleScope.launch {
delay(200)
scrollView.smoothScrollTo(0, scrollPosition)
}
}
override fun onResume() {
super.onResume()
checkNotificationPermission()
}
}

View File

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

View File

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

View File

@ -29,7 +29,6 @@ import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.global.RequestCode
import com.tommasoberlose.anotherwidget.helpers.SettingsStringHelper
import com.tommasoberlose.anotherwidget.receivers.WeatherReceiver
import com.tommasoberlose.anotherwidget.services.WeatherWorker
import com.tommasoberlose.anotherwidget.ui.activities.ChooseApplicationActivity
import com.tommasoberlose.anotherwidget.ui.activities.CustomLocationActivity
import com.tommasoberlose.anotherwidget.ui.activities.MainActivity
@ -42,10 +41,10 @@ import kotlinx.android.synthetic.main.fragment_weather_settings.scrollView
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class WeatherSettingsFragment : Fragment() {
class WeatherTabFragment : Fragment() {
companion object {
fun newInstance() = WeatherSettingsFragment()
fun newInstance() = WeatherTabFragment()
}
private lateinit var viewModel: MainViewModel
@ -82,12 +81,14 @@ class WeatherSettingsFragment : Fragment() {
) {
viewModel.showWeatherWarning.observe(viewLifecycleOwner, Observer {
weather_warning?.isVisible = it
checkLocationPermission()
})
viewModel.showWeather.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
show_weather_label?.text =
if (it) getString(R.string.show_weather_visible) else getString(R.string.show_weather_not_visible)
checkWeatherProviderConfig()
binding.isWeatherVisible = it
}
checkLocationPermission()
@ -95,11 +96,7 @@ class WeatherSettingsFragment : Fragment() {
viewModel.weatherProviderApi.observe(viewLifecycleOwner, Observer {
maintainScrollPosition {
label_weather_provider_api_key?.text =
if (it == "") getString(R.string.settings_weather_provider_api_key_subtitle_not_set) else getString(
R.string.settings_weather_provider_api_key_subtitle_all_set
)
label_weather_provider_api_key?.setTextColor(ContextCompat.getColor(requireContext(), if (it == "") R.color.errorColorText else R.color.colorSecondaryText))
checkWeatherProviderConfig()
}
checkLocationPermission()
})
@ -137,21 +134,31 @@ class WeatherSettingsFragment : Fragment() {
private fun checkLocationPermission() {
// Background permission
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && activity?.checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION) == true) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && activity?.checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION) == true && activity?.checkGrantedPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) != true) {
requirePermission()
}
if (activity?.checkGrantedPermission(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else Manifest.permission.ACCESS_FINE_LOCATION) == true) {
location_permission_alert?.isVisible = false
WeatherWorker.setUpdates(requireContext())
WeatherReceiver.setUpdates(requireContext())
} else if (Preferences.showWeather && Preferences.customLocationAdd == "") {
location_permission_alert?.isVisible = true
location_permission_alert?.setOnClickListener {
requirePermission()
}
} else {
location_permission_alert?.isVisible = false
}
}
private fun checkWeatherProviderConfig() {
label_weather_provider_api_key?.text =
if (Preferences.weatherProviderApi == "") getString(R.string.settings_weather_provider_api_key_subtitle_not_set) else getString(
R.string.settings_weather_provider_api_key_subtitle_all_set
)
label_weather_provider_api_key?.setTextColor(ContextCompat.getColor(requireContext(), if (Preferences.weatherProviderApi == "" && Preferences.showWeather) R.color.errorColorText else R.color.colorSecondaryText))
}
private fun setupListener() {
action_hide_weather_warning.setOnClickListener {
Preferences.showWeatherWarning = false
@ -161,6 +168,10 @@ class WeatherSettingsFragment : Fragment() {
Preferences.showWeather = !Preferences.showWeather
}
show_weather_switch.setOnCheckedChangeListener { _, enabled: Boolean ->
Preferences.showWeather = enabled
}
action_weather_provider_api_key.setOnClickListener {
if (Preferences.showWeather) {
startActivityForResult(
@ -218,7 +229,7 @@ class WeatherSettingsFragment : Fragment() {
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
Constants.RESULT_CODE_CUSTOM_LOCATION -> {
WeatherWorker.setUpdates(requireContext())
WeatherReceiver.setUpdates(requireContext())
checkLocationPermission()
}
RequestCode.WEATHER_APP_REQUEST_CODE.code -> {
@ -229,7 +240,7 @@ class WeatherSettingsFragment : Fragment() {
MainWidget.updateWidget(requireContext())
}
RequestCode.WEATHER_PROVIDER_REQUEST_CODE.code -> {
WeatherWorker.setOneTimeUpdate(requireContext())
WeatherReceiver.setOneTimeUpdate(requireContext())
}
}
}

View File

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

View File

@ -16,6 +16,7 @@ class MainViewModel : ViewModel() {
val textShadow = Preferences.asLiveData(Preferences::textShadow)
val customFont = Preferences.asLiveData(Preferences::customFont)
val secondRowInformation = Preferences.asLiveData(Preferences::secondRowInformation)
val showDividers = Preferences.asLiveData(Preferences::showDividers)
// Calendar Settings
val showEvents = Preferences.asLiveData(Preferences::showEvents)
@ -27,13 +28,13 @@ class MainViewModel : ViewModel() {
val openEventDetails = Preferences.asLiveData(Preferences::openEventDetails)
val calendarAppName = Preferences.asLiveData(Preferences::calendarAppName)
// Clock Settings
val showClock = Preferences.asLiveData(Preferences::showClock)
val clockTextSize = Preferences.asLiveData(Preferences::clockTextSize)
val clockTextColor = Preferences.asLiveData(Preferences::clockTextColor)
val clockTextAlpha = Preferences.asLiveData(Preferences::clockTextAlpha)
val clockAppName = Preferences.asLiveData(Preferences::clockAppName)
val showNextAlarm = Preferences.asLiveData(Preferences::showNextAlarm)
val dateFormat = Preferences.asLiveData(Preferences::dateFormat)
val clockBottomMargin = Preferences.asLiveData(Preferences::clockBottomMargin)
@ -51,8 +52,14 @@ class MainViewModel : ViewModel() {
val showWeatherWarning = Preferences.asLiveData(Preferences::showWeatherWarning)
// Glance
val showGlance = Preferences.asLiveData(Preferences::showGlance)
val showMusic = Preferences.asLiveData(Preferences::showMusic)
val showNextAlarm = Preferences.asLiveData(Preferences::showNextAlarm)
// Advanced Settings
val darkThemePreference = Preferences.asLiveData(Preferences::darkThemePreference)
val showWallpaper = Preferences.asLiveData(Preferences::showWallpaper)
val showPreview = Preferences.asLiveData(Preferences::showPreview)
val installedIntegrations = Preferences.asLiveData(Preferences::installedIntegrations)
}

View File

@ -20,23 +20,17 @@ import android.widget.RemoteViews
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.db.EventRepository
import com.tommasoberlose.anotherwidget.global.Actions
import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.*
import com.tommasoberlose.anotherwidget.helpers.WidgetHelper.reduceDimensionWithMaxWidth
import com.tommasoberlose.anotherwidget.receivers.NewCalendarEventReceiver
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
import com.tommasoberlose.anotherwidget.receivers.WidgetClickListenerReceiver
import com.tommasoberlose.anotherwidget.services.WeatherWorker
import com.tommasoberlose.anotherwidget.receivers.*
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.getCapWordString
import com.tommasoberlose.anotherwidget.utils.toPixel
import kotlinx.android.synthetic.main.the_widget.view.*
import kotlinx.android.synthetic.main.the_widget_sans.*
import java.lang.Exception
import java.text.DateFormat
import java.util.*
@ -58,7 +52,8 @@ class MainWidget : AppWidgetProvider() {
override fun onEnabled(context: Context) {
CalendarHelper.updateEventList(context)
WeatherWorker.setUpdates(context)
WeatherReceiver.setUpdates(context)
MediaPlayerHelper.updatePlayingMediaInfo(context)
if (Preferences.showEvents) {
CalendarHelper.setEventUpdatesAndroidN(context)
@ -70,20 +65,14 @@ class MainWidget : AppWidgetProvider() {
override fun onDisabled(context: Context) {
if (getWidgetCount(context) == 0) {
UpdatesReceiver.removeUpdates(context)
WeatherWorker.removeUpdates(context)
WeatherReceiver.removeUpdates(context)
}
}
companion object {
fun updateWidget(context: Context) {
val widgetManager = AppWidgetManager.getInstance(context)
val widgetComponent = ComponentName(context, MainWidget::class.java)
val widgetIds = widgetManager.getAppWidgetIds(widgetComponent)
val update = Intent(context, MainWidget::class.java)
update.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIds)
update.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
context.sendBroadcast(update)
context.sendBroadcast(IntentHelper.getWidgetUpdateIntent(context))
}
fun getWidgetCount(context: Context): Int {
@ -104,19 +93,46 @@ class MainWidget : AppWidgetProvider() {
private fun generateWidgetView(context: Context, appWidgetId: Int, appWidgetManager: AppWidgetManager, w: Int) {
var views = RemoteViews(context.packageName, R.layout.the_widget_sans)
val generatedView = generateWidgetView(context)
views.setImageViewBitmap(R.id.bitmap_container, BitmapHelper.getBitmapFromView(generatedView, width = w))
// Background
views.setInt(R.id.widget_shape_background, "setColorFilter", ColorHelper.getBackgroundColorRgb())
views.setInt(R.id.widget_shape_background, "setImageAlpha", ColorHelper.getBackgroundAlpha())
try {
// Background
views.setInt(
R.id.widget_shape_background,
"setColorFilter",
ColorHelper.getBackgroundColorRgb()
)
views.setInt(
R.id.widget_shape_background,
"setImageAlpha",
ColorHelper.getBackgroundAlpha()
)
val refreshIntent = PendingIntent.getActivity(
context,
appWidgetId,
IntentHelper.getWidgetUpdateIntent(context),
0
)
views.setOnClickPendingIntent(R.id.widget_shape_background, refreshIntent)
} catch (ex: Exception) {
ex.printStackTrace()
CrashlyticsReceiver.sendCrash(context, ex)
}
// Clock
views = updateClockView(context, views, appWidgetId)
// Setup listener
views = updateCalendarView(context, generatedView, views, appWidgetId)
views = updateWeatherView(context, generatedView, views, appWidgetId)
try {
val generatedView = generateWidgetView(context)
views.setImageViewBitmap(
R.id.bitmap_container,
BitmapHelper.getBitmapFromView(generatedView, width = w)
)
views = updateCalendarView(context, generatedView, views, appWidgetId)
views = updateWeatherView(context, generatedView, views, appWidgetId)
} catch (ex: Exception) {
ex.printStackTrace()
CrashlyticsReceiver.sendCrash(context, ex)
}
appWidgetManager.updateAppWidget(appWidgetId, views)
}
@ -133,6 +149,7 @@ class MainWidget : AppWidgetProvider() {
views.setViewVisibility(R.id.empty_layout_rect, View.VISIBLE)
views.setViewVisibility(R.id.calendar_layout_rect, View.GONE)
views.setViewVisibility(R.id.second_row_rect, View.GONE)
views.setViewVisibility(R.id.next_event_difference_time_rect, View.GONE)
val calPIntent = PendingIntent.getActivity(
context,
@ -244,14 +261,14 @@ class MainWidget : AppWidgetProvider() {
views.setViewVisibility(R.id.empty_layout_rect, View.GONE)
views.setViewVisibility(R.id.calendar_layout_rect, View.VISIBLE)
} else if (Preferences.showNextAlarm && nextAlarm != "") {
val clockIntent = PendingIntent.getActivity(
} else if (MediaPlayerHelper.isSomeonePlaying(context)) {
val musicIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getClockIntent(context),
IntentHelper.getMusicIntent(context),
0
)
views.setOnClickPendingIntent(R.id.second_row_rect, clockIntent)
views.setOnClickPendingIntent(R.id.second_row_rect, musicIntent)
views.setImageViewBitmap(
R.id.next_event_rect,
@ -266,10 +283,34 @@ class MainWidget : AppWidgetProvider() {
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.calendar_layout_rect, View.VISIBLE)
views.setOnClickPendingIntent(R.id.next_event_rect, calPIntent)
}
} catch (ex: Exception) {
ex.printStackTrace()
FirebaseCrashlytics.getInstance().recordException(ex)
CrashlyticsReceiver.sendCrash(context, ex)
}
return views
@ -280,6 +321,7 @@ class MainWidget : AppWidgetProvider() {
if (Preferences.showWeather && Preferences.weatherIcon != "") {
views.setViewVisibility(R.id.weather_rect, View.VISIBLE)
views.setViewVisibility(R.id.calendar_weather_rect, View.VISIBLE)
views.setViewVisibility(R.id.special_weather_rect, View.VISIBLE)
val i = Intent(context, WidgetClickListenerReceiver::class.java)
i.action = Actions.ACTION_OPEN_WEATHER_INTENT
@ -287,6 +329,7 @@ class MainWidget : AppWidgetProvider() {
views.setOnClickPendingIntent(R.id.weather_rect, weatherPIntent)
views.setOnClickPendingIntent(R.id.calendar_weather_rect, weatherPIntent)
views.setOnClickPendingIntent(R.id.special_weather_rect, weatherPIntent)
views.setImageViewBitmap(
R.id.weather_rect,
@ -297,13 +340,25 @@ class MainWidget : AppWidgetProvider() {
R.id.calendar_weather_rect,
BitmapHelper.getBitmapFromView(v.calendar_weather, draw = false)
)
views.setImageViewBitmap(
R.id.special_weather_rect,
BitmapHelper.getBitmapFromView(v.calendar_weather, draw = false)
)
if (WidgetHelper.showSpecialWeather(context)) {
views.setViewVisibility(R.id.calendar_weather_rect, View.GONE)
} else {
views.setViewVisibility(R.id.special_weather_rect, View.GONE)
}
} else {
views.setViewVisibility(R.id.weather_rect, View.GONE)
views.setViewVisibility(R.id.calendar_weather_rect, View.GONE)
views.setViewVisibility(R.id.special_weather_rect, View.GONE)
}
} catch (ex: Exception) {
ex.printStackTrace()
FirebaseCrashlytics.getInstance().recordException(ex)
CrashlyticsReceiver.sendCrash(context, ex)
}
return views
}
@ -318,8 +373,8 @@ class MainWidget : AppWidgetProvider() {
views.setViewVisibility(R.id.clock_bottom_margin_medium, View.GONE)
views.setViewVisibility(R.id.clock_bottom_margin_large, View.GONE)
} else {
views.setTextColor(R.id.time, ColorHelper.getFontColor())
views.setTextColor(R.id.time_am_pm, ColorHelper.getFontColor())
views.setTextColor(R.id.time, ColorHelper.getClockFontColor())
views.setTextColor(R.id.time_am_pm, ColorHelper.getClockFontColor())
views.setTextViewTextSize(
R.id.time,
TypedValue.COMPLEX_UNIT_SP,
@ -360,7 +415,7 @@ class MainWidget : AppWidgetProvider() {
}
} catch (ex: Exception) {
ex.printStackTrace()
FirebaseCrashlytics.getInstance().recordException(ex)
CrashlyticsReceiver.sendCrash(context, ex)
}
return views
@ -376,6 +431,7 @@ class MainWidget : AppWidgetProvider() {
v.empty_layout.visibility = View.VISIBLE
v.calendar_layout.visibility = View.GONE
v.next_event_difference_time.visibility = View.GONE
v.action_next.isVisible = false
v.action_previous.isVisible = false
@ -441,6 +497,17 @@ class MainWidget : AppWidgetProvider() {
}
}
v.empty_layout.visibility = View.GONE
v.calendar_layout.visibility = View.VISIBLE
} else if (MediaPlayerHelper.isSomeonePlaying(context)) {
v.second_row_icon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_music_note
)
)
v.next_event.text = DateHelper.getDateText(context, now)
v.next_event_date.text = MediaPlayerHelper.getMediaInfo()
v.empty_layout.visibility = View.GONE
v.calendar_layout.visibility = View.VISIBLE
} else if (Preferences.showNextAlarm && nextAlarm != "") {
@ -458,7 +525,7 @@ class MainWidget : AppWidgetProvider() {
// Color
listOf<TextView>(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.next_event_date, v.divider2, v.calendar_temp).forEach {
listOf<TextView>(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.next_event_date, v.divider2, v.calendar_temp, v.divider3, v.special_temp).forEach {
it.setTextColor(ColorHelper.getFontColor())
}
@ -469,13 +536,15 @@ class MainWidget : AppWidgetProvider() {
// Text Size
listOf<Pair<TextView, Float>>(
v.empty_date to Preferences.textMainSize,
v.divider1 to Preferences.textMainSize,
v.divider1 to (Preferences.textMainSize - 2),
v.temp to Preferences.textMainSize,
v.next_event to Preferences.textMainSize,
v.next_event_difference_time to Preferences.textMainSize,
v.next_event_date to Preferences.textSecondSize,
v.divider2 to Preferences.textSecondSize,
v.calendar_temp to Preferences.textSecondSize
v.divider2 to (Preferences.textSecondSize - 2),
v.calendar_temp to Preferences.textSecondSize,
v.divider3 to (Preferences.textMainSize - 2),
v.special_temp to Preferences.textMainSize
).forEach {
it.first.setTextSize(TypedValue.COMPLEX_UNIT_SP, it.second)
}
@ -496,6 +565,9 @@ class MainWidget : AppWidgetProvider() {
v.action_previous.scaleX = Preferences.textMainSize / 28f
v.action_previous.scaleY = Preferences.textMainSize / 28f
v.special_weather_icon.scaleX = Preferences.textMainSize / 20f
v.special_weather_icon.scaleY = Preferences.textMainSize / 20f
// Shadows
val shadowRadius = when (Preferences.textShadow) {
@ -517,14 +589,14 @@ class MainWidget : AppWidgetProvider() {
else -> 0f
}
listOf<TextView>(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.next_event_date, v.divider2, v.calendar_temp).forEach {
listOf<TextView>(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.next_event_date, v.divider2, v.calendar_temp, v.divider3, v.special_temp).forEach {
it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor)
}
// Custom Font
if (Preferences.customFont == Constants.CUSTOM_FONT_PRODUCT_SANS) {
val productSans: Typeface = Typeface.createFromAsset(context.assets, "fonts/product_sans_regular.ttf")
listOf<TextView>(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.next_event_date, v.divider2, v.calendar_temp).forEach {
listOf<TextView>(v.empty_date, v.divider1, v.temp, v.next_event, v.next_event_difference_time, v.next_event_date, v.divider2, v.calendar_temp, v.divider3, v.special_temp).forEach {
it.typeface = productSans
}
}
@ -533,24 +605,41 @@ class MainWidget : AppWidgetProvider() {
if (Preferences.showWeather && Preferences.weatherIcon != "") {
v.weather.visibility = View.VISIBLE
v.calendar_weather.visibility = View.VISIBLE
v.special_weather.visibility = View.VISIBLE
val currentTemp = String.format(Locale.getDefault(), "%.0f °%s", Preferences.weatherTemp, Preferences.weatherRealTempUnit)
val icon: String = Preferences.weatherIcon
if (icon == "") {
v.weather_icon.visibility = View.GONE
v.empty_weather_icon.visibility = View.GONE
v.special_weather_icon.visibility = View.GONE
} else {
v.weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(icon))
v.empty_weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(icon))
v.special_weather_icon.setImageResource(WeatherHelper.getWeatherIconResource(icon))
v.weather_icon.visibility = View.VISIBLE
v.empty_weather_icon.visibility = View.VISIBLE
v.special_weather_icon.visibility = View.VISIBLE
}
v.temp.text = currentTemp
v.calendar_temp.text = currentTemp
v.special_temp.text = currentTemp
if (WidgetHelper.showSpecialWeather(context)) {
v.calendar_weather.visibility = View.GONE
} else {
v.special_weather.visibility = View.GONE
}
} else {
v.weather.visibility = View.GONE
v.calendar_weather.visibility = View.GONE
v.special_weather.visibility = View.GONE
}
// Dividers
arrayOf(v.divider1, v.divider2, v.divider3).forEach {
it.isVisible = Preferences.showDividers
}
return v

View File

@ -32,8 +32,8 @@ import java.util.*
fun PackageManager.missingSystemFeature(name: String): Boolean = !hasSystemFeature(name)
fun Context.toast(message: String) {
val toast = Toast.makeText(this, message, Toast.LENGTH_SHORT)
fun Context.toast(message: String, long: Boolean = false) {
val toast = Toast.makeText(this, message, if (long) Toast.LENGTH_LONG else Toast.LENGTH_SHORT)
// toast.setGravity(Gravity.CENTER, 0, 0)
toast.show()
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 689 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

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