wearos adjustments

This commit is contained in:
Phillip Thelen 2023-07-07 11:01:40 +02:00
parent 87cb30cae1
commit 609737e414
18 changed files with 641 additions and 36 deletions

View file

@ -1,7 +1,9 @@
Habitica is a free habit-building and productivity app that uses retro RPG elements to gamify your tasks and goals.
Use Habitica to help with ADHD, self care, New Years resolutions, household chores, work tasks, creative projects, fitness goals, back-to-school routines, and more!
How it works:
Create an avatar then add tasks, chores, or goals youd like to work on. When you do something in real life, check it off in the app and receive gold, experience, and items that can be used in-game!
Features:
• Automatically repeating tasks scheduled for your daily, weekly, or monthly routines
• Flexible habit tracker for tasks you want to do multiple times a day or only once in awhile
@ -17,8 +19,16 @@ Features:
• Reminders and widgets to help keep you on track
• Customizable color themes with dark and light mode
• Syncing across devices
• Brand new WearOS watch app available in version 4.0!
Want even more flexibility to take your tasks on the go? We have a Wear OS app on the watch!
Wear OS features:
• View, create, and complete Habites, Dailies, and To dos
• Receive rewards for your efforts with experience, food, eggs, and potions
• Track your stats with dynamic progress bars
• Show off your stunning pixel avatar on the watch face
Habitica is an open-source app run by a small team thats made better by the work of volunteers who contribute pixel art, translations, bug fixes, and more. If youd like to contribute, reach out!
Community, privacy, and transparency are important to us. Your tasks are private and we dont sell your personal data to third parties.
If you have any questions, feel free to send feedback to admin@habitica.com! And if you enjoy our app, we would really appreciate it if you would leave us a review.
If you have any questions, feel free to send feedback to admin@habitica.com! And if you enjoy our app, we would really appreciate it if you would leave us a review.

View file

@ -1,2 +1,2 @@
NAME=4.2.3
CODE=6181
CODE=6201

View file

@ -62,7 +62,7 @@ class LoginActivity : BaseActivity<ActivityLoginBinding, LoginViewModel>() {
binding.loginButton.isVisible = true
}
}
binding.root.smoothScrollTo(0, 0)
binding.scrollView.smoothScrollTo(0, 0)
}
override fun onCreate(savedInstanceState: Bundle?) {

View file

@ -32,7 +32,7 @@ class MainActivity : BaseActivity<ActivityMainBinding, MainViewModel>() {
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivityMainBinding.inflate(layoutInflater)
super.onCreate(savedInstanceState)
binding.root.apply {
binding.recyclerView.apply {
layoutManager =
WearableLinearLayoutManager(this@MainActivity, HabiticaScrollingLayoutCallback())
adapter = this@MainActivity.adapter

View file

@ -27,7 +27,7 @@ class SettingsActivity : BaseActivity<ActivitySettingsBinding, SettingsViewModel
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivitySettingsBinding.inflate(layoutInflater)
super.onCreate(savedInstanceState)
binding.root.apply {
binding.recyclerView.apply {
layoutManager =
WearableLinearLayoutManager(this@SettingsActivity, HabiticaScrollingLayoutCallback())
adapter = this@SettingsActivity.adapter

View file

@ -4,7 +4,6 @@ import android.content.Intent
import android.os.Bundle
import android.os.PersistableBundle
import androidx.activity.viewModels
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.habitrpg.android.habitica.databinding.ActivitySplashBinding
import com.habitrpg.wearos.habitica.ui.viewmodels.SplashViewModel
@ -81,7 +80,6 @@ class SplashActivity : BaseActivity<ActivitySplashBinding, SplashViewModel>() {
} else {
stopAnimatingProgress()
}
binding.textView.isVisible = show
delay(90.toDuration(DurationUnit.SECONDS))
if (isActive) {
// the sync attempt has timed out

View file

@ -0,0 +1,67 @@
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.wearable.timetext
import android.view.View
import android.widget.TextView
import androidx.wear.widget.CurvedTextView
/**
* A wrapper around a [TextView] like object, that may not actually extend [TextView] (like a [CurvedTextView]).
*/
interface TextViewWrapper {
val view: View
var text: CharSequence?
var textColor: Int
}
/**
* A [TextViewWrapper] wrapping a [CurvedTextView].
*/
class CurvedTextViewWrapper(
override val view: CurvedTextView
) : TextViewWrapper {
override var text: CharSequence?
get() = view.text
set(value) {
view.text = value?.toString().orEmpty()
}
override var textColor: Int
get() = view.textColor
set(value) {
view.textColor = value
}
}
/**
* A [TextViewWrapper] wrapping a [TextView].
*/
class NormalTextViewWrapper(
override val view: TextView
) : TextViewWrapper {
override var text: CharSequence?
get() = view.text
set(value) {
view.text = value
}
override var textColor: Int
get() = view.currentTextColor
set(value) {
view.setTextColor(value)
}
}

View file

@ -0,0 +1,306 @@
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.habitrpg.wearos.habitica.ui.views
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.database.ContentObserver
import android.graphics.Color
import android.provider.Settings
import android.text.format.DateFormat
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.FrameLayout
import androidx.annotation.AttrRes
import androidx.annotation.StyleRes
import androidx.annotation.VisibleForTesting
import androidx.core.content.res.use
import androidx.core.os.ConfigurationCompat
import androidx.core.view.isGone
import com.example.android.wearable.timetext.CurvedTextViewWrapper
import com.example.android.wearable.timetext.NormalTextViewWrapper
import com.example.android.wearable.timetext.TextViewWrapper
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.CurvedTimeTextBinding
import com.habitrpg.android.habitica.databinding.StraightTimeTextBinding
import com.habitrpg.wearos.habitica.ui.views.TimeText.Clock
import com.habitrpg.wearos.habitica.ui.views.TimeTextViewBinding.TimeTextCurvedViewBinding
import com.habitrpg.wearos.habitica.ui.views.TimeTextViewBinding.TimeTextStraightViewBinding
import java.util.Calendar
/**
* The max sweep angle for the [TimeText] to occupy.
*/
private const val MAX_SWEEP_ANGLE = 90f
class TimeText @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
@AttrRes defStyleAttr: Int = 0,
@StyleRes defStyleRes: Int = 0
) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) {
/**
* The underlying [Calendar] instance for producing the time.
*
* This will be updated in [onTimeZoneChange] in response to any timezone updates.
*/
private var time = Calendar.getInstance()
/**
* True if we should format the time in the 24 hour manner.
*
* This will be updated in [onTimeFormatChange] in response to any format updates.
*/
private var use24HourFormat = DateFormat.is24HourFormat(context)
/**
* An [IntentFilter] for any time related broadcast.
*/
private val timeBroadcastReceiverFilter = IntentFilter().apply {
addAction(Intent.ACTION_TIME_TICK)
addAction(Intent.ACTION_TIME_CHANGED)
addAction(Intent.ACTION_TIMEZONE_CHANGED)
}
private val timeBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
Intent.ACTION_TIMEZONE_CHANGED -> onTimeZoneChange()
Intent.ACTION_TIME_TICK, Intent.ACTION_TIME_CHANGED -> onTimeChange()
}
}
}
/**
* The wrapped view binding for the inflated views.
*/
private val timeTextViewBinding: TimeTextViewBinding
/**
* A non-clock portion of the time text to display.
*/
var title: CharSequence? = null
set(value) {
field = value
timeTextViewBinding.timeTextTitle.text = title
// Only show the title and divider if the title is non-empty
val hideTitle = title.isNullOrEmpty()
timeTextViewBinding.timeTextTitle.view.isGone = hideTitle
timeTextViewBinding.timeTextDivider.view.isGone = hideTitle
}
/**
* The color of the non-clock portion of the time text.
*/
var titleTextColor: Int = Color.WHITE
set(value) {
field = value
timeTextViewBinding.timeTextTitle.textColor = titleTextColor
}
/**
* The backing [Clock] used to drive the time.
*
* Overridable for testing.
*/
@VisibleForTesting
var clock: Clock = Clock(System::currentTimeMillis)
set(value) {
field = value
onTimeChange()
}
/**
* The [ContentObserver] listening for a time format change.
*
* This is constructed lazily, since [getHandler] needs the view to be attached.
*/
private val timeContentObserver by lazy(LazyThreadSafetyMode.NONE) {
object : ContentObserver(handler) {
override fun onChange(selfChange: Boolean) {
super.onChange(selfChange)
onTimeFormatChange()
}
}
}
init {
val layoutInflater = LayoutInflater.from(context)
// Create the view structure based on whether the screen is round.
// This will inflate one of two distinct layouts, which we abstract away in a TimeTextViewBinding
timeTextViewBinding = if (resources.configuration.isScreenRound) {
TimeTextCurvedViewBinding(CurvedTimeTextBinding.inflate(layoutInflater, this, true))
} else {
TimeTextStraightViewBinding(StraightTimeTextBinding.inflate(layoutInflater, this, true))
}
// Set the divider text
timeTextViewBinding.timeTextDivider.text = "·"
// Update based on the styled attributes.
// Note that this runs the side-effects of setting those attributes.
context.obtainStyledAttributes(attrs, R.styleable.TimeText, defStyleAttr, defStyleRes)
.use { typedArray ->
titleTextColor =
typedArray.getColor(R.styleable.TimeText_android_titleTextColor, titleTextColor)
title = typedArray.getString(R.styleable.TimeText_titleText)
}
}
/**
* Restrict the total sweep angle on round screens to [MAX_SWEEP_ANGLE].
*
* We accomplish this with two measure passes:
*
* After the first, we measure to get the angle that the clock and divider occupy (together, these shouldn't ever
* be more than [MAX_SWEEP_ANGLE].
*
* Then, we update the title's max sweep angle to the remaining angle, and measure again to apply the limit.
*/
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
when (timeTextViewBinding) {
is TimeTextCurvedViewBinding -> {
// Reset the title sweep to ensure we get a true initial measurement
timeTextViewBinding.timeTextTitle.view.setSweepRangeDegrees(0f, MAX_SWEEP_ANGLE)
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val clockSweepAngle =
timeTextViewBinding.timeTextClock.view.sweepAngleDegrees.coerceAtLeast(0f)
// Avoid getting the divider sweep angle if it is gone, since it won't be accurate
val dividerSweepAngle = if (timeTextViewBinding.timeTextDivider.view.isGone) {
0f
} else {
timeTextViewBinding.timeTextDivider.view.sweepAngleDegrees.coerceAtLeast(0f)
}
val maxTitleSweepAngle = MAX_SWEEP_ANGLE - clockSweepAngle - dividerSweepAngle
// Update the title max sweep angle to effectively get a total max sweep of MAX_SWEEP_ANGLE
timeTextViewBinding.timeTextTitle.view.setSweepRangeDegrees(0f, maxTitleSweepAngle)
// Measure again, with the updated max sweep
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}
is TimeTextStraightViewBinding -> {
// Need to do nothing special the for the straight view, just call through to super
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
onTimeZoneChange()
onTimeFormatChange()
onTimeChange()
context.contentResolver.registerContentObserver(
Settings.System.getUriFor(Settings.System.TIME_12_24),
true,
timeContentObserver
)
context.registerReceiver(timeBroadcastReceiver, timeBroadcastReceiverFilter)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
context.contentResolver.unregisterContentObserver(timeContentObserver)
context.unregisterReceiver(timeBroadcastReceiver)
}
private fun onTimeChange() {
val pattern = DateFormat.getBestDateTimePattern(
ConfigurationCompat.getLocales(resources.configuration)[0],
if (use24HourFormat) "Hm" else "hm"
)
// Remove the am/pm indicator (if any). This is locale safe.
val patternWithoutAmPm = pattern.replace("a", "").trim()
time.timeInMillis = clock.getCurrentTimeMillis()
timeTextViewBinding.timeTextClock.text = DateFormat.format(patternWithoutAmPm, time)
}
private fun onTimeZoneChange() {
time = Calendar.getInstance()
onTimeChange()
}
private fun onTimeFormatChange() {
use24HourFormat = DateFormat.is24HourFormat(context)
onTimeChange()
}
/**
* A provider of the current time.
*/
fun interface Clock {
/**
* Returns the current time in milliseconds since the epoch.
*/
fun getCurrentTimeMillis(): Long
}
}
/**
* An abstraction around the view binding, since we inflate two different layouts depending on the shape of the screen.
*/
private sealed class TimeTextViewBinding {
abstract val timeTextTitle: TextViewWrapper
abstract val timeTextDivider: TextViewWrapper
abstract val timeTextClock: TextViewWrapper
/**
* The [TimeTextViewBinding] wrapping the [CurvedTimeTextBinding].
*/
class TimeTextCurvedViewBinding(
timeTextBinding: CurvedTimeTextBinding
) : TimeTextViewBinding() {
override val timeTextTitle: CurvedTextViewWrapper =
CurvedTextViewWrapper(timeTextBinding.timeTextTitle)
override val timeTextDivider: CurvedTextViewWrapper =
CurvedTextViewWrapper(timeTextBinding.timeTextDivider)
override val timeTextClock: CurvedTextViewWrapper =
CurvedTextViewWrapper(timeTextBinding.timeTextClock)
}
/**
* The [TimeTextViewBinding] wrapping the [StraightTimeTextBinding].
*/
class TimeTextStraightViewBinding(
timeTextBinding: StraightTimeTextBinding
) : TimeTextViewBinding() {
override val timeTextTitle: NormalTextViewWrapper =
NormalTextViewWrapper(timeTextBinding.timeTextTitle)
override val timeTextDivider: NormalTextViewWrapper =
NormalTextViewWrapper(timeTextBinding.timeTextDivider)
override val timeTextClock: NormalTextViewWrapper =
NormalTextViewWrapper(timeTextBinding.timeTextClock)
}
}

View file

@ -0,0 +1,53 @@
package com.habitrpg.wearos.habitica.util
import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.ViewCompat
import kotlin.math.abs
import kotlin.math.min
class TopScrollAwayBehavior<V : View>(context: Context, attrs: AttributeSet) :
CoordinatorLayout.Behavior<V>(context, attrs) {
override fun onStartNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: V,
directTargetChild: View,
target: View,
axes: Int,
type: Int
): Boolean {
return axes == ViewCompat.SCROLL_AXIS_VERTICAL
}
override fun onNestedPreScroll(
coordinatorLayout: CoordinatorLayout,
child: V,
target: View,
dx: Int,
dy: Int,
consumed: IntArray,
type: Int
) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type)
child.translationY = min(0f, -min(child.height.toFloat(), -child.translationY + dy))
}
override fun onStopNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: V,
target: View,
type: Int
) {
super.onStopNestedScroll(coordinatorLayout, child, target, type)
if (child.translationY != 0f && abs(child.translationY) != child.height.toFloat()) {
if (abs(child.translationY) < (child.height.toFloat() / 2f) && abs(child.translationY) < 40) {
child.translationY = 0f
} else {
child.translationY = if (child.top < child.bottom) -child.height.toFloat() else child.height.toFloat()
}
}
}
}

View file

@ -1,6 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<com.habitrpg.wearos.habitica.ui.views.HabiticaScrollView xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.habitrpg.wearos.habitica.ui.views.HabiticaScrollView
android:id="@+id/scroll_view"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -15,7 +19,8 @@
android:layout_height="16dp"
android:src="@drawable/ic_gryphon_white"
android:importantForAccessibility="no"
android:layout_marginBottom="@dimen/spacing_medium"/>
android:layout_marginBottom="@dimen/spacing_medium"
android:layout_marginTop="24dp"/>
<TextView
android:id="@+id/title_view"
android:layout_width="wrap_content"
@ -110,4 +115,11 @@
android:textAlignment="center"
style="@style/ChipButton.Purple"/>
</LinearLayout>
</com.habitrpg.wearos.habitica.ui.views.HabiticaScrollView>
</com.habitrpg.wearos.habitica.ui.views.HabiticaScrollView>
<com.habitrpg.wearos.habitica.ui.views.TimeText
android:id="@+id/timeText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:titleTextColor="@color/text_primary"
app:layout_behavior="com.habitrpg.wearos.habitica.util.TopScrollAwayBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -1,11 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<com.habitrpg.wearos.habitica.ui.views.HabiticaRecyclerView
<androidx.coordinatorlayout.widget.CoordinatorLayout tools:context="com.habitrpg.wearos.habitica.ui.activities.MainActivity"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.habitrpg.wearos.habitica.ui.activities.MainActivity"
android:scrollbars="vertical"
tools:deviceIds="wear">
</com.habitrpg.wearos.habitica.ui.views.HabiticaRecyclerView>
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.habitrpg.wearos.habitica.ui.views.HabiticaRecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
tools:deviceIds="wear">
</com.habitrpg.wearos.habitica.ui.views.HabiticaRecyclerView>
<com.habitrpg.wearos.habitica.ui.views.TimeText
android:id="@+id/timeText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:titleTextColor="@color/text_primary"
app:layout_behavior="com.habitrpg.wearos.habitica.util.TopScrollAwayBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -1,10 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<com.habitrpg.wearos.habitica.ui.views.HabiticaRecyclerView
<androidx.coordinatorlayout.widget.CoordinatorLayout tools:context="com.habitrpg.wearos.habitica.ui.activities.MainActivity"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.habitrpg.wearos.habitica.ui.activities.SettingsActivity"
tools:deviceIds="wear">
</com.habitrpg.wearos.habitica.ui.views.HabiticaRecyclerView>
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.habitrpg.wearos.habitica.ui.views.HabiticaRecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
tools:deviceIds="wear">
</com.habitrpg.wearos.habitica.ui.views.HabiticaRecyclerView>
<com.habitrpg.wearos.habitica.ui.views.TimeText
android:id="@+id/timeText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:titleTextColor="@color/text_primary"
app:layout_behavior="com.habitrpg.wearos.habitica.util.TopScrollAwayBehavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -1,17 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<TextView
android:id="@+id/text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/syncing_account"
android:layout_margin="@dimen/spacing_xlarge"
android:visibility="gone"
tools:visibility="visible"
android:gravity="center"/>
</LinearLayout>
<FrameLayout
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:background="@drawable/circle"
android:clipChildren="true"
android:clipToOutline="true">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@mipmap/ic_launcher"/>
</FrameLayout>
</FrameLayout>

View file

@ -14,4 +14,10 @@
android:layout_height="39dp"
android:layout_gravity="center|bottom"
app:layout_behavior="com.habitrpg.wearos.habitica.util.ScrollAwayBehavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.habitrpg.wearos.habitica.ui.views.TimeText
android:id="@+id/timeText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:titleTextColor="@color/text_primary"
app:layout_behavior="com.habitrpg.wearos.habitica.util.TopScrollAwayBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<androidx.wear.widget.ArcLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/timeArcLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:anchorPosition="center">
<androidx.wear.widget.CurvedTextView
android:id="@+id/timeTextTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:paddingVertical="4dp"
style="@style/TextTime"
android:textAppearance="@style/TextAppearanceTime"
tools:text="Text" />
<androidx.wear.widget.CurvedTextView
android:id="@+id/timeTextDivider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingVertical="4dp"
style="@style/TextTime"
android:textAppearance="@style/TextAppearanceTime"
tools:text="·" />
<androidx.wear.widget.CurvedTextView
android:id="@+id/timeTextClock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingVertical="4dp"
style="@style/TextTime"
android:textAppearance="@style/TextAppearanceTime"
tools:text="01:23" />
</androidx.wear.widget.ArcLayout>

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="4dp">
<TextView
android:id="@+id/timeTextTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearanceTime"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/timeTextDivider"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"
tools:text="ABCDEFGHIJKLMNOPQRSTUVWXYZ" />
<TextView
android:id="@+id/timeTextDivider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="4dp"
android:textAppearance="@style/TextAppearanceTime"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/timeTextClock"
app:layout_constraintStart_toEndOf="@id/timeTextTitle"
app:layout_constraintTop_toTopOf="parent"
tools:text="·" />
<TextView
android:id="@+id/timeTextClock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearanceTime"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/timeTextDivider"
app:layout_constraintTop_toTopOf="parent"
tools:text="01:23" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -19,4 +19,8 @@
<attr name="chipColor" format="color" />
<attr name="chipTextColor" format="color" />
</declare-styleable>
</resources>
<declare-styleable name="TimeText">
<attr name="android:titleTextColor" format="reference|color" />
<attr name="titleText" format="string" />
</declare-styleable>
</resources>

View file

@ -105,4 +105,12 @@
<item name="android:textColorHint">@color/watch_gray_200</item>
</style>
</resources>
<style name="TextAppearanceTime">
<item name="android:textColor">@android:color/white</item>
<item name="android:textSize">14sp</item>
<item name="android:lineSpacingExtra">4sp</item>
</style>
<style name="TextTime">
<item name="android:paddingHorizontal">4dp</item>
</style>
</resources>