Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
@PreferenceGroup(name = "clock", prefix = "settings_clock_", suffix = "_key", value = {
@Preference(name = "font", type = Font.class, defaultValue = "HACK"),
@Preference(name = "color", type = int.class, defaultValue = "0xffffffff"),
@Preference(name = "modern", type = boolean.class, defaultValue = "true"),
@Preference(name = "date_bold", type = boolean.class, defaultValue = "false"),
@Preference(name = "time_bold", type = boolean.class, defaultValue = "false"),
@Preference(name = "date_visible", type = boolean.class, defaultValue = "true"),
@Preference(name = "time_visible", type = boolean.class, defaultValue = "true"),
@Preference(name = "flip_date_time", type = boolean.class, defaultValue = "false"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import de.jrpie.android.launcher.R
@Suppress("unused")
enum class Font(val id: Int) {
HACK(R.style.fontHack),
INTER(R.style.fontInter),
GEIST(R.style.fontGeist),
GEIST_MONO(R.style.fontGeistMono),
SYSTEM_DEFAULT(R.style.fontSystemDefault),
SANS_SERIF(R.style.fontSansSerif),
SERIF(R.style.fontSerif),
Expand Down
237 changes: 224 additions & 13 deletions app/src/main/java/de/jrpie/android/launcher/ui/widgets/ClockView.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
package de.jrpie.android.launcher.ui.widgets

import android.content.Context
import android.graphics.drawable.GradientDrawable
import android.text.format.DateFormat
import android.util.AttributeSet
import android.util.TypedValue
import android.view.Gravity
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.graphics.ColorUtils
import androidx.core.view.isVisible
import androidx.core.widget.TextViewCompat
import de.jrpie.android.launcher.actions.Gesture
import de.jrpie.android.launcher.databinding.WidgetClockBinding
import de.jrpie.android.launcher.preferences.LauncherPreferences
Expand Down Expand Up @@ -44,45 +53,90 @@ class ClockView(

val dateVisible = LauncherPreferences.clock().dateVisible()
val timeVisible = LauncherPreferences.clock().timeVisible()
val modernClock = LauncherPreferences.clock().modern()
val showSeconds = LauncherPreferences.clock().showSeconds()
val dateBold = LauncherPreferences.clock().dateBold()
val timeBold = LauncherPreferences.clock().timeBold()

var dateFMT = "yyyy-MM-dd"
var timeFMT = if (use24hFormat) {
var defaultDateFMT = "yyyy-MM-dd"
var modernDateFMT = "dd | MMMM yyyy"
var defaultTimeFMT = if (use24hFormat) {
"HH:mm"
} else {
"hh:mm"
}
if (LauncherPreferences.clock().showSeconds()) {
timeFMT += ":ss"
if (showSeconds) {
defaultTimeFMT += ":ss"
}
if (!use24hFormat) {
timeFMT += " a"
defaultTimeFMT += " a"
}
var modernTimeFMT = if (use24hFormat) {
"HH '|' mm"
} else {
"hh '|' mm"
}
if (showSeconds) {
modernTimeFMT += " '|' ss"
}
if (!use24hFormat) {
modernTimeFMT += " a"
}

if (LauncherPreferences.clock().localized()) {
dateFMT = DateFormat.getBestDateTimePattern(locale, dateFMT)
timeFMT = DateFormat.getBestDateTimePattern(locale, timeFMT)
defaultDateFMT = DateFormat.getBestDateTimePattern(locale, defaultDateFMT)
modernDateFMT = DateFormat.getBestDateTimePattern(locale, modernDateFMT)
defaultTimeFMT = DateFormat.getBestDateTimePattern(locale, defaultTimeFMT)
modernTimeFMT = DateFormat.getBestDateTimePattern(locale, modernTimeFMT)
}

var upperFormat = dateFMT
var lowerFormat = timeFMT
var upperVisible = dateVisible
var lowerVisible = timeVisible
var upperFormat: String
var lowerFormat: String
var upperVisible: Boolean
var lowerVisible: Boolean
var upperIsTime: Boolean

if (modernClock) {
upperFormat = modernDateFMT
lowerFormat = modernTimeFMT
upperVisible = dateVisible
lowerVisible = timeVisible
upperIsTime = false
} else {
upperFormat = defaultDateFMT
lowerFormat = defaultTimeFMT
upperVisible = dateVisible
lowerVisible = timeVisible
upperIsTime = false
}

if (LauncherPreferences.clock().flipDateTime()) {
upperFormat = lowerFormat.also { lowerFormat = upperFormat }
upperVisible = lowerVisible.also { lowerVisible = upperVisible }
upperIsTime = !upperIsTime
}

binding.clockUpperView.isVisible = upperVisible
binding.clockLowerView.isVisible = lowerVisible
binding.clockPanel.isVisible = upperVisible || lowerVisible

binding.clockUpperView.setTextColor(LauncherPreferences.clock().color())
binding.clockLowerView.setTextColor(LauncherPreferences.clock().color())
val clockColor = LauncherPreferences.clock().color()
binding.clockUpperView.setTextColor(clockColor)
binding.clockLowerView.setTextColor(clockColor)
applyClockStyle(clockColor, modernClock, upperIsTime, showSeconds)

binding.clockLowerView.format24Hour = lowerFormat
binding.clockUpperView.format24Hour = upperFormat
binding.clockLowerView.format12Hour = lowerFormat
binding.clockUpperView.format12Hour = upperFormat

if (upperIsTime) {
applyBold(binding.clockUpperView, timeBold)
applyBold(binding.clockLowerView, dateBold)
} else {
applyBold(binding.clockUpperView, dateBold)
applyBold(binding.clockLowerView, timeBold)
}
}

private fun setOnClicks() {
Expand All @@ -102,4 +156,161 @@ class ClockView(
}
}
}

private fun createClockPanelBackground(clockColor: Int): GradientDrawable {
return GradientDrawable().apply {
shape = GradientDrawable.RECTANGLE
cornerRadius = 36f.dp
setColor(ColorUtils.setAlphaComponent(0xFFFFFFFF.toInt(), 20))
setStroke(1f.dp.toInt(), ColorUtils.blendARGB(
ColorUtils.setAlphaComponent(0xFFFFFFFF.toInt(), 120),
ColorUtils.setAlphaComponent(clockColor, 72),
0.35f
))
}
}

private fun createModernTimeBackground(clockColor: Int): GradientDrawable {
return GradientDrawable().apply {
shape = GradientDrawable.RECTANGLE
cornerRadius = 0f
setColor(ColorUtils.setAlphaComponent(0xFFFFFFFF.toInt(), 0))
setStroke(3f.dp.toInt(), ColorUtils.blendARGB(
ColorUtils.setAlphaComponent(0xFFFFFFFF.toInt(), 230),
ColorUtils.setAlphaComponent(clockColor, 140),
0.25f
))
}
}

private fun applyClockStyle(
clockColor: Int,
modernClock: Boolean,
upperIsTime: Boolean,
showSeconds: Boolean
) {
val panelLayoutParams = binding.clockPanel.layoutParams as LayoutParams
binding.clockPanel.layoutParams = panelLayoutParams

val upperParams = binding.clockUpperView.layoutParams as LinearLayout.LayoutParams
val lowerParams = binding.clockLowerView.layoutParams as LinearLayout.LayoutParams

fun setAutoSize(textView: TextView, minSp: Int, maxSp: Int) {
TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(
textView,
minSp,
maxSp,
1,
TypedValue.COMPLEX_UNIT_SP
)
}

if (modernClock) {
val timeMaxSp = if (showSeconds) 38 else 64
val timeMinSp = if (showSeconds) 16 else 34
val dateMaxSp = if (showSeconds) 17 else 19
val timeHorizontalPadding = if (showSeconds) 14f.dp.toInt() else 10f.dp.toInt()
val timeVerticalPadding = if (showSeconds) 26f.dp.toInt() else 24f.dp.toInt()
val sideMargin = if (showSeconds) 18f.dp.toInt() else 44f.dp.toInt()

panelLayoutParams.width = 0
panelLayoutParams.marginStart = sideMargin
panelLayoutParams.marginEnd = sideMargin
binding.clockPanel.background = null
binding.clockPanel.elevation = 0f
binding.clockPanel.minimumWidth = 0
binding.clockPanel.setPadding(0, 0, 0, 0)

upperParams.width = ViewGroup.LayoutParams.MATCH_PARENT
lowerParams.width = ViewGroup.LayoutParams.MATCH_PARENT
lowerParams.topMargin = if (showSeconds) 16f.dp.toInt() else 22f.dp.toInt()

binding.clockUpperView.gravity = Gravity.CENTER
binding.clockLowerView.gravity = Gravity.CENTER
binding.clockUpperView.textAlignment = TEXT_ALIGNMENT_CENTER
binding.clockLowerView.textAlignment = TEXT_ALIGNMENT_CENTER

if (upperIsTime) {
setAutoSize(binding.clockUpperView, timeMinSp, timeMaxSp)
setAutoSize(binding.clockLowerView, 10, dateMaxSp)
binding.clockUpperView.letterSpacing = 0.01f
binding.clockLowerView.letterSpacing = if (showSeconds) 0.14f else 0.16f
binding.clockUpperView.setShadowLayer(8f.dp, 0f, 2f.dp, ColorUtils.setAlphaComponent(0xFFFFFFFF.toInt(), 16))
binding.clockLowerView.setShadowLayer(0f, 0f, 0f, 0)
binding.clockUpperView.setAllCaps(false)
binding.clockLowerView.setAllCaps(true)
binding.clockUpperView.background = createModernTimeBackground(clockColor)
binding.clockUpperView.setPadding(
timeHorizontalPadding,
timeVerticalPadding,
timeHorizontalPadding,
timeVerticalPadding
)
binding.clockLowerView.background = null
binding.clockLowerView.setPadding(0, 0, 0, 0)
} else {
setAutoSize(binding.clockUpperView, 10, dateMaxSp)
setAutoSize(binding.clockLowerView, timeMinSp, timeMaxSp)
binding.clockUpperView.letterSpacing = if (showSeconds) 0.14f else 0.16f
binding.clockLowerView.letterSpacing = 0.01f
binding.clockUpperView.setShadowLayer(0f, 0f, 0f, 0)
binding.clockLowerView.setShadowLayer(8f.dp, 0f, 2f.dp, ColorUtils.setAlphaComponent(0xFFFFFFFF.toInt(), 16))
binding.clockUpperView.setAllCaps(true)
binding.clockLowerView.setAllCaps(false)
binding.clockLowerView.background = createModernTimeBackground(clockColor)
binding.clockLowerView.setPadding(
timeHorizontalPadding,
timeVerticalPadding,
timeHorizontalPadding,
timeVerticalPadding
)
binding.clockUpperView.background = null
binding.clockUpperView.setPadding(0, 0, 0, 0)
}
} else {
panelLayoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT
panelLayoutParams.marginStart = 0
panelLayoutParams.marginEnd = 0
binding.clockPanel.background = null
binding.clockPanel.elevation = 0f
binding.clockPanel.minimumWidth = 0
binding.clockPanel.setPadding(0, 0, 0, 0)

upperParams.width = ViewGroup.LayoutParams.WRAP_CONTENT
lowerParams.width = ViewGroup.LayoutParams.WRAP_CONTENT
lowerParams.topMargin = 0

binding.clockUpperView.gravity = Gravity.CENTER
binding.clockLowerView.gravity = Gravity.CENTER
binding.clockUpperView.textAlignment = TEXT_ALIGNMENT_CENTER
binding.clockLowerView.textAlignment = TEXT_ALIGNMENT_CENTER
setAutoSize(binding.clockUpperView, 18, 30)
setAutoSize(binding.clockLowerView, 10, 18)
binding.clockUpperView.letterSpacing = 0f
binding.clockLowerView.letterSpacing = 0f
binding.clockUpperView.setShadowLayer(0f, 0f, 0f, 0)
binding.clockLowerView.setShadowLayer(0f, 0f, 0f, 0)
binding.clockUpperView.background = null
binding.clockLowerView.background = null
binding.clockUpperView.setPadding(0, 0, 0, 0)
binding.clockLowerView.setPadding(0, 0, 0, 0)
binding.clockUpperView.setAllCaps(false)
binding.clockLowerView.setAllCaps(false)
}

binding.clockUpperView.layoutParams = upperParams
binding.clockLowerView.layoutParams = lowerParams
}

private fun applyBold(textView: TextView, enabled: Boolean) {
textView.paint.isFakeBoldText = enabled
textView.invalidate()
}

private val Float.dp: Float
get() = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
this,
resources.displayMetrics
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class WidgetPanelActivity : LauncherGestureActivity(), UIObject {
override fun onCreate(savedInstanceState: Bundle?) {
super<LauncherGestureActivity>.onCreate(savedInstanceState)
super<UIObject>.onCreate()
val binding = ActivityWidgetPanelBinding.inflate(layoutInflater)
binding = ActivityWidgetPanelBinding.inflate(layoutInflater)
val binding = binding!!
setContentView(binding.root)

widgetPanelId = intent.getIntExtra(EXTRA_PANEL_ID, WidgetPanel.HOME.id)
Expand Down
Binary file added app/src/main/res/font/geist_mono_variable.ttf
Binary file not shown.
Binary file added app/src/main/res/font/geist_variable.ttf
Binary file not shown.
Binary file added app/src/main/res/font/inter_variable.ttf
Binary file not shown.
62 changes: 47 additions & 15 deletions app/src/main/res/layout/widget_clock.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,59 @@
android:longClickable="false"
tools:context=".ui.widgets.ClockView">

<TextClock
android:id="@+id/clock_upper_view"
<LinearLayout
android:id="@+id/clock_panel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
android:textSize="30sp"
android:elevation="14dp"
android:gravity="center_horizontal"
android:minWidth="236dp"
android:orientation="vertical"
android:paddingStart="26dp"
android:paddingTop="22dp"
android:paddingEnd="26dp"
android:paddingBottom="22dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="2024-12-24" />
tools:background="#14FFFFFF">

<TextClock
android:id="@+id/clock_lower_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
android:textSize="18sp"
tools:text="18:00:00"
app:layout_constraintTop_toBottomOf="@+id/clock_upper_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextClock
android:id="@+id/clock_upper_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoSizeMaxTextSize="42sp"
android:autoSizeMinTextSize="18sp"
android:autoSizeStepGranularity="1sp"
android:autoSizeTextType="uniform"
android:fontFeatureSettings="tnum"
android:gravity="center"
android:maxLines="1"
android:singleLine="true"
android:letterSpacing="0.015"
android:textAlignment="center"
android:textSize="32sp"
tools:text="2024-12-24" />

<TextClock
android:id="@+id/clock_lower_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:autoSizeMaxTextSize="18sp"
android:autoSizeMinTextSize="10sp"
android:autoSizeStepGranularity="1sp"
android:autoSizeTextType="uniform"
android:fontFeatureSettings="tnum"
android:gravity="center"
android:maxLines="1"
android:singleLine="true"
android:letterSpacing="0.06"
android:textAlignment="center"
android:textSize="18sp"
tools:text="18:00:00" />

</LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
Loading