Compare commits

..

7 Commits

Author SHA1 Message Date
1ac53e09a8 Merge pull request #312 from chreddy/patch-6
Updating Danish translation
2021-05-07 15:09:55 +02:00
3412e044df Merge pull request #311 from Drumber/translation
Update German strings.xml
2021-05-07 15:09:30 +02:00
80023da430 Updating Danish translation 2021-05-07 15:09:17 +02:00
e2a2d17506 Update German strings.xml 2021-05-07 13:35:38 +02:00
b93443b736 Added right-aligned widget 2021-05-07 12:21:31 +02:00
9842ba3ea9 Bugfixes 2021-05-06 17:29:29 +02:00
75aba66987 Bugfixes 2021-05-05 18:23:01 +02:00
54 changed files with 1888 additions and 1456 deletions

Binary file not shown.

2
.idea/misc.xml generated
View File

@ -61,7 +61,7 @@
</profile-state>
</entry>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@ -3,6 +3,7 @@
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />

View File

@ -22,7 +22,7 @@ android {
applicationId "com.tommasoberlose.anotherwidget"
minSdkVersion 23
targetSdkVersion 30
versionCode 130
versionCode 135
versionName "2.3.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@ -83,7 +83,7 @@ object Constants {
enum class WidgetAlign(val rawValue: Int) {
LEFT(0),
// RIGHT(1),
RIGHT(1),
CENTER(2)
}
}

View File

@ -74,7 +74,11 @@ class TimeZoneSelectorActivity : AppCompatActivity() {
if (id != null) {
Preferences.bulk {
altTimezoneId = id
altTimezoneLabel = item.locality
altTimezoneLabel = try {
item.locality
} catch (ex: Exception) {
item.getAddressLine(0)
}
}
MainWidget.updateWidget(this@TimeZoneSelectorActivity)
setResult(Activity.RESULT_OK)

View File

@ -16,6 +16,7 @@ import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.RelativeLayout
import androidx.core.content.ContextCompat
import androidx.core.view.children
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
@ -183,8 +184,6 @@ class MainFragment : Fragment() {
private var uiJob: Job? = null
private fun updateUI() {
uiJob?.cancel()
if (Preferences.showPreview) {
lifecycleScope.launch(Dispatchers.IO) {
val bgColor: Int = ContextCompat.getColor(
@ -207,28 +206,33 @@ class MainFragment : Fragment() {
}
WidgetHelper.runWithCustomTypeface(requireContext()) { typeface ->
uiJob?.cancel()
uiJob = lifecycleScope.launch(Dispatchers.IO) {
val generatedView = MainWidget.getWidgetView(requireContext(), typeface).root
val generatedView = MainWidget.getWidgetView(requireContext(), typeface)?.root
withContext(Dispatchers.Main) {
generatedView.measure(0, 0)
binding.preview.measure(0, 0)
}
if (generatedView != null) {
withContext(Dispatchers.Main) {
val bitmap = BitmapHelper.getBitmapFromView(
generatedView,
if (binding.preview.width > 0) binding.preview.width else generatedView.measuredWidth,
generatedView.measuredHeight
)
binding.widgetDetail.content.removeAllViews()
val container = LinearLayout(requireContext()).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
}
container.gravity = when (Preferences.widgetAlign) {
Constants.WidgetAlign.CENTER.rawValue -> Gravity.CENTER_HORIZONTAL
Constants.WidgetAlign.LEFT.rawValue -> Gravity.START
Constants.WidgetAlign.RIGHT.rawValue -> Gravity.END
else -> Gravity.NO_GRAVITY
}
container.addView(generatedView)
binding.widgetDetail.content.addView(container)
withContext(Dispatchers.Main) {
binding.widgetDetail.bitmapContainer.apply {
setImageBitmap(bitmap)
binding.widgetLoader.animate().scaleX(0f).scaleY(0f).alpha(0f)
.setDuration(200L).start()
binding.widget.animate().alpha(1f).start()
}
binding.widgetLoader.animate().scaleX(0f).scaleY(0f).alpha(0f)
.setDuration(200L).start()
binding.widget.animate().alpha(1f).start()
}
}
}
@ -287,7 +291,35 @@ class MainFragment : Fragment() {
// Align
binding.widgetDetail.timeContainer.layoutParams = (binding.widgetDetail.timeContainer.layoutParams as LinearLayout.LayoutParams).apply {
gravity = if (Preferences.widgetAlign == Constants.WidgetAlign.CENTER.rawValue) Gravity.CENTER_HORIZONTAL else Gravity.NO_GRAVITY
gravity = when (Preferences.widgetAlign) {
Constants.WidgetAlign.CENTER.rawValue -> Gravity.CENTER_HORIZONTAL
Constants.WidgetAlign.LEFT.rawValue -> Gravity.START
Constants.WidgetAlign.RIGHT.rawValue -> Gravity.END
else -> Gravity.NO_GRAVITY
}
}
if (Preferences.widgetAlign == Constants.WidgetAlign.RIGHT.rawValue) {
with (binding.widgetDetail.timeContainer) {
val child = getChildAt(2)
if (child.id == R.id.timezones_container) {
removeViewAt(2)
child.layoutParams = (child.layoutParams as ViewGroup.MarginLayoutParams).apply {
marginEnd = 16f.convertDpToPixel(requireContext()).toInt()
}
addView(child, 0)
}
}
} else {
with (binding.widgetDetail.timeContainer) {
val child = getChildAt(0)
if (child.id == R.id.timezones_container) {
removeViewAt(0)
child.layoutParams = (child.layoutParams as ViewGroup.MarginLayoutParams).apply {
marginEnd = 0
}
addView(child, 2)
}
}
}
}
@ -310,7 +342,7 @@ class MainFragment : Fragment() {
if (showClock) 0f else 1f,
if (showClock) 1f else 0f
).apply {
duration = 300L
duration = 500L
addUpdateListener {
val animatedValue = animatedValue as Float
binding.widgetDetail.timeContainer.layoutParams =
@ -318,6 +350,10 @@ class MainFragment : Fragment() {
height = (initialHeight * animatedValue).toInt()
}
binding.widgetDetail.time.alpha = animatedValue
binding.widgetDetail.timeAmPm.alpha = animatedValue
binding.widgetDetail.altTimezoneTime.alpha = animatedValue
binding.widgetDetail.altTimezoneTimeAmPm.alpha = animatedValue
binding.widgetDetail.altTimezoneLabel.alpha = animatedValue
}
}.start()
}
@ -334,7 +370,7 @@ class MainFragment : Fragment() {
requireContext()
) else 0)
).apply {
duration = 300L
duration = 500L
addUpdateListener {
val animatedValue = animatedValue as Int
val layoutParams = binding.preview.layoutParams

View File

@ -101,12 +101,14 @@ class LayoutFragment : Fragment() {
maintainScrollPosition {
binding.widgetAlignIcon.setImageDrawable(when (it) {
Constants.WidgetAlign.LEFT.rawValue -> ContextCompat.getDrawable(requireContext(), R.drawable.round_align_horizontal_left_24)
Constants.WidgetAlign.RIGHT.rawValue -> ContextCompat.getDrawable(requireContext(), R.drawable.round_align_horizontal_right_24)
Constants.WidgetAlign.CENTER.rawValue -> ContextCompat.getDrawable(requireContext(), R.drawable.round_align_horizontal_center_24)
else -> ContextCompat.getDrawable(requireContext(), R.drawable.round_align_horizontal_center_24)
})
binding.widgetAlignLabel.text = when (it) {
Constants.WidgetAlign.LEFT.rawValue -> getString(R.string.settings_widget_align_left_subtitle)
Constants.WidgetAlign.RIGHT.rawValue -> getString(R.string.settings_widget_align_right_subtitle)
Constants.WidgetAlign.CENTER.rawValue -> getString(R.string.settings_widget_align_center_subtitle)
else -> getString(R.string.settings_widget_align_center_subtitle)
}
@ -210,6 +212,10 @@ class LayoutFragment : Fragment() {
getString(R.string.settings_widget_align_left_subtitle),
Constants.WidgetAlign.LEFT.rawValue
)
.addItem(
getString(R.string.settings_widget_align_right_subtitle),
Constants.WidgetAlign.RIGHT.rawValue
)
.addOnSelectItemListener { value ->
Preferences.widgetAlign = value
}.show()

View File

@ -0,0 +1,909 @@
package com.tommasoberlose.anotherwidget.ui.widgets
import android.Manifest
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.graphics.Typeface
import android.text.format.DateUtils
import android.util.Log
import android.util.TypedValue
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.view.updateMargins
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.LeftAlignedWidgetBinding
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.ColorHelper.toIntValue
import com.tommasoberlose.anotherwidget.helpers.ImageHelper.applyShadow
import com.tommasoberlose.anotherwidget.receivers.CrashlyticsReceiver
import com.tommasoberlose.anotherwidget.receivers.NewCalendarEventReceiver
import com.tommasoberlose.anotherwidget.receivers.WidgetClickListenerReceiver
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.convertDpToPixel
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
import java.text.DateFormat
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt
class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
fun generateWidget(appWidgetId: Int, w: Int, typeface: Typeface? = null): RemoteViews? {
var views = RemoteViews(context.packageName, if (!rightAligned) R.layout.left_aligned_widget_sans else R.layout.right_aligned_widget_sans)
try {
// Background
views.setInt(
R.id.widget_shape_background,
"setColorFilter",
ColorHelper.getBackgroundColorRgb(context.isDarkTheme())
)
views.setInt(
R.id.widget_shape_background,
"setImageAlpha",
ColorHelper.getBackgroundAlpha(context.isDarkTheme())
)
val refreshIntent = PendingIntent.getActivity(
context,
appWidgetId,
IntentHelper.getWidgetUpdateIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.widget_shape_background, refreshIntent)
} catch (ex: Exception) {
ex.printStackTrace()
CrashlyticsReceiver.sendCrash(context, ex)
}
// Clock
views = ClockWidget(context).updateClockView(views, appWidgetId)
// Setup listener
try {
val generatedBinding = generateWidgetView(typeface) ?: return null
views.setImageViewBitmap(
R.id.bitmap_container,
BitmapHelper.getBitmapFromView(generatedBinding.root, width = w)
)
views = updateGridView(generatedBinding, views, appWidgetId)
} catch (ex: Exception) {
ex.printStackTrace()
CrashlyticsReceiver.sendCrash(context, ex)
}
return views
}
private fun updateGridView(bindingView: LeftAlignedWidgetBinding, views: RemoteViews, widgetID: Int): RemoteViews {
try {
val eventRepository = EventRepository(context)
val nextEvent = eventRepository.getNextEvent()
val eventsCount = eventRepository.getEventsCount()
eventRepository.close()
// Weather
if (Preferences.showWeather && Preferences.weatherIcon != "") {
views.setViewVisibility(R.id.weather_rect, View.VISIBLE)
views.setViewVisibility(R.id.weather_sub_line, View.GONE)
val i = Intent(context, WidgetClickListenerReceiver::class.java)
i.action = Actions.ACTION_OPEN_WEATHER_INTENT
val weatherPIntent = PendingIntent.getBroadcast(context, widgetID, i, 0)
views.setOnClickPendingIntent(R.id.weather_rect, weatherPIntent)
views.setOnClickPendingIntent(R.id.weather_sub_line_rect, weatherPIntent)
views.setImageViewBitmap(
R.id.weather_rect,
BitmapHelper.getBitmapFromView(bindingView.weatherDateLine, draw = false)
)
views.setImageViewBitmap(
R.id.weather_sub_line_rect,
BitmapHelper.getBitmapFromView(bindingView.weatherSubLine, draw = false)
)
} else {
views.setViewVisibility(R.id.weather_rect, View.GONE)
views.setViewVisibility(R.id.weather_sub_line, View.GONE)
}
// Calendar
views.setImageViewBitmap(
R.id.date_rect,
BitmapHelper.getBitmapFromView(bindingView.date, draw = false)
)
val calPIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getCalendarIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.date_rect, calPIntent)
views.setViewVisibility(R.id.first_line_rect, View.VISIBLE)
val nextAlarm = AlarmHelper.getNextAlarm(context)
// Spacing
views.setViewVisibility(
R.id.sub_line_top_margin_small_sans,
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.SMALL.rawValue) View.VISIBLE else View.GONE
)
views.setViewVisibility(
R.id.sub_line_top_margin_medium_sans,
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.MEDIUM.rawValue) View.VISIBLE else View.GONE
)
views.setViewVisibility(
R.id.sub_line_top_margin_large_sans,
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.rawValue) View.VISIBLE else View.GONE
)
if (Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null && !Preferences.showEventsAsGlanceProvider) {
if (Preferences.showNextEvent && eventsCount > 1) {
// Action next event
views.setImageViewBitmap(
R.id.action_next_rect,
BitmapHelper.getBitmapFromView(bindingView.actionNext, draw = false)
)
views.setViewVisibility(R.id.action_next_rect, View.VISIBLE)
views.setOnClickPendingIntent(
R.id.action_next_rect,
PendingIntent.getBroadcast(
context,
widgetID,
Intent(
context,
NewCalendarEventReceiver::class.java
).apply { action = Actions.ACTION_GO_TO_NEXT_EVENT },
PendingIntent.FLAG_UPDATE_CURRENT
)
)
views.setViewVisibility(R.id.action_next_rect, View.VISIBLE)
} else {
views.setViewVisibility(R.id.action_next_rect, View.GONE)
}
// Event intent
val eventIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getEventIntent(context, nextEvent),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.next_event_rect, eventIntent)
views.setViewVisibility(R.id.next_event_rect, View.VISIBLE)
// Event time difference
if (Preferences.showDiffTime && Calendar.getInstance().timeInMillis < nextEvent.startDate) {
views.setImageViewBitmap(
R.id.next_event_difference_time_rect,
BitmapHelper.getBitmapFromView(
bindingView.nextEventDifferenceTime,
draw = false
)
)
views.setOnClickPendingIntent(R.id.next_event_difference_time_rect, eventIntent)
views.setViewVisibility(R.id.next_event_difference_time_rect, View.VISIBLE)
} else {
views.setViewVisibility(R.id.next_event_difference_time_rect, View.GONE)
}
// Event information
if (nextEvent.address != "" && Preferences.secondRowInformation == 1) {
val mapIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getGoogleMapsIntentFromAddress(context, nextEvent.address),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, mapIntent)
} else {
val pIntentDetail = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getEventIntent(
context,
nextEvent,
forceEventDetails = true
),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, pIntentDetail)
}
views.setImageViewBitmap(
R.id.next_event_rect,
BitmapHelper.getBitmapFromView(bindingView.nextEvent, draw = false)
)
views.setViewVisibility(R.id.calendar_layout_rect, View.VISIBLE)
views.setViewVisibility(R.id.calendar_layout_rect, View.VISIBLE)
views.setViewVisibility(R.id.sub_line_rect, View.VISIBLE)
views.setViewVisibility(R.id.weather_sub_line_rect, View.VISIBLE)
views.setViewVisibility(R.id.first_line_rect, View.GONE)
views.setViewVisibility(R.id.sub_line_top_margin_small_sans, View.GONE)
views.setViewVisibility(R.id.sub_line_top_margin_medium_sans, View.GONE)
views.setViewVisibility(R.id.sub_line_top_margin_large_sans, View.GONE)
} else if (GlanceProviderHelper.showGlanceProviders(context)) {
var showSomething = false
loop@ for (provider: Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(context)) {
when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> {
if (MediaPlayerHelper.isSomeonePlaying(context)) {
val musicIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getMusicIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, musicIntent)
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
if (Preferences.showNextAlarm && nextAlarm != "") {
val alarmIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getClockIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, alarmIntent)
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
if (Preferences.showBatteryCharging) {
BatteryHelper.updateBatteryInfo(context)
if (Preferences.isCharging || Preferences.isBatteryLevelLow) {
val batteryIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getBatteryIntent(),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, batteryIntent)
showSomething = true
break@loop
}
}
}
Constants.GlanceProviderId.CUSTOM_INFO -> {
if (Preferences.customNotes.isNotEmpty()) {
break@loop
}
}
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
if (Preferences.showDailySteps && Preferences.googleFitSteps > 0) {
val fitIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getFitIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, fitIntent)
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.NOTIFICATIONS -> {
if (Preferences.showNotifications && ActiveNotificationsHelper.showLastNotification()) {
try {
if (Preferences.lastNotificationIcon != 0) {
val remotePackageContext = context.createPackageContext(
Preferences.lastNotificationPackage, 0)
ContextCompat.getDrawable(
remotePackageContext,
Preferences.lastNotificationIcon)
}
val notificationIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getNotificationIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(
R.id.sub_line_rect,
notificationIntent
)
showSomething = true
break@loop
} catch (ex: Exception) {}
}
}
Constants.GlanceProviderId.GREETINGS -> {
if (Preferences.showGreetings && GreetingsHelper.showGreetings() && GreetingsHelper.getRandomString(context).isNotBlank()) {
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.EVENTS -> {
if (Preferences.showEventsAsGlanceProvider&& Preferences.showEvents && context.checkGrantedPermission(
Manifest.permission.READ_CALENDAR) && nextEvent != null) {
val pIntentDetail = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getEventIntent(
context,
nextEvent,
forceEventDetails = true
),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(
R.id.sub_line_rect,
pIntentDetail
)
showSomething = true
break@loop
}
}
}
}
if (showSomething) {
views.setViewVisibility(R.id.first_line_rect, View.VISIBLE)
views.setViewVisibility(R.id.weather_rect, View.VISIBLE)
views.setViewVisibility(R.id.sub_line_rect, View.VISIBLE)
views.setViewVisibility(R.id.calendar_layout_rect, View.GONE)
views.setViewVisibility(R.id.weather_sub_line_rect, View.GONE)
} else {
// Spacing
views.setViewVisibility(R.id.sub_line_top_margin_small_sans, View.GONE)
views.setViewVisibility(R.id.sub_line_top_margin_medium_sans, View.GONE)
views.setViewVisibility(R.id.sub_line_top_margin_large_sans, View.GONE)
}
}
// Second row
views.setImageViewBitmap(
R.id.sub_line_rect,
BitmapHelper.getBitmapFromView(bindingView.subLine, draw = false)
)
} catch (ex: Exception) {
ex.printStackTrace()
CrashlyticsReceiver.sendCrash(context, ex)
}
return views
}
// Generates the widget bitmap from the view
fun generateWidgetView(typeface: Typeface? = null): LeftAlignedWidgetBinding? {
try {
val eventRepository = EventRepository(context)
val nextEvent = eventRepository.getNextEvent()
val eventsCount = eventRepository.getEventsCount()
eventRepository.close()
val bindingView = LeftAlignedWidgetBinding.inflate(LayoutInflater.from(context))
bindingView.loader.isVisible = false
// Weather
if (Preferences.showWeather && Preferences.weatherIcon != "") {
bindingView.weatherDateLine.isVisible = true
val currentTemp = String.format(
Locale.getDefault(),
"%d°%s",
Preferences.weatherTemp.roundToInt(),
Preferences.weatherRealTempUnit
)
val icon: String = Preferences.weatherIcon
if (icon == "") {
bindingView.weatherSubLineWeatherIcon.isVisible = false
bindingView.weatherDateLineWeatherIcon.isVisible = false
} else {
bindingView.weatherSubLineWeatherIcon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon))
bindingView.weatherDateLineWeatherIcon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon))
bindingView.weatherSubLineWeatherIcon.isVisible = true
bindingView.weatherDateLineWeatherIcon.isVisible = true
}
bindingView.weatherDateLineTemperature.text = currentTemp
bindingView.weatherSubLineTemperature.text = currentTemp
if (GlanceProviderHelper.showGlanceProviders(context)) {
bindingView.weatherSubLine.isVisible = false
}
} else {
bindingView.weatherDateLine.isVisible = false
bindingView.weatherSubLine.isVisible = false
}
val now = Calendar.getInstance().apply {
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
bindingView.dateLayout.isVisible = true
bindingView.calendarLayout.isVisible = false
bindingView.nextEventDifferenceTime.isVisible = false
bindingView.actionNext.isVisible = false
bindingView.date.text = DateHelper.getDateText(context, now)
val nextAlarm = AlarmHelper.getNextAlarm(context)
if (Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null && !Preferences.showEventsAsGlanceProvider) {
// Multiple counter
bindingView.actionNext.isVisible =
Preferences.showNextEvent && eventsCount > 1
bindingView.nextEvent.text = nextEvent.title
if (Preferences.showDiffTime && now.timeInMillis < nextEvent.startDate) {
bindingView.nextEventDifferenceTime.text = if (!nextEvent.allDay) {
SettingsStringHelper.getDifferenceText(
context,
now.timeInMillis,
nextEvent.startDate
)
.toLowerCase(Locale.getDefault())
} else {
SettingsStringHelper.getAllDayEventDifferenceText(
context,
now.timeInMillis,
nextEvent.startDate
).toLowerCase(Locale.getDefault())
}
bindingView.nextEventDifferenceTime.isVisible = true
} else {
bindingView.nextEventDifferenceTime.isVisible = false
}
if (nextEvent.address != "" && Preferences.secondRowInformation == 1) {
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_place_24
)
)
bindingView.subLineText.text = nextEvent.address
} else {
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_today_24
)
)
if (!nextEvent.allDay) {
val startHour =
DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault())
.format(nextEvent.startDate)
val endHour =
DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault())
.format(nextEvent.endDate)
var dayDiff =
TimeUnit.MILLISECONDS.toDays(nextEvent.endDate - nextEvent.startDate)
val startCal = Calendar.getInstance()
startCal.timeInMillis = nextEvent.startDate
val endCal = Calendar.getInstance()
endCal.timeInMillis = nextEvent.endDate
if (startCal.get(Calendar.HOUR_OF_DAY) > endCal.get(Calendar.HOUR_OF_DAY)) {
dayDiff++
} else if (startCal.get(Calendar.HOUR_OF_DAY) == endCal.get(Calendar.HOUR_OF_DAY) && startCal.get(
Calendar.MINUTE
) > endCal.get(Calendar.MINUTE)
) {
dayDiff++
}
var multipleDay = ""
if (dayDiff > 0) {
multipleDay = String.format(
" (+%s%s)",
dayDiff,
context.getString(R.string.day_char)
)
}
if (nextEvent.startDate != nextEvent.endDate) {
bindingView.subLineText.text =
String.format("%s - %s%s", startHour, endHour, multipleDay)
} else {
bindingView.subLineText.text =
String.format("%s", startHour)
}
} else {
val flags: Int =
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_ABBREV_MONTH
val start = Calendar.getInstance().apply { timeInMillis = nextEvent.startDate }
bindingView.subLineText.text = if (now.get(Calendar.DAY_OF_YEAR) == start.get(
Calendar.DAY_OF_YEAR)) {
DateUtils.formatDateTime(context, nextEvent.startDate, flags)
} else if (now.get(Calendar.DAY_OF_YEAR) > start.get(Calendar.DAY_OF_YEAR) || now.get(
Calendar.YEAR) > start.get(Calendar.YEAR)) {
DateUtils.formatDateTime(context, now.timeInMillis, flags)
} else {
DateUtils.formatDateTime(context, nextEvent.startDate, flags)
}
}
}
bindingView.dateLayout.isVisible = false
bindingView.calendarLayout.isVisible = true
bindingView.subLine.isVisible = true
bindingView.weatherSubLine.isVisible = true
bindingView.subLineTopMarginSmall.visibility = View.GONE
bindingView.subLineTopMarginMedium.visibility = View.GONE
bindingView.subLineTopMarginLarge.visibility = View.GONE
} else if (GlanceProviderHelper.showGlanceProviders(context)) {
bindingView.subLineIcon.isVisible = true
var showSomething = false
loop@ for (provider: Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(
context
)) {
when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> {
if (MediaPlayerHelper.isSomeonePlaying(context)) {
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_music_note_24
)
)
bindingView.subLineText.text = MediaPlayerHelper.getMediaInfo()
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
if (Preferences.showNextAlarm && nextAlarm != "") {
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_alarm_24
)
)
bindingView.subLineText.text = AlarmHelper.getNextAlarm(context)
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
if (Preferences.showBatteryCharging) {
BatteryHelper.updateBatteryInfo(context)
if (Preferences.isCharging) {
bindingView.subLineIcon.isVisible = false
val batteryLevel = BatteryHelper.getBatteryLevel(context)
if (batteryLevel != 100) {
bindingView.subLineText.text = context.getString(R.string.charging)
} else {
bindingView.subLineText.text =
context.getString(R.string.charged)
}
showSomething = true
break@loop
} else if (Preferences.isBatteryLevelLow) {
bindingView.subLineIcon.isVisible = false
bindingView.subLineText.text =
context.getString(R.string.battery_low_warning)
showSomething = true
break@loop
}
}
}
Constants.GlanceProviderId.CUSTOM_INFO -> {
if (Preferences.customNotes.isNotEmpty()) {
bindingView.subLineIcon.isVisible = false
bindingView.subLineText.text = Preferences.customNotes
bindingView.subLineText.maxLines = 2
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
if (Preferences.showDailySteps && Preferences.googleFitSteps > 0) {
bindingView.subLineIcon.isVisible = false
bindingView.subLineText.text =
context.getString(R.string.daily_steps_counter)
.format(Preferences.googleFitSteps)
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.NOTIFICATIONS -> {
if (Preferences.showNotifications && ActiveNotificationsHelper.showLastNotification()) {
try {
if (Preferences.lastNotificationIcon != 0) {
val remotePackageContext = context.createPackageContext(
Preferences.lastNotificationPackage, 0)
val icon = ContextCompat.getDrawable(remotePackageContext,
Preferences.lastNotificationIcon)
bindingView.subLineIcon.isVisible = true
bindingView.subLineIcon.setImageDrawable(icon)
} else {
bindingView.subLineIcon.isVisible = false
}
bindingView.subLineText.text = Preferences.lastNotificationTitle
showSomething = true
break@loop
} catch (ex: Exception) {}
}
}
Constants.GlanceProviderId.GREETINGS -> {
val greetingsText = GreetingsHelper.getRandomString(context)
if (Preferences.showGreetings && GreetingsHelper.showGreetings() && greetingsText.isNotBlank()) {
bindingView.subLineText.text = greetingsText
bindingView.subLineText.maxLines = 2
bindingView.subLineIcon.isVisible = false
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.EVENTS -> {
if (Preferences.showEventsAsGlanceProvider && Preferences.showEvents && context.checkGrantedPermission(
Manifest.permission.READ_CALENDAR) && nextEvent != null) {
bindingView.subLineText.text = context.getString(R.string.events_glance_provider_format).format(nextEvent.title, if (Preferences.showDiffTime && now.timeInMillis < nextEvent.startDate) {
if (!nextEvent.allDay) {
SettingsStringHelper.getDifferenceText(
context,
now.timeInMillis,
nextEvent.startDate
)
.toLowerCase(Locale.getDefault())
} else {
SettingsStringHelper.getAllDayEventDifferenceText(
context,
now.timeInMillis,
nextEvent.startDate
).toLowerCase(Locale.getDefault())
}
} else "").trimEnd()
bindingView.subLineIcon.isVisible = true
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_today_24
)
)
showSomething = true
break@loop
}
}
}
}
if (showSomething) {
bindingView.dateLayout.isVisible = true
bindingView.calendarLayout.isVisible = false
bindingView.subLine.isVisible = true
bindingView.weatherSubLine.isVisible = false
bindingView.subLineTopMarginSmall.visibility =
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.SMALL.rawValue) View.VISIBLE else View.GONE
bindingView.subLineTopMarginMedium.visibility =
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.MEDIUM.rawValue) View.VISIBLE else View.GONE
bindingView.subLineTopMarginLarge.visibility =
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.rawValue) View.VISIBLE else View.GONE
} else {
bindingView.subLineIcon.isVisible = false
}
}
// Color
listOf<TextView>(
bindingView.date,
bindingView.weatherDateLineTemperature,
bindingView.nextEvent,
bindingView.nextEventDifferenceTime,
).forEach {
it.setTextColor(ColorHelper.getFontColor(context.applicationContext.isDarkTheme()))
}
if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.rawValue) {
listOf<ImageView>(bindingView.actionNext)
} else {
listOf<ImageView>(
bindingView.actionNext,
bindingView.weatherDateLineWeatherIcon,
bindingView.weatherSubLineWeatherIcon
)
}.forEach {
it.setColorFilter(ColorHelper.getFontColorRgb(context.applicationContext.isDarkTheme()))
it.alpha =
(if (context.isDarkTheme()) Preferences.textGlobalAlphaDark.toIntValue()
.toFloat() else Preferences.textGlobalAlpha.toIntValue()
.toFloat()) / 100
}
listOf<TextView>(bindingView.subLineText, bindingView.weatherSubLineDivider, bindingView.weatherSubLineTemperature).forEach {
it.setTextColor(ColorHelper.getSecondaryFontColor(context.applicationContext.isDarkTheme()))
}
if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.rawValue) {
listOf<ImageView>(bindingView.subLineIcon, bindingView.subLineIconShadow)
} else {
listOf<ImageView>(bindingView.subLineIcon, bindingView.weatherSubLineWeatherIcon, bindingView.subLineIconShadow)
}.forEach {
it.setColorFilter(ColorHelper.getSecondaryFontColorRgb(context.applicationContext.isDarkTheme()))
it.alpha =
(if (context.isDarkTheme()) Preferences.textSecondaryAlphaDark.toIntValue()
.toFloat() else Preferences.textSecondaryAlpha.toIntValue()
.toFloat()) / 100
}
// Text Size
listOf<Pair<TextView, Float>>(
bindingView.date to Preferences.textMainSize,
bindingView.weatherDateLineTemperature to ((Preferences.textMainSize + Preferences.textSecondSize) / 2),
bindingView.nextEvent to Preferences.textMainSize,
bindingView.nextEventDifferenceTime to Preferences.textMainSize,
bindingView.subLineText to Preferences.textSecondSize,
bindingView.weatherSubLineDivider to (Preferences.textSecondSize - 2),
bindingView.weatherSubLineTemperature to Preferences.textSecondSize,
).forEach {
it.first.setTextSize(TypedValue.COMPLEX_UNIT_SP, it.second)
}
// Icons scale
bindingView.subLineIcon.scaleX = Preferences.textSecondSize / 18f
bindingView.subLineIcon.scaleY = Preferences.textSecondSize / 18f
bindingView.weatherSubLineWeatherIcon.scaleX = Preferences.textSecondSize / 18f
bindingView.weatherSubLineWeatherIcon.scaleY = Preferences.textSecondSize / 18f
bindingView.weatherDateLineWeatherIcon.scaleX = ((Preferences.textMainSize + Preferences.textSecondSize) / 2) / 20f
bindingView.weatherDateLineWeatherIcon.scaleY = ((Preferences.textMainSize + Preferences.textSecondSize) / 2) / 20f
bindingView.actionNext.scaleX = Preferences.textMainSize / 28f
bindingView.actionNext.scaleY = Preferences.textMainSize / 28f
// Shadows
val shadowRadius =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> 0f
1 -> 5f
2 -> 5f
else -> 5f
}
val shadowColor =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> Color.TRANSPARENT
1 -> R.color.black_50
2 -> Color.BLACK
else -> R.color.black_50
}
val shadowDy =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> 0f
1 -> 0f
2 -> 1f
else -> 0f
}
listOf<TextView>(
bindingView.date,
bindingView.weatherDateLineTemperature,
bindingView.nextEvent,
bindingView.nextEventDifferenceTime,
bindingView.subLineText,
bindingView.weatherSubLineDivider,
bindingView.weatherSubLineTemperature,
).forEach {
it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor)
}
// Icons shadow
listOf(
Pair(bindingView.subLineIcon, bindingView.subLineIconShadow),
).forEach {
if ((if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) == 0) {
it.second.isVisible = false
} else {
it.second.isVisible = it.first.isVisible
it.second.scaleX = it.first.scaleX
it.second.scaleY = it.first.scaleY
it.second.applyShadow(it.first)
}
}
listOf(
Pair(bindingView.actionNext, bindingView.actionNextShadow),
).forEach {
if ((if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) == 0) {
it.second.isVisible = false
} else {
it.second.isVisible = it.first.isVisible
it.second.scaleX = it.first.scaleX
it.second.scaleY = it.first.scaleY
it.second.applyShadow(it.first, 0.6f)
}
}
// Custom Font
if (Preferences.customFont == Constants.CUSTOM_FONT_GOOGLE_SANS) {
val googleSans: Typeface = when (Preferences.customFontVariant) {
"100" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_thin.ttf")
"200" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_light.ttf")
"500" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_medium.ttf")
"700" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_bold.ttf")
"800" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_black.ttf")
else -> Typeface.createFromAsset(context.assets, "fonts/google_sans_regular.ttf")
}
listOf<TextView>(
bindingView.date,
bindingView.weatherDateLineTemperature,
bindingView.nextEvent,
bindingView.nextEventDifferenceTime,
bindingView.subLineText,
bindingView.weatherSubLineDivider,
bindingView.weatherSubLineTemperature,
).forEach {
it.typeface = googleSans
}
} else if (Preferences.customFont == Constants.CUSTOM_FONT_DOWNLOADED && typeface != null) {
listOf<TextView>(
bindingView.date,
bindingView.weatherDateLineTemperature,
bindingView.nextEvent,
bindingView.nextEventDifferenceTime,
bindingView.subLineText,
bindingView.weatherSubLineDivider,
bindingView.weatherSubLineTemperature,
).forEach {
it.typeface = typeface
}
}
// Dividers
arrayOf(bindingView.weatherSubLineDivider).forEach {
it.visibility = if (Preferences.showDividers) View.VISIBLE else View.INVISIBLE
it.layoutParams = (it.layoutParams as ViewGroup.MarginLayoutParams).apply {
this.marginEnd = if (Preferences.showDividers) 8f.convertDpToPixel(context).toInt() else 0
}
}
// Right Aligned
if (rightAligned) {
bindingView.mainContent.layoutParams = (bindingView.mainContent.layoutParams as RelativeLayout.LayoutParams).apply {
addRule(RelativeLayout.ALIGN_PARENT_END)
}
bindingView.mainContent.gravity = Gravity.END
bindingView.dateLayout.gravity = Gravity.END
bindingView.calendarLayout.gravity = Gravity.END or Gravity.CENTER_VERTICAL
bindingView.subLineContainer.gravity = Gravity.END or Gravity.CENTER_VERTICAL
}
return bindingView
} catch (ex: Exception) {
ex.printStackTrace()
CrashlyticsReceiver.sendCrash(context, ex)
return null
}
}
}

View File

@ -3,6 +3,7 @@ package com.tommasoberlose.anotherwidget.ui.widgets
import android.app.PendingIntent
import android.content.Context
import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.widget.RemoteViews
import com.tommasoberlose.anotherwidget.R
@ -24,6 +25,7 @@ class ClockWidget(val context: Context) {
views.setViewVisibility(R.id.clock_bottom_margin_small, View.GONE)
views.setViewVisibility(R.id.clock_bottom_margin_medium, View.GONE)
views.setViewVisibility(R.id.clock_bottom_margin_large, View.GONE)
views.setViewVisibility(R.id.timezones_container, View.GONE)
} else {
views.setTextColor(R.id.time, ColorHelper.getClockFontColor(context.isDarkTheme()))
views.setTextColor(R.id.time_am_pm, ColorHelper.getClockFontColor(context.isDarkTheme()))

View File

@ -1,890 +0,0 @@
package com.tommasoberlose.anotherwidget.ui.widgets
import android.Manifest
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.graphics.Typeface
import android.text.format.DateUtils
import android.util.Log
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.RemoteViews
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.view.updateMargins
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.LeftAlignedWidgetBinding
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.ColorHelper.toIntValue
import com.tommasoberlose.anotherwidget.helpers.ImageHelper.applyShadow
import com.tommasoberlose.anotherwidget.receivers.CrashlyticsReceiver
import com.tommasoberlose.anotherwidget.receivers.NewCalendarEventReceiver
import com.tommasoberlose.anotherwidget.receivers.WidgetClickListenerReceiver
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.convertDpToPixel
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
import java.text.DateFormat
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt
class LeftAlignedWidget(val context: Context) {
fun generateWidget(appWidgetId: Int, w: Int, typeface: Typeface? = null): RemoteViews {
var views = RemoteViews(context.packageName, R.layout.left_aligned_widget_sans)
try {
// Background
views.setInt(
R.id.widget_shape_background,
"setColorFilter",
ColorHelper.getBackgroundColorRgb(context.isDarkTheme())
)
views.setInt(
R.id.widget_shape_background,
"setImageAlpha",
ColorHelper.getBackgroundAlpha(context.isDarkTheme())
)
val refreshIntent = PendingIntent.getActivity(
context,
appWidgetId,
IntentHelper.getWidgetUpdateIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.widget_shape_background, refreshIntent)
} catch (ex: Exception) {
ex.printStackTrace()
CrashlyticsReceiver.sendCrash(context, ex)
}
// Clock
views = ClockWidget(context).updateClockView(views, appWidgetId)
// Setup listener
try {
val generatedBinding = generateWidgetView(typeface)
views.setImageViewBitmap(
R.id.bitmap_container,
BitmapHelper.getBitmapFromView(generatedBinding.root, width = w)
)
views = updateGridView(generatedBinding, views, appWidgetId)
} catch (ex: Exception) {
ex.printStackTrace()
CrashlyticsReceiver.sendCrash(context, ex)
}
return views
}
private fun updateGridView(bindingView: LeftAlignedWidgetBinding, views: RemoteViews, widgetID: Int): RemoteViews {
val eventRepository = EventRepository(context)
try {
// Weather
if (Preferences.showWeather && Preferences.weatherIcon != "") {
views.setViewVisibility(R.id.weather_rect, View.VISIBLE)
views.setViewVisibility(R.id.weather_sub_line, View.GONE)
val i = Intent(context, WidgetClickListenerReceiver::class.java)
i.action = Actions.ACTION_OPEN_WEATHER_INTENT
val weatherPIntent = PendingIntent.getBroadcast(context, widgetID, i, 0)
views.setOnClickPendingIntent(R.id.weather_rect, weatherPIntent)
views.setOnClickPendingIntent(R.id.weather_sub_line_rect, weatherPIntent)
views.setImageViewBitmap(
R.id.weather_rect,
BitmapHelper.getBitmapFromView(bindingView.weatherDateLine, draw = false)
)
views.setImageViewBitmap(
R.id.weather_sub_line_rect,
BitmapHelper.getBitmapFromView(bindingView.weatherSubLine, draw = false)
)
} else {
views.setViewVisibility(R.id.weather_rect, View.GONE)
views.setViewVisibility(R.id.weather_sub_line, View.GONE)
}
// Calendar
views.setImageViewBitmap(
R.id.date_rect,
BitmapHelper.getBitmapFromView(bindingView.date, draw = false)
)
val calPIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getCalendarIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.date_rect, calPIntent)
views.setViewVisibility(R.id.first_line_rect, View.VISIBLE)
val nextEvent = eventRepository.getNextEvent()
val nextAlarm = AlarmHelper.getNextAlarm(context)
// Spacing
views.setViewVisibility(
R.id.sub_line_top_margin_small_sans,
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.SMALL.rawValue) View.VISIBLE else View.GONE
)
views.setViewVisibility(
R.id.sub_line_top_margin_medium_sans,
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.MEDIUM.rawValue) View.VISIBLE else View.GONE
)
views.setViewVisibility(
R.id.sub_line_top_margin_large_sans,
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.rawValue) View.VISIBLE else View.GONE
)
if (Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null && !Preferences.showEventsAsGlanceProvider) {
if (Preferences.showNextEvent && eventRepository.getEventsCount() > 1) {
// Action next event
views.setImageViewBitmap(
R.id.action_next_rect,
BitmapHelper.getBitmapFromView(bindingView.actionNext, draw = false)
)
views.setViewVisibility(R.id.action_next_rect, View.VISIBLE)
views.setOnClickPendingIntent(
R.id.action_next_rect,
PendingIntent.getBroadcast(
context,
widgetID,
Intent(
context,
NewCalendarEventReceiver::class.java
).apply { action = Actions.ACTION_GO_TO_NEXT_EVENT },
PendingIntent.FLAG_UPDATE_CURRENT
)
)
views.setViewVisibility(R.id.action_next_rect, View.VISIBLE)
} else {
views.setViewVisibility(R.id.action_next_rect, View.GONE)
}
// Event intent
val eventIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getEventIntent(context, nextEvent),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.next_event_rect, eventIntent)
views.setViewVisibility(R.id.next_event_rect, View.VISIBLE)
// Event time difference
if (Preferences.showDiffTime && Calendar.getInstance().timeInMillis < nextEvent.startDate) {
views.setImageViewBitmap(
R.id.next_event_difference_time_rect,
BitmapHelper.getBitmapFromView(
bindingView.nextEventDifferenceTime,
draw = false
)
)
views.setOnClickPendingIntent(R.id.next_event_difference_time_rect, eventIntent)
views.setViewVisibility(R.id.next_event_difference_time_rect, View.VISIBLE)
} else {
views.setViewVisibility(R.id.next_event_difference_time_rect, View.GONE)
}
// Event information
if (nextEvent.address != "" && Preferences.secondRowInformation == 1) {
val mapIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getGoogleMapsIntentFromAddress(context, nextEvent.address),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, mapIntent)
} else {
val pIntentDetail = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getEventIntent(
context,
nextEvent,
forceEventDetails = true
),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, pIntentDetail)
}
views.setImageViewBitmap(
R.id.next_event_rect,
BitmapHelper.getBitmapFromView(bindingView.nextEvent, draw = false)
)
views.setViewVisibility(R.id.calendar_layout_rect, View.VISIBLE)
views.setViewVisibility(R.id.calendar_layout_rect, View.VISIBLE)
views.setViewVisibility(R.id.sub_line_rect, View.VISIBLE)
views.setViewVisibility(R.id.weather_sub_line_rect, View.VISIBLE)
views.setViewVisibility(R.id.first_line_rect, View.GONE)
views.setViewVisibility(R.id.sub_line_top_margin_small_sans, View.GONE)
views.setViewVisibility(R.id.sub_line_top_margin_medium_sans, View.GONE)
views.setViewVisibility(R.id.sub_line_top_margin_large_sans, View.GONE)
} else if (GlanceProviderHelper.showGlanceProviders(context)) {
var showSomething = false
loop@ for (provider: Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(context)) {
when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> {
if (MediaPlayerHelper.isSomeonePlaying(context)) {
val musicIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getMusicIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, musicIntent)
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
if (Preferences.showNextAlarm && nextAlarm != "") {
val alarmIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getClockIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, alarmIntent)
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
if (Preferences.showBatteryCharging) {
BatteryHelper.updateBatteryInfo(context)
if (Preferences.isCharging || Preferences.isBatteryLevelLow) {
val batteryIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getBatteryIntent(),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, batteryIntent)
showSomething = true
break@loop
}
}
}
Constants.GlanceProviderId.CUSTOM_INFO -> {
if (Preferences.customNotes.isNotEmpty()) {
break@loop
}
}
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
if (Preferences.showDailySteps && Preferences.googleFitSteps > 0) {
val fitIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getFitIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(R.id.sub_line_rect, fitIntent)
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.NOTIFICATIONS -> {
if (Preferences.showNotifications && ActiveNotificationsHelper.showLastNotification()) {
try {
if (Preferences.lastNotificationIcon != 0) {
val remotePackageContext = context.createPackageContext(
Preferences.lastNotificationPackage, 0)
ContextCompat.getDrawable(
remotePackageContext,
Preferences.lastNotificationIcon)
}
val notificationIntent = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getNotificationIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(
R.id.sub_line_rect,
notificationIntent
)
showSomething = true
break@loop
} catch (ex: Exception) {}
}
}
Constants.GlanceProviderId.GREETINGS -> {
if (Preferences.showGreetings && GreetingsHelper.showGreetings() && GreetingsHelper.getRandomString(context).isNotBlank()) {
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.EVENTS -> {
if (Preferences.showEventsAsGlanceProvider&& Preferences.showEvents && context.checkGrantedPermission(
Manifest.permission.READ_CALENDAR) && nextEvent != null) {
val pIntentDetail = PendingIntent.getActivity(
context,
widgetID,
IntentHelper.getEventIntent(
context,
nextEvent,
forceEventDetails = true
),
PendingIntent.FLAG_UPDATE_CURRENT
)
views.setOnClickPendingIntent(
R.id.sub_line_rect,
pIntentDetail
)
showSomething = true
break@loop
}
}
}
}
if (showSomething) {
views.setViewVisibility(R.id.first_line_rect, View.VISIBLE)
views.setViewVisibility(R.id.weather_rect, View.VISIBLE)
views.setViewVisibility(R.id.sub_line_rect, View.VISIBLE)
views.setViewVisibility(R.id.calendar_layout_rect, View.GONE)
views.setViewVisibility(R.id.weather_sub_line_rect, View.GONE)
} else {
// Spacing
views.setViewVisibility(R.id.sub_line_top_margin_small_sans, View.GONE)
views.setViewVisibility(R.id.sub_line_top_margin_medium_sans, View.GONE)
views.setViewVisibility(R.id.sub_line_top_margin_large_sans, View.GONE)
}
}
// Second row
views.setImageViewBitmap(
R.id.sub_line_rect,
BitmapHelper.getBitmapFromView(bindingView.subLine, draw = false)
)
} catch (ex: Exception) {
ex.printStackTrace()
CrashlyticsReceiver.sendCrash(context, ex)
} finally {
eventRepository.close()
}
return views
}
// Generates the widget bitmap from the view
fun generateWidgetView(typeface: Typeface? = null): LeftAlignedWidgetBinding {
val eventRepository = EventRepository(context)
val bindingView = LeftAlignedWidgetBinding.inflate(LayoutInflater.from(context))
bindingView.loader.isVisible = false
// Weather
if (Preferences.showWeather && Preferences.weatherIcon != "") {
bindingView.weatherDateLine.isVisible = true
val currentTemp = String.format(
Locale.getDefault(),
"%d°%s",
Preferences.weatherTemp.roundToInt(),
Preferences.weatherRealTempUnit
)
val icon: String = Preferences.weatherIcon
if (icon == "") {
bindingView.weatherSubLineWeatherIcon.isVisible = false
bindingView.weatherDateLineWeatherIcon.isVisible = false
} else {
bindingView.weatherSubLineWeatherIcon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon))
bindingView.weatherDateLineWeatherIcon.setImageResource(WeatherHelper.getWeatherIconResource(context, icon))
bindingView.weatherSubLineWeatherIcon.isVisible = true
bindingView.weatherDateLineWeatherIcon.isVisible = true
}
bindingView.weatherDateLineTemperature.text = currentTemp
bindingView.weatherSubLineTemperature.text = currentTemp
if (GlanceProviderHelper.showGlanceProviders(context)) {
bindingView.weatherSubLine.isVisible = false
}
} else {
bindingView.weatherDateLine.isVisible = false
bindingView.weatherSubLine.isVisible = false
}
val now = Calendar.getInstance().apply {
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
bindingView.dateLayout.isVisible = true
bindingView.calendarLayout.isVisible = false
bindingView.nextEventDifferenceTime.isVisible = false
bindingView.actionNext.isVisible = false
bindingView.date.text = DateHelper.getDateText(context, now)
val nextEvent = eventRepository.getNextEvent()
val nextAlarm = AlarmHelper.getNextAlarm(context)
if (Preferences.showEvents && context.checkGrantedPermission(Manifest.permission.READ_CALENDAR) && nextEvent != null && !Preferences.showEventsAsGlanceProvider) {
// Multiple counter
bindingView.actionNext.isVisible =
Preferences.showNextEvent && eventRepository.getEventsCount() > 1
bindingView.nextEvent.text = nextEvent.title
if (Preferences.showDiffTime && now.timeInMillis < nextEvent.startDate) {
bindingView.nextEventDifferenceTime.text = if (!nextEvent.allDay) {
SettingsStringHelper.getDifferenceText(
context,
now.timeInMillis,
nextEvent.startDate
)
.toLowerCase(Locale.getDefault())
} else {
SettingsStringHelper.getAllDayEventDifferenceText(
context,
now.timeInMillis,
nextEvent.startDate
).toLowerCase(Locale.getDefault())
}
bindingView.nextEventDifferenceTime.isVisible = true
} else {
bindingView.nextEventDifferenceTime.isVisible = false
}
if (nextEvent.address != "" && Preferences.secondRowInformation == 1) {
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_place_24
)
)
bindingView.subLineText.text = nextEvent.address
} else {
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_today_24
)
)
if (!nextEvent.allDay) {
val startHour =
DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault())
.format(nextEvent.startDate)
val endHour =
DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault())
.format(nextEvent.endDate)
var dayDiff =
TimeUnit.MILLISECONDS.toDays(nextEvent.endDate - nextEvent.startDate)
val startCal = Calendar.getInstance()
startCal.timeInMillis = nextEvent.startDate
val endCal = Calendar.getInstance()
endCal.timeInMillis = nextEvent.endDate
if (startCal.get(Calendar.HOUR_OF_DAY) > endCal.get(Calendar.HOUR_OF_DAY)) {
dayDiff++
} else if (startCal.get(Calendar.HOUR_OF_DAY) == endCal.get(Calendar.HOUR_OF_DAY) && startCal.get(
Calendar.MINUTE
) > endCal.get(Calendar.MINUTE)
) {
dayDiff++
}
var multipleDay = ""
if (dayDiff > 0) {
multipleDay = String.format(
" (+%s%s)",
dayDiff,
context.getString(R.string.day_char)
)
}
if (nextEvent.startDate != nextEvent.endDate) {
bindingView.subLineText.text =
String.format("%s - %s%s", startHour, endHour, multipleDay)
} else {
bindingView.subLineText.text =
String.format("%s", startHour)
}
} else {
val flags: Int =
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR or DateUtils.FORMAT_ABBREV_MONTH
val start = Calendar.getInstance().apply { timeInMillis = nextEvent.startDate }
bindingView.subLineText.text = if (now.get(Calendar.DAY_OF_YEAR) == start.get(
Calendar.DAY_OF_YEAR)) {
DateUtils.formatDateTime(context, nextEvent.startDate, flags)
} else if (now.get(Calendar.DAY_OF_YEAR) > start.get(Calendar.DAY_OF_YEAR) || now.get(
Calendar.YEAR) > start.get(Calendar.YEAR)) {
DateUtils.formatDateTime(context, now.timeInMillis, flags)
} else {
DateUtils.formatDateTime(context, nextEvent.startDate, flags)
}
}
}
bindingView.dateLayout.isVisible = false
bindingView.calendarLayout.isVisible = true
bindingView.subLine.isVisible = true
bindingView.weatherSubLine.isVisible = true
bindingView.subLineTopMarginSmall.visibility = View.GONE
bindingView.subLineTopMarginMedium.visibility = View.GONE
bindingView.subLineTopMarginLarge.visibility = View.GONE
} else if (GlanceProviderHelper.showGlanceProviders(context)) {
bindingView.subLineIcon.isVisible = true
var showSomething = false
loop@ for (provider: Constants.GlanceProviderId in GlanceProviderHelper.getGlanceProviders(
context
)) {
when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> {
if (MediaPlayerHelper.isSomeonePlaying(context)) {
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_music_note_24
)
)
bindingView.subLineText.text = MediaPlayerHelper.getMediaInfo()
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.NEXT_CLOCK_ALARM -> {
if (Preferences.showNextAlarm && nextAlarm != "") {
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_alarm_24
)
)
bindingView.subLineText.text = AlarmHelper.getNextAlarm(context)
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.BATTERY_LEVEL_LOW -> {
if (Preferences.showBatteryCharging) {
BatteryHelper.updateBatteryInfo(context)
if (Preferences.isCharging) {
bindingView.subLineIcon.isVisible = false
val batteryLevel = BatteryHelper.getBatteryLevel(context)
if (batteryLevel != 100) {
bindingView.subLineText.text = context.getString(R.string.charging)
} else {
bindingView.subLineText.text =
context.getString(R.string.charged)
}
showSomething = true
break@loop
} else if (Preferences.isBatteryLevelLow) {
bindingView.subLineIcon.isVisible = false
bindingView.subLineText.text =
context.getString(R.string.battery_low_warning)
showSomething = true
break@loop
}
}
}
Constants.GlanceProviderId.CUSTOM_INFO -> {
if (Preferences.customNotes.isNotEmpty()) {
bindingView.subLineIcon.isVisible = false
bindingView.subLineText.text = Preferences.customNotes
bindingView.subLineText.maxLines = 2
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.GOOGLE_FIT_STEPS -> {
if (Preferences.showDailySteps && Preferences.googleFitSteps > 0) {
bindingView.subLineIcon.isVisible = false
bindingView.subLineText.text =
context.getString(R.string.daily_steps_counter)
.format(Preferences.googleFitSteps)
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.NOTIFICATIONS -> {
if (Preferences.showNotifications && ActiveNotificationsHelper.showLastNotification()) {
try {
if (Preferences.lastNotificationIcon != 0) {
val remotePackageContext = context.createPackageContext(
Preferences.lastNotificationPackage, 0)
val icon = ContextCompat.getDrawable(remotePackageContext,
Preferences.lastNotificationIcon)
bindingView.subLineIcon.isVisible = true
bindingView.subLineIcon.setImageDrawable(icon)
} else {
bindingView.subLineIcon.isVisible = false
}
bindingView.subLineText.text = Preferences.lastNotificationTitle
showSomething = true
break@loop
} catch (ex: Exception) {}
}
}
Constants.GlanceProviderId.GREETINGS -> {
val greetingsText = GreetingsHelper.getRandomString(context)
if (Preferences.showGreetings && GreetingsHelper.showGreetings() && greetingsText.isNotBlank()) {
bindingView.subLineText.text = greetingsText
bindingView.subLineText.maxLines = 2
bindingView.subLineIcon.isVisible = false
showSomething = true
break@loop
}
}
Constants.GlanceProviderId.EVENTS -> {
if (Preferences.showEventsAsGlanceProvider && Preferences.showEvents && context.checkGrantedPermission(
Manifest.permission.READ_CALENDAR) && nextEvent != null) {
bindingView.subLineText.text = context.getString(R.string.events_glance_provider_format).format(nextEvent.title, if (Preferences.showDiffTime && now.timeInMillis < nextEvent.startDate) {
if (!nextEvent.allDay) {
SettingsStringHelper.getDifferenceText(
context,
now.timeInMillis,
nextEvent.startDate
)
.toLowerCase(Locale.getDefault())
} else {
SettingsStringHelper.getAllDayEventDifferenceText(
context,
now.timeInMillis,
nextEvent.startDate
).toLowerCase(Locale.getDefault())
}
} else "").trimEnd()
bindingView.subLineIcon.isVisible = true
bindingView.subLineIcon.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.round_today_24
)
)
showSomething = true
break@loop
}
}
}
}
if (showSomething) {
bindingView.dateLayout.isVisible = true
bindingView.calendarLayout.isVisible = false
bindingView.subLine.isVisible = true
bindingView.weatherSubLine.isVisible = false
bindingView.subLineTopMarginSmall.visibility =
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.SMALL.rawValue) View.VISIBLE else View.GONE
bindingView.subLineTopMarginMedium.visibility =
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.MEDIUM.rawValue) View.VISIBLE else View.GONE
bindingView.subLineTopMarginLarge.visibility =
if (Preferences.secondRowTopMargin == Constants.SecondRowTopMargin.LARGE.rawValue) View.VISIBLE else View.GONE
} else {
bindingView.subLineIcon.isVisible = false
}
}
// Color
listOf<TextView>(
bindingView.date,
bindingView.weatherDateLineTemperature,
bindingView.nextEvent,
bindingView.nextEventDifferenceTime,
).forEach {
it.setTextColor(ColorHelper.getFontColor(context.applicationContext.isDarkTheme()))
}
if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.rawValue) {
listOf<ImageView>(bindingView.actionNext)
} else {
listOf<ImageView>(
bindingView.actionNext,
bindingView.weatherDateLineWeatherIcon,
bindingView.weatherSubLineWeatherIcon
)
}.forEach {
it.setColorFilter(ColorHelper.getFontColorRgb(context.applicationContext.isDarkTheme()))
it.alpha =
(if (context.isDarkTheme()) Preferences.textGlobalAlphaDark.toIntValue()
.toFloat() else Preferences.textGlobalAlpha.toIntValue()
.toFloat()) / 100
}
listOf<TextView>(bindingView.subLineText, bindingView.weatherSubLineDivider, bindingView.weatherSubLineTemperature).forEach {
it.setTextColor(ColorHelper.getSecondaryFontColor(context.applicationContext.isDarkTheme()))
}
if (Preferences.weatherIconPack != Constants.WeatherIconPack.MINIMAL.rawValue) {
listOf<ImageView>(bindingView.subLineIcon, bindingView.subLineIconShadow)
} else {
listOf<ImageView>(bindingView.subLineIcon, bindingView.weatherSubLineWeatherIcon, bindingView.subLineIconShadow)
}.forEach {
it.setColorFilter(ColorHelper.getSecondaryFontColorRgb(context.applicationContext.isDarkTheme()))
it.alpha =
(if (context.isDarkTheme()) Preferences.textSecondaryAlphaDark.toIntValue()
.toFloat() else Preferences.textSecondaryAlpha.toIntValue()
.toFloat()) / 100
}
// Text Size
listOf<Pair<TextView, Float>>(
bindingView.date to Preferences.textMainSize,
bindingView.weatherDateLineTemperature to ((Preferences.textMainSize + Preferences.textSecondSize) / 2),
bindingView.nextEvent to Preferences.textMainSize,
bindingView.nextEventDifferenceTime to Preferences.textMainSize,
bindingView.subLineText to Preferences.textSecondSize,
bindingView.weatherSubLineDivider to (Preferences.textSecondSize - 2),
bindingView.weatherSubLineTemperature to Preferences.textSecondSize,
).forEach {
it.first.setTextSize(TypedValue.COMPLEX_UNIT_SP, it.second)
}
// Icons scale
bindingView.subLineIcon.scaleX = Preferences.textSecondSize / 18f
bindingView.subLineIcon.scaleY = Preferences.textSecondSize / 18f
bindingView.weatherSubLineWeatherIcon.scaleX = Preferences.textSecondSize / 18f
bindingView.weatherSubLineWeatherIcon.scaleY = Preferences.textSecondSize / 18f
bindingView.weatherDateLineWeatherIcon.scaleX = ((Preferences.textMainSize + Preferences.textSecondSize) / 2) / 20f
bindingView.weatherDateLineWeatherIcon.scaleY = ((Preferences.textMainSize + Preferences.textSecondSize) / 2) / 20f
bindingView.actionNext.scaleX = Preferences.textMainSize / 28f
bindingView.actionNext.scaleY = Preferences.textMainSize / 28f
// Shadows
val shadowRadius =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> 0f
1 -> 5f
2 -> 5f
else -> 5f
}
val shadowColor =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> Color.TRANSPARENT
1 -> R.color.black_50
2 -> Color.BLACK
else -> R.color.black_50
}
val shadowDy =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> 0f
1 -> 0f
2 -> 1f
else -> 0f
}
listOf<TextView>(
bindingView.date,
bindingView.weatherDateLineTemperature,
bindingView.nextEvent,
bindingView.nextEventDifferenceTime,
bindingView.subLineText,
bindingView.weatherSubLineDivider,
bindingView.weatherSubLineTemperature,
).forEach {
it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor)
}
// Icons shadow
listOf(
Pair(bindingView.subLineIcon, bindingView.subLineIconShadow),
).forEach {
if ((if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) == 0) {
it.second.isVisible = false
} else {
it.second.isVisible = it.first.isVisible
it.second.scaleX = it.first.scaleX
it.second.scaleY = it.first.scaleY
it.second.applyShadow(it.first)
}
}
listOf(
Pair(bindingView.actionNext, bindingView.actionNextShadow),
).forEach {
if ((if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) == 0) {
it.second.isVisible = false
} else {
it.second.isVisible = it.first.isVisible
it.second.scaleX = it.first.scaleX
it.second.scaleY = it.first.scaleY
it.second.applyShadow(it.first, 0.6f)
}
}
// Custom Font
if (Preferences.customFont == Constants.CUSTOM_FONT_GOOGLE_SANS) {
val googleSans: Typeface = when (Preferences.customFontVariant) {
"100" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_thin.ttf")
"200" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_light.ttf")
"500" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_medium.ttf")
"700" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_bold.ttf")
"800" -> Typeface.createFromAsset(context.assets, "fonts/google_sans_black.ttf")
else -> Typeface.createFromAsset(context.assets, "fonts/google_sans_regular.ttf")
}
listOf<TextView>(
bindingView.date,
bindingView.weatherDateLineTemperature,
bindingView.nextEvent,
bindingView.nextEventDifferenceTime,
bindingView.subLineText,
bindingView.weatherSubLineDivider,
bindingView.weatherSubLineTemperature,
).forEach {
it.typeface = googleSans
}
} else if (Preferences.customFont == Constants.CUSTOM_FONT_DOWNLOADED && typeface != null) {
listOf<TextView>(
bindingView.date,
bindingView.weatherDateLineTemperature,
bindingView.nextEvent,
bindingView.nextEventDifferenceTime,
bindingView.subLineText,
bindingView.weatherSubLineDivider,
bindingView.weatherSubLineTemperature,
).forEach {
it.typeface = typeface
}
}
// Dividers
arrayOf(bindingView.weatherSubLineDivider).forEach {
it.visibility = if (Preferences.showDividers) View.VISIBLE else View.INVISIBLE
it.layoutParams = (it.layoutParams as ViewGroup.MarginLayoutParams).apply {
this.marginEnd = if (Preferences.showDividers) 8f.convertDpToPixel(context).toInt() else 0
}
}
eventRepository.close()
return bindingView
}
}

View File

@ -1,47 +1,19 @@
package com.tommasoberlose.anotherwidget.ui.widgets
import android.Manifest
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.res.Resources
import android.graphics.Color
import android.graphics.Typeface
import android.os.Bundle
import android.text.format.DateUtils
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.RemoteViews
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.viewbinding.ViewBinding
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.LeftAlignedWidgetBinding
import com.tommasoberlose.anotherwidget.databinding.TheWidgetBinding
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.ColorHelper.toIntValue
import com.tommasoberlose.anotherwidget.helpers.ImageHelper.applyShadow
import com.tommasoberlose.anotherwidget.receivers.*
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.convertDpToPixel
import com.tommasoberlose.anotherwidget.utils.isDarkTheme
import com.tommasoberlose.anotherwidget.utils.toPixel
import java.text.DateFormat
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.math.min
import kotlin.math.roundToInt
class MainWidget : AppWidgetProvider() {
@ -98,16 +70,18 @@ class MainWidget : AppWidgetProvider() {
WidgetHelper.runWithCustomTypeface(context) {
val views = when (Preferences.widgetAlign) {
Constants.WidgetAlign.LEFT.rawValue -> LeftAlignedWidget(context).generateWidget(appWidgetId, min(dimensions.first - 8.toPixel(context), min(width, height) - 16.toPixel(context)), it)
Constants.WidgetAlign.LEFT.rawValue -> AlignedWidget(context).generateWidget(appWidgetId, min(dimensions.first - 8.toPixel(context), min(width, height) - 16.toPixel(context)), it)
Constants.WidgetAlign.RIGHT.rawValue -> AlignedWidget(context, rightAligned = true).generateWidget(appWidgetId, min(dimensions.first - 8.toPixel(context), min(width, height) - 16.toPixel(context)), it)
else -> StandardWidget(context).generateWidget(appWidgetId, min(dimensions.first - 8.toPixel(context), min(width, height) - 16.toPixel(context)), it)
}
appWidgetManager.updateAppWidget(appWidgetId, views)
if (views != null) appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
fun getWidgetView(context: Context, typeface: Typeface?): ViewBinding {
fun getWidgetView(context: Context, typeface: Typeface?): ViewBinding? {
return when (Preferences.widgetAlign) {
Constants.WidgetAlign.LEFT.rawValue -> LeftAlignedWidget(context).generateWidgetView(typeface)
Constants.WidgetAlign.LEFT.rawValue -> AlignedWidget(context).generateWidgetView(typeface)
Constants.WidgetAlign.RIGHT.rawValue -> AlignedWidget(context, rightAligned = true).generateWidgetView(typeface)
else -> StandardWidget(context).generateWidgetView(typeface)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 871 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M8.5,8.53v3.24c0,0.18 0.09,0.34 0.24,0.43l2.52,1.51c0.23,0.14 0.52,0.06 0.66,-0.16l0,0c0.14,-0.23 0.06,-0.53 -0.16,-0.66L9.5,11.55V8.53c0,-0.26 -0.21,-0.48 -0.48,-0.48H8.98C8.71,8.05 8.5,8.26 8.5,8.53z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M13.9,10c0.07,0.32 0.1,0.66 0.1,1c0,2.76 -2.24,5 -5,5s-5,-2.24 -5,-5s2.24,-5 5,-5c0.71,0 1.39,0.15 2,0.42V5.35C10.37,5.13 9.7,5 9,5c-3.31,0 -6,2.69 -6,6s2.69,6 6,6s6,-2.69 6,-6c0,-0.34 -0.04,-0.67 -0.09,-1H13.9z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M15,6V4.5C15,4.22 14.78,4 14.5,4h0C14.22,4 14,4.22 14,4.5V6h0h-1.5C12.22,6 12,6.22 12,6.5v0C12,6.78 12.22,7 12.5,7H14v1.5C14,8.78 14.22,9 14.5,9h0C14.78,9 15,8.78 15,8.5V7v0h1.5C16.78,7 17,6.78 17,6.5v0C17,6.22 16.78,6 16.5,6H15z"/>
</vector>

View File

@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M10.75,8C10.34,8 10,8.34 10,8.75v4.69c0,0.35 0.18,0.67 0.47,0.85l3.64,2.24c0.33,0.2 0.76,0.11 0.97,-0.21c0.23,-0.34 0.12,-0.8 -0.23,-1.01L11.5,13.3V8.75C11.5,8.34 11.16,8 10.75,8z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M17.92,12c0.05,0.33 0.08,0.66 0.08,1c0,3.9 -3.1,7 -7,7s-7,-3.1 -7,-7c0,-3.9 3.1,-7 7,-7c0.7,0 1.37,0.1 2,0.29V4.23C12.36,4.08 11.69,4 11,4c-5,0 -9,4 -9,9s4,9 9,9s9,-4 9,-9c0,-0.34 -0.02,-0.67 -0.06,-1H17.92z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M22,5h-2V3c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v2h-2c-0.55,0 -1,0.45 -1,1c0,0.55 0.45,1 1,1h2v2c0,0.55 0.45,1 1,1s1,-0.45 1,-1V7h2c0.55,0 1,-0.45 1,-1C23,5.45 22.55,5 22,5z"/>
</vector>

View File

@ -163,7 +163,7 @@
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="12dp"
android:src="@drawable/round_text_fields_24"
android:src="@drawable/round_more_time_24"
app:tint="@color/colorPrimaryText"/>
<LinearLayout
android:layout_width="match_parent"

View File

@ -16,11 +16,11 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/main_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:visibility="gone"
android:paddingBottom="8dp"
android:orientation="vertical"
@ -63,7 +63,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layoutDirection="locale"
android:layout_gravity="center_vertical"
android:id="@+id/calendar_layout"
android:visibility="gone"
android:orientation="horizontal">
@ -129,10 +128,11 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:baselineAligned="false"
android:layout_gravity="center_vertical"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:id="@+id/sub_line_container"
android:layoutDirection="locale"
android:gravity="center_vertical">
<LinearLayout

View File

@ -28,6 +28,7 @@
android:layout_height="wrap_content"
android:format12Hour="h:mm"
android:padding="0dp"
android:includeFontPadding="false"
android:lines="1"
android:maxLines="1"
style="@style/AnotherWidget.Widget.Title"
@ -39,6 +40,7 @@
android:format12Hour="a"
android:format24Hour=""
android:padding="0dp"
android:includeFontPadding="false"
android:lines="1"
android:maxLines="1"
style="@style/AnotherWidget.Widget.Title"
@ -47,6 +49,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/timezones_container"
android:visibility="gone"
android:layout_marginStart="16dp"
android:layout_marginEnd="0dp"
android:layout_marginTop="18dp"
@ -61,6 +64,7 @@
android:layout_height="wrap_content"
android:format12Hour="h:mm"
android:padding="0dp"
android:includeFontPadding="false"
android:lines="1"
android:maxLines="1"
style="@style/AnotherWidget.Widget.Title"
@ -71,6 +75,7 @@
android:layout_height="wrap_content"
android:format12Hour="a"
android:format24Hour=""
android:includeFontPadding="false"
android:padding="0dp"
android:lines="1"
android:maxLines="1"
@ -115,7 +120,8 @@
android:id="@+id/clock_bottom_margin_large" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:id="@+id/content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

View File

@ -0,0 +1,243 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp"
android:scaleType="fitXY"
android:id="@+id/widget_shape_background"
android:src="@drawable/card_background" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:layout_alignParentEnd="true"
android:gravity="end"
android:id="@+id/main_layout"
android:animateLayoutChanges="true">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/time_container"
android:layout_gravity="end"
android:layoutDirection="locale"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/timezones_container"
android:visibility="gone"
android:layout_marginStart="0dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="18dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextClock
android:id="@+id/alt_timezone_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:format12Hour="h:mm"
android:padding="0dp"
android:includeFontPadding="false"
android:lines="1"
android:maxLines="1"
style="@style/AnotherWidget.Widget.Title"
android:gravity="center_vertical"/>
<TextClock
android:id="@+id/alt_timezone_time_am_pm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:format12Hour="a"
android:format24Hour=""
android:includeFontPadding="false"
android:padding="0dp"
android:lines="1"
android:maxLines="1"
style="@style/AnotherWidget.Widget.Title"
android:gravity="center_vertical"/>
</LinearLayout>
<TextView
android:id="@+id/alt_timezone_label"
android:layout_width="wrap_content"
android:gravity="start"
android:maxLines="1"
android:textStyle="bold"
android:includeFontPadding="false"
android:layout_height="wrap_content"
style="@style/AnotherWidget.Widget.Subtitle" />
</LinearLayout>
<TextClock
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:format12Hour="h:mm"
android:padding="0dp"
android:includeFontPadding="false"
android:lines="1"
android:maxLines="1"
style="@style/AnotherWidget.Widget.Title"
android:gravity="center_vertical"/>
<TextClock
android:id="@+id/time_am_pm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:format12Hour="a"
android:format24Hour=""
android:padding="0dp"
android:includeFontPadding="false"
android:lines="1"
android:maxLines="1"
style="@style/AnotherWidget.Widget.Title"
android:gravity="center_vertical"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_marginTop="-16dp"
android:orientation="horizontal"
android:id="@+id/clock_bottom_margin_none" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_marginTop="-8dp"
android:visibility="gone"
android:orientation="horizontal"
android:id="@+id/clock_bottom_margin_small" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="2dp"
android:visibility="gone"
android:orientation="horizontal"
android:id="@+id/clock_bottom_margin_medium" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="10dp"
android:visibility="gone"
android:orientation="horizontal"
android:id="@+id/clock_bottom_margin_large" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:id="@+id/content">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:layout_alignParentEnd="true"
android:id="@+id/bitmap_container"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0"
android:gravity="end"
android:layout_alignParentEnd="true"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@color/errorColorText"
android:layoutDirection="locale"
android:visibility="gone"
android:gravity="end"
android:layout_marginBottom="4dp"
android:id="@+id/first_line_rect">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:id="@+id/date_rect"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/colorNightDark"
android:visibility="gone"
android:id="@+id/weather_rect" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layoutDirection="locale"
android:visibility="gone"
android:id="@+id/calendar_layout_rect"
android:orientation="horizontal">
<ImageView
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
android:background="@color/colorAccent"
android:id="@+id/next_event_rect" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/colorNightDark"
android:id="@+id/next_event_difference_time_rect" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:background="@color/colorAccent"
android:layout_gravity="center_vertical"
android:id="@+id/action_next_rect" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="4dp"
android:visibility="gone"
android:orientation="horizontal"
android:id="@+id/sub_line_top_margin_small_sans" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="8dp"
android:visibility="gone"
android:orientation="horizontal"
android:id="@+id/sub_line_top_margin_medium_sans" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="12dp"
android:visibility="gone"
android:orientation="horizontal"
android:id="@+id/sub_line_top_margin_large_sans" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/colorNightDark"
android:orientation="horizontal"
android:layoutDirection="locale"
android:gravity="center_vertical">
<LinearLayout
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:id="@+id/sub_line_rect" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:background="@color/colorNightDark"
android:id="@+id/weather_sub_line_rect" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
</RelativeLayout>

View File

@ -30,6 +30,7 @@
android:layout_height="wrap_content"
android:gravity="center"
android:format12Hour="h:mm"
android:includeFontPadding="false"
android:padding="0dp"
android:lines="1"
android:maxLines="1"
@ -41,6 +42,7 @@
android:gravity="center"
android:format12Hour="a"
android:format24Hour=""
android:includeFontPadding="false"
android:padding="0dp"
android:lines="1"
android:maxLines="1"
@ -49,6 +51,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/timezones_container"
android:visibility="gone"
android:layout_marginStart="16dp"
android:layout_marginEnd="0dp"
android:layout_marginTop="20dp"
@ -65,6 +68,7 @@
android:padding="0dp"
android:lines="1"
android:maxLines="1"
android:includeFontPadding="false"
style="@style/AnotherWidget.Widget.Title"
android:gravity="center_vertical"/>
<TextClock
@ -74,6 +78,7 @@
android:format12Hour="a"
android:format24Hour=""
android:padding="0dp"
android:includeFontPadding="false"
android:lines="1"
android:maxLines="1"
style="@style/AnotherWidget.Widget.Title"
@ -117,7 +122,8 @@
android:id="@+id/clock_bottom_margin_large" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:id="@+id/content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name" translatable="false">Another Widget</string>
<string name="add_widget">Tilføj</string>
<string name="add_widget">Tilføj Widget</string>
<!-- Display -->
<string name="settings_general_title">Visning</string>
@ -22,14 +22,19 @@
<string name="alpha">Alpha</string>
<string name="transparent">Gennemsigtig</string>
<string name="settings_show_dividers_title">Vis tekstdelere</string>
<string name="first_row_header">Første række</string>
<string name="second_row_header">Anden række</string>
<string name="first_row_header">Primær tekst</string>
<string name="second_row_header">Sekundær tekst</string>
<string name="global_style_header">Widget</string>
<string name="action_capitalize_the_date">Skift mellem stort og småt begyndelsesbogstav</string>
<string name="settings_date_format_title">Datoformat</string>
<string name="header_widget_background">Widget baggrund</string>
<string name="settings_secondary_row_top_margin_title">Margen</string>
<string name="settings_secondary_row_top_margin_title">Rækkeafstand</string>
<string name="action_custom_font_to_search">Brugerdefineret skrifttype...</string>
<string name="font_family_settings_title">Font familie</string>
<string name="settings_widget_align_title">Justering</string>
<string name="settings_widget_align_left_subtitle">Venstre</string>
<string name="settings_widget_align_right_subtitle">Højre</string>
<string name="settings_widget_align_center_subtitle">Centrer</string>
<string name="font_100" translatable="false">Thin</string>
<string name="font_200" translatable="false">Light</string>
@ -40,6 +45,16 @@
<string name="font_700" translatable="false">Bold</string>
<string name="font_800" translatable="false">Black</string>
<string name="font_900" translatable="false">Heavy</string>
<string name="font_italic" translatable="false">Italic</string>
<string name="font_100_italic" translatable="false">Thin Italic</string>
<string name="font_200_italic" translatable="false">Light Italic</string>
<string name="font_300_italic" translatable="false">Book Italic</string>
<string name="font_400_italic" translatable="false">Regular Italic</string>
<string name="font_500_italic" translatable="false">Medium Italic</string>
<string name="font_600_italic" translatable="false">Semi-bold Italic</string>
<string name="font_700_italic" translatable="false">Bold Italic</string>
<string name="font_800_italic" translatable="false">Black Italic</string>
<string name="font_900_italic" translatable="false">Heavy Italic</string>
<!-- Calendar -->
<string name="settings_calendar_title">Kalender</string>
@ -63,7 +78,7 @@
<string name="settings_show_until_subtitle_5">7 dage senere</string>
<string name="settings_show_until_title">Vis begivenheder mindst</string>
<string name="day_char">d</string>
<string name="settings_calendar_app_title">Standard app til kalender</string>
<string name="settings_calendar_app_title">Kalender</string>
<string name="error_no_calendar">Ingen kalendere fundet.</string>
<string name="tomorrow">i morgen</string>
<string name="today">i dag</string>
@ -93,6 +108,11 @@
<string name="attendee_status_invited">Begivenhedsinvitationer</string>
<string name="attendee_status_declined">Afviste begivenheder</string>
<string name="calendar_sync_notification_channel_id" translatable="false">calendar-sync</string>
<string name="calendar_sync_notification_channel_name">Synkroniseringstjeneste for begivenheder</string>
<string name="calendar_sync_notification_channel_description">Tjeneste som bruges til at synkronisere kalenderens begivenheder.</string>
<string name="calendar_sync_notification_title">Synkroniserer kalender begivenheder…</string>
<!-- Weather -->
<string name="settings_weather_title">Vejr</string>
<string name="title_permission_location">Vis vejret</string>
@ -108,27 +128,19 @@
<string name="settings_weather_refresh_period_subtitle_4">12 timer</string>
<string name="settings_weather_refresh_period_subtitle_5">24 timer</string>
<string name="settings_custom_location_title">Lokation</string>
<string name="custom_location_gps">Brug geolocation</string>
<string name="custom_location_gps">Brug geolokation</string>
<string name="show_weather_visible">Vejrinfo er synlig</string>
<string name="show_weather_not_visible">Vejrinfo er skjult</string>
<string name="settings_weather_app_title">Tryk på vejret åbner</string>
<string name="settings_weather_app_title">Vejr</string>
<string name="settings_weather_provider_api_key_title">Vejr API nøgle</string>
<string name="settings_weather_provider_api_key_subtitle_all_set">Vejrudbyderen er konfigureret korrekt</string>
<string name="settings_weather_provider_api_key_subtitle_not_set">Vejrudbyderen skal konfigureres</string>
<string name="api_key_hint">OpenWeatherMap API nøgle</string>
<string name="default_weather_app">Google Weather</string>
<string name="weather_warning">Vejr fra Google Awareness er forældet. Der kræves nu en OpenWeatherMap API nøgle for at vise vejret i widget\'en.</string>
<string name="api_key_title_1">Opret en OpenWeatherMap konto</string>
<string name="api_key_subtitle_1">Opret en gratis konto hos OpenWeatherMap. Det vil kun tage få minutter.</string>
<string name="api_key_title_2">Kopier din API nøgle</string>
<string name="api_key_subtitle_2">Find menuen API keys i dine kontoindstillinger, og kopier standardnøglen.</string>
<string name="api_key_title_3">Indtast nøglen i app\'en</string>
<string name="api_key_subtitle_3">Indsæt nøglen i feltet ovenfor og gem ændringen. Så snart nøglen er aktiveret, vil vejret blive synligt.</string>
<string name="action_open_provider">Gå til OpenWeatherMap.org</string>
<string name="api_key_info_all_set">Der kan gå op til 10 minutter inden din API nøgle er aktiveret. Vejret vil blive opdateret så snart det er tilgængeligt.</string>
<string name="weather_warning">Google Awareness API er forældet. Der kræves nu en API nøgle fra en tredjepartsudbyder, for at vise vejret i widget\'en.</string>
<string name="settings_weather_icon_pack_title">Ikonpakke</string>
<string name="settings_weather_icon_pack_default">Ikonpakke %d</string>
<string name="background_location_warning">Vi indsamler placeringsdata for at opdatere vejrinformationer, selv når app\'en er lukket eller ikke er i brug.\nVi bruger ikke disse data på andre måder.</string>
<string name="background_location_warning">Når geolokation er aktiveret, indsamler vi placeringsdata for at opdatere vejrinformationer, selv når app\'en er lukket eller ikke er i brug.\nVi bruger ikke disse data på andre måder.</string>
<string name="settings_weather_provider_api">Vejrudbyder</string>
<string name="settings_weather_provider_open_weather" translatable="false">OpenWeatherMap.org</string>
@ -152,15 +164,15 @@
<string name="weather_provider_info_weatherapi_subtitle">Åbn udbyderens websted, opret en personlig konto og indsæt standardnøglen her.</string>
<string name="weather_provider_info_here_subtitle">Åbn udbyderens websted, opret en personlig konto og indsæt standardnøglen her.</string>
<string name="weather_provider_info_accuweather_subtitle">Åbn udbyderens websted, opret en personlig konto og indsæt standardnøglen her.</string>
<string name="weather_provider_info_weather_gov_subtitle" />
<string name="weather_provider_info_yr_subtitle" />
<string name="weather_provider_info_weather_gov_subtitle">\n</string>
<string name="weather_provider_info_yr_subtitle">\n</string>
<string name="weather_provider_error_missing_key">Vejrudbyderen du har valgt kræver en API-nøgle.</string>
<string name="weather_provider_error_wrong_location">Den aktuelle placering understøttes ikke.</string>
<string name="weather_provider_error_missing_location">Vælg en gyldig placering.</string>
<string name="weather_provider_error_expired_key">Det ser ud til, at din API-nøgle er udløbet.</string>
<string name="weather_provider_error_invalid_key">API-nøgle er ikke gyldig eller endnu ikke aktiveret.</string>
<string name="weather_provider_error_misconfigured">Vejrudbyderen er forkert konfigureret.</string>
<string name="weather_provider_error_invalid_key">API-nøgle er ugyldig eller ikke aktiveret endnu.</string>
<string name="weather_provider_error_misconfigured">Vejrudbyderen er konfigureret forkert.</string>
<string name="weather_provider_error_connection">Forbindelsesfejl.</string>
<string name="weather_provider_error_generic">Noget gik galt, tjek vejrudbyderens konfiguration.</string>
<string name="api_key_required_message">Konto påkrævet</string>
@ -169,9 +181,15 @@
<string name="weather_select_a_provider">Vælg en udbyder</string>
<string name="weather_provider_activity_subtitle">Vælg en vejrudbyder fra listen.\nEnkelte udbydere har brug for en gratis personlig konto,\nmen de er normalt mere nøjagtige.</string>
<string name="location_access_notification_channel_id" translatable="false">location-access</string>
<string name="location_access_notification_channel_name">Opdateringstjeneste til vejret</string>
<string name="location_access_notification_channel_description">Tjeneste som bruges til at opdatere vejret, baseret på brugerens nuværende lokation.</string>
<string name="location_access_notification_title">Opdaterer vejret…</string>
<string name="location_access_notification_subtitle">Vi opdaterer vejret baseret på din nuværnde lokation.</string>
<!-- Clock -->
<string name="settings_clock_title">Ur</string>
<string name="settings_clock_app_title">Tryk på ur åbner</string>
<string name="settings_clock_app_title">Ur</string>
<string name="title_show_clock">Vis uret</string>
<string name="show_clock_visible">Uret vises</string>
<string name="show_clock_not_visible">Uret er skjult</string>
@ -180,47 +198,52 @@
<string name="settings_clock_bottom_margin_title">Nedre margen på ur</string>
<string name="settings_clock_bottom_margin_subtitle_none">Ingen</string>
<string name="settings_clock_bottom_margin_subtitle_small">Lille</string>
<string name="settings_clock_bottom_margin_subtitle_medium">Medium</string>
<string name="settings_clock_bottom_margin_subtitle_medium">Mellem</string>
<string name="settings_clock_bottom_margin_subtitle_large">Stor</string>
<string name="clock_warning">Grundet teknologiske begrænsninger har uret ikke den tilpassede skrifttype og tekstskyggerne, som valgt under Typografi sektionen.</string>
<string name="clock_warning">Grundet tekniske begrænsninger har uret ikke de tilpassede skrifttyper og skygger, som valgt under Typografi sektionen.</string>
<string name="settings_clock_text_color_title">Tekstfarve</string>
<string name="settings_ampm_indicator_title">Vis AM/PM indikator</string>
<string name="clock_text_header">Tekststil på ur</string>
<string name="time_zones_header">Tidszoner</string>
<string name="settings_alt_timezone_title">Ekstra ur</string>
<string name="no_time_zone_label">Ingen</string>
<string name="time_zone_search_error_message">Fejl ved søgning efter tidszonen.</string>
<string name="geoname_credit">Tjeneste tilgængelig takket være <a href="http://www.geonames.org/">GeoName</a>.</string>
<!-- Glance -->
<string name="settings_show_next_alarm_title">Vis næste alarm</string>
<string name="settings_at_a_glance_title">Overblik</string>
<string name="settings_show_music_title">Aktuel sang</string>
<string name="settings_request_notification_access">Vi har brug for tilladelse til aflæsning af notifikationer, for at vise den aktuelle sang.</string>
<string name="settings_request_notification_access">Vi har brug for notifikations-tilladelsen, for at vise den aktuelle sang.</string>
<string name="settings_request_last_notification_access">Vi har brug for notifikations-tilladelsen, for at tjekke dine seneste notifikationer.</string>
<string name="settings_request_fitness_access">Vi har brug for et par få tilladelser, til at vise dine daglige skridt fra Google Fit.</string>
<string name="title_show_glance">Vis overbliks-informationer</string>
<string name="description_show_glance_visible">Service aktiveret</string>
<string name="description_show_glance_not_visible">Service deaktiveret</string>
<string name="settings_sort_glance_providers_title">Datakildeprioritet</string>
<string name="settings_sort_glance_providers_subtitle">Skift datakilders prioritet</string>
<string name="settings_sort_glance_providers_subtitle">Skift datakilders prioritet ved at sortere listen nedenfor med drag-and-drop ikonerne</string>
<string name="settings_custom_notes_title">Brugerdefinerede noter</string>
<string name="settings_low_battery_level_title">Batteri</string>
<string name="settings_daily_steps_title">Daglige skridt</string>
<string name="battery_low_warning">Lavt batteriniveau</string>
<string name="daily_steps_counter">%d skridt indtil videre</string>
<string name="settings_low_battery_level_title">Batteri</string>
<string name="battery_low_warning">Lavt batteriniveau</string>
<string name="charging">Oplader</string>
<string name="charged">Opladet</string>
<string name="charged">Fuldt opladet</string>
<string name="providers">Kilder</string>
<string name="glance_info">Glance info will show up only when there are no events displayed and only when a few conditions are verified.</string>
<string name="glance_info">Overbliks-informationer vil kun blive vist, når der ikke er nogle begivenheder vist, og kun når et par betingelser er mødt.</string>
<string name="settings_music_players_filter_title">Musikafspillere</string>
<string name="settings_music_players_filter_subtitle">Vælg dine relevante musikafspillere</string>
<string name="settings_show_music_subtitle">Vi benytter musikafspillerens notifikation til at vise den aktuelle sang.</string>
<string name="settings_low_battery_level_subtitle">Bliv underrettet, når enheden har lavt batteriniveau eller oplader.</string>
<string name="settings_daily_steps_subtitle">Se dine daglige skridt et kort øjeblik efter, når du er færdig med at gå eller løbe.</string>
<string name="settings_show_next_alarm_subtitle">Vis din næste alarm, tjek om din standard ur app er den eneste der angiver alarmer.</string>
<string name="settings_show_next_alarm_subtitle">Vis din næste alarm.\nTjek om din standard ur app er den eneste der angiver alarmer.</string>
<string name="settings_show_next_alarm_app_title">Alarm sat af %s</string>
<string name="settings_show_next_alarm_app_subtitle_wrong">Den næste alarm ser ud til at være forkert.</string>
<string name="settings_show_next_alarm_app_subtitle_correct">Den næste alarm ser ud til at være korrekt.</string>
<string name="settings_show_next_alarm_app_subtitle_wrong">Den næste alarm ser ud til at være forkert</string>
<string name="settings_show_next_alarm_app_subtitle_correct">Den næste alarm ser ud til at være korrekt</string>
<string name="settings_show_notifications_title">Seneste notifikationer</string>
<string name="settings_show_notifications_subtitle">Få et hurtigt overblik over de seneste notifikationer, som blev vist på din enhed.</string>
<string name="settings_show_greetings_title">Hilsener</string>
<string name="settings_show_greetings_subtitle">Se en sej sætning, når du mindst venter det.</string>
<string name="settings_show_greetings_title">Hilsner</string>
<string name="settings_show_greetings_subtitle">Se en sej sætning for at lette din dag, når du mindst venter det.</string>
<string name="google_fit_account_connected">Konto forbundet</string>
<string name="google_fit_account_not_connected">Konto ikke forbundet</string>
<string name="google_fit">Google Fit</string>
@ -228,6 +251,40 @@
<string name="action_disconnect">Afbryd</string>
<string name="applications_filter_title">Applikationer</string>
<string name="applications_filter_subtitle">Vælg hvilke notifikationer der skal vises</string>
<string-array name="morning_greetings" tools:ignore="InconsistentArrays">
<item>Godmorgen!</item>
<item>Hav en god dag!</item>
<item>Klar til at tage afsted?</item>
<item>Vågn op.</item>
<item>Glem ikke din morgenmad!</item>
</string-array>
<string-array name="evening_greetings" tools:ignore="InconsistentArrays">
<item>Godaften!</item>
<item>Hvordan gik din dag?</item>
<item>Har du haft en god dag?</item>
<item>Efter indsats kommer komfort.</item>
<item>Glem ikke at spise aftensmad!</item>
</string-array>
<string-array name="night_greetings" tools:ignore="InconsistentArrays">
<item>Godnat!</item>
<item>Sov godt!</item>
<item>Søde drømme indtil solstrålerne finder dig!</item>
<item>Vi ses i morgen...</item>
<item>Bliv ikke oppe for længe!</item>
</string-array>
<string name="glance_notification_hide_timeout_title">Skjul notifikationen efter</string>
<string-array name="glance_notifications_timeout">
<item>30 sekunder</item>
<item>1 minut</item>
<item>5 minutter</item>
<item>10 minutter</item>
<item>15 minutter</item>
<item>Afvisning</item>
</string-array>
<string name="settings_show_events_as_glance_provider_title">Begivenheder</string>
<string name="settings_show_events_as_glance_provider_subtitle">Se et smugkig på dine kalenderbegivenheder, og vis altid den aktuelle dato.</string>
<string name="settings_show_events_as_glance_provider_error">Aktiver visningen af begivenhederne i kalenderfanen, og giv den nødvendige tilladelse.</string>
<string name="events_glance_provider_format">%1$s %2$s</string>
<!-- Settings -->
<string name="action_share">Del</string>
@ -238,7 +295,7 @@
<string name="action_refresh_widget">Genindlæs widget</string>
<string name="toolbar_transition_name" translatable="false">toolbar</string>
<string name="error_opening_uri">Fejl ved åbning af URL: Link kopieret til udklipsholder.</string>
<string name="loading_text">Indlæser data</string>
<string name="loading_text">Indlæser data...</string>
<string name="error_opening_app">Fejl ved åbning af app.</string>
<string name="default_name">Standard app</string>
<string name="action_save">Gem</string>
@ -260,7 +317,7 @@
<string name="settings_app_version_title">App version</string>
<string name="settings_title_show_wallpaper">Vis baggrundsbillede</string>
<string name="support_refresh_widget_subtitle">Tving en genstart af widget-tjenesten</string>
<string name="settings_feedback_subtitle">Dette er et open-source projekt, du er velkommen til at hjælpe.</string>
<string name="settings_feedback_subtitle">Du er velkommen til at hjælpe med projektet.</string>
<string name="settings_feedback_title">Feedback og feature requests</string>
<string name="xiaomi_manufacturer" translatable="false">xiaomi</string>
<string name="xiaomi_warning_title">Xiaomi Enheder</string>
@ -275,7 +332,7 @@
<string name="preferences_header">Præferencer</string>
<string name="settings_privacy_policy_title"><![CDATA[Juridisk og privatliv]]></string>
<string name="settings_privacy_policy_subtitle">Se appens privatlivspolitik</string>
<string name="project_header">Projekt</string>
<string name="project_header">Open-source projekt</string>
<string name="information_header">Information</string>
<!-- Activities -->
@ -294,4 +351,21 @@
<string name="nothing">Intet</string>
<string name="action_dismiss">Afvis</string>
<string name="action_accept">Accepter</string>
<string name="gestures_do_nothing">Ingen</string>
<string name="song_info_format_activity_title">Format på sang</string>
<string name="song_info_format_activity_subtitle">Skift den synlige sanginformation</string>
<!-- More -->
<string name="look_and_feel_header"><![CDATA[Look & feel]]></string>
<string name="typography_settings_title">Typografi</string>
<string name="typography_settings_subtitle">Angiv skrifttype, størrelse og farve</string>
<string name="layout_settings_title">Layout</string>
<string name="gestures_settings_subtitle">Handlinger, adfærd og genveje</string>
<string name="gestures_settings_title">Bevægelser</string>
<string name="gestures_amp_input_header"><![CDATA[Gestures & input]]></string>
<string name="clock_settings_subtitle">Angiv størrelse, farve og tidszoner</string>
<string name="layout_settings_subtitle">Widgetafstand og justeringer</string>
<string name="smart_content_header">Smart indhold</string>
<string name="spacing_settings_header">Mellemrum</string>
<string name="gestures_refresh_widget">Ingen, widget\'en vil blive opdateret</string>
</resources>

View File

@ -28,6 +28,11 @@
<string name="header_widget_background">Widget Hintergrund</string>
<string name="settings_secondary_row_top_margin_title">Zeilenabstand</string>
<string name="action_custom_font_to_search">Benutzerdefinierte Schriftart…</string>
<string name="font_family_settings_title">Schriftfamilie</string>
<string name="settings_widget_align_title">Widget Ausrichtung</string>
<string name="settings_widget_align_left_subtitle">Links</string>
<string name="settings_widget_align_right_subtitle">Rechts</string>
<string name="settings_widget_align_center_subtitle">Zentriert</string>
<!-- Calendar -->
<string name="settings_calendar_title">Kalender</string>
@ -81,6 +86,10 @@
<string name="attendee_status_invited">Einladungen zu Terminen</string>
<string name="attendee_status_declined">Abgelehnte Termine</string>
<string name="calendar_sync_notification_channel_name">Termine Synchronisierungsdienst</string>
<string name="calendar_sync_notification_channel_description">Dienst, der zum Synchronisieren der Kalendertermine verwendet wird.</string>
<string name="calendar_sync_notification_title">Synchronisiere Kalendertermine…</string>
<!-- Weather -->
<string name="settings_weather_title">Wetter</string>
<string name="title_permission_location">Wetter anzeigen</string>
@ -139,7 +148,7 @@
<string name="weather_select_a_provider">Wählen einen Anbieter</string>
<string name="weather_provider_activity_subtitle">Wählen Sie einen Wetterdienst aus der Liste aus.\nEinige Anbieter benötigen ein kostenloses\nBenutzerkonto, allerdings sind sie\ndafür in der Regel genauer.</string>
<string name="location_access_notification_channel_name">Hintergrunddienst</string>
<string name="location_access_notification_channel_name">Wetter Aktualisierungsdienst</string>
<string name="location_access_notification_channel_description">Dienst zur Aktualisierung des Wetters anhand des aktuellen Standorts des Benutzers.</string>
<string name="location_access_notification_title">Wetter wird aktualisiert…</string>
<string name="location_access_notification_subtitle">Das Wetter wird basierend auf Ihrem aktuellen Standort aktualisiert.</string>
@ -161,6 +170,11 @@
<string name="settings_clock_text_color_title">Schriftfarbe</string>
<string name="settings_ampm_indicator_title">Zeige AM/PM</string>
<string name="clock_text_header">Uhr Textstil</string>
<string name="time_zones_header">Zeitzonen</string>
<string name="settings_alt_timezone_title">Mehrere Uhren</string>
<string name="no_time_zone_label">Keine</string>
<string name="time_zone_search_error_message">Fehler beim Suchen der Zeitzone.</string>
<string name="geoname_credit">Service verfügbar dank <a href="http://www.geonames.org/">GeoName</a>.</string>
<!-- Glance -->
<string name="settings_show_next_alarm_title">Nächster Wecker</string>
@ -301,6 +315,9 @@
<string name="nothing">Nichts</string>
<string name="action_dismiss">Verwerfen</string>
<string name="action_accept">Akzeptieren</string>
<string name="gestures_do_nothing">Nichts</string>
<string name="song_info_format_activity_title">Songinfo-Format</string>
<string name="song_info_format_activity_subtitle">Ändere das Songinfo-Format</string>
<!-- More -->
<string name="look_and_feel_header"><![CDATA[Erscheinungsbild]]></string>
@ -313,5 +330,6 @@
<string name="clock_settings_subtitle">Größe, Farbe und Zeitzonen einstellen</string>
<string name="layout_settings_subtitle">Widget Abstände und Anpassungen</string>
<string name="smart_content_header">Smarte Inhalte</string>
<string name="font_family_settings_title">Schriftfamilie</string>
<string name="spacing_settings_header">Abstände</string>
<string name="gestures_refresh_widget">Keine, das Widget wird aktualisiert.</string>
</resources>

View File

@ -10,7 +10,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.3'
classpath 'com.android.tools.build:gradle:4.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.5'

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip