Add support for ambient mode on wearable

This commit is contained in:
William Brawner 2020-09-22 17:57:04 -07:00
parent 26c6977fd6
commit fbf60ac13e
11 changed files with 214 additions and 53 deletions

View file

@ -0,0 +1,123 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
<option name="WRAP_ON_TYPING" value="1" />
</codeStyleSettings>
</code_scheme>
</component>

View file

@ -18,8 +18,8 @@ import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.viewModelScope
import androidx.navigation.fragment.findNavController
import com.robinhood.ticker.TickerUtils
import com.google.android.gms.wearable.*
import com.robinhood.ticker.TickerUtils
import com.wbrawner.trainterval.Logger
import com.wbrawner.trainterval.R
import com.wbrawner.trainterval.shared.IntervalTimerDao
@ -92,7 +92,7 @@ class ActiveTimerFragment : Fragment(), MessageClient.OnMessageReceivedListener
it.setSupportActionBar(toolbar)
it.supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
timeRemaining.setCharacterLists(TickerUtils.provideNumberList() + ":")
timeRemaining.setCharacterLists(TickerUtils.provideNumberList())
timerSets.setCharacterLists(TickerUtils.provideNumberList())
timerRounds.setCharacterLists(TickerUtils.provideNumberList())
coroutineScope = CoroutineScope(Dispatchers.Main)
@ -137,7 +137,6 @@ class ActiveTimerFragment : Fragment(), MessageClient.OnMessageReceivedListener
}
(activity as? AppCompatActivity)?.supportActionBar?.title = state.timerName
val backgroundColor = resources.getColor(state.phase.colorRes, context?.theme)
Log.d("ActiveTimerFragment", "State: $state")
state.previousPhase?.let {
val previousBackgroundColor = resources.getColor(it.colorRes, context?.theme)
val colorAnimation =

View file

@ -56,6 +56,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.wear:wear:1.0.0'
implementation 'com.google.android.support:wearable:2.7.0'
implementation 'androidx.appcompat:appcompat:1.2.0'
compileOnly 'com.google.android.wearable:wearable:2.7.0'
testImplementation 'junit:junit:4.12'
}

View file

@ -1,12 +1,15 @@
package com.wbrawner.trainterval.wear
import android.content.Context
import android.graphics.Color
import android.os.Bundle
import android.os.VibrationEffect
import android.os.Vibrator
import android.support.wearable.activity.WearableActivity
import android.util.Log
import android.view.View
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import androidx.wear.ambient.AmbientModeSupport
import com.google.android.gms.wearable.*
import com.wbrawner.trainterval.R
import com.wbrawner.trainterval.shared.IntervalTimerState
@ -15,12 +18,16 @@ import com.wbrawner.trainterval.shared.IntervalTimerState.Companion.TIMER_STATE
import com.wbrawner.trainterval.shared.toIntervalTimerState
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : WearableActivity(), DataClient.OnDataChangedListener {
class MainActivity : FragmentActivity(),
AmbientModeSupport.AmbientCallbackProvider,
DataClient.OnDataChangedListener {
private lateinit var dataClient: DataClient
private lateinit var messageClient: MessageClient
private lateinit var nodeClient: NodeClient
private lateinit var vibrator: Vibrator
private lateinit var ambientController: AmbientModeSupport.AmbientController
private var lastState: IntervalTimerState? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -29,7 +36,7 @@ class MainActivity : WearableActivity(), DataClient.OnDataChangedListener {
messageClient = Wearable.getMessageClient(this)
nodeClient = Wearable.getNodeClient(this)
vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
setAmbientEnabled()
ambientController = AmbientModeSupport.attach(this)
}
override fun onResume() {
@ -49,30 +56,48 @@ class MainActivity : WearableActivity(), DataClient.OnDataChangedListener {
.dataMap
.toIntervalTimerState()
?: return@forEach
when (intervalTimerState) {
is IntervalTimerState.LoadingState -> timeRemaining.text = "Loading"
is IntervalTimerState.TimerRunningState -> {
val backgroundColor =
resources.getColor(intervalTimerState.phase.colorRes, theme)
timerRoot.setBackgroundColor(backgroundColor)
timeRemaining.text = intervalTimerState.timeRemaining
lastState = intervalTimerState
renderState()
}
}
private fun renderState() {
val intervalTimerState = lastState ?: return
when (intervalTimerState) {
is IntervalTimerState.LoadingState -> timeRemaining.text = "Loading"
is IntervalTimerState.TimerRunningState -> {
val backgroundColor = if (ambientController.isAmbient) Color.BLACK
else resources.getColor(intervalTimerState.phase.colorRes, theme)
timerRoot.setBackgroundColor(backgroundColor)
timeRemaining.text = intervalTimerState.timeRemaining
val textColor = if (ambientController.isAmbient) resources.getColor(
intervalTimerState.phase.colorRes,
theme
)
else Color.BLACK
timeRemaining.setTextColor(textColor)
if (ambientController.isAmbient) {
toggleButton.visibility = View.GONE
} else {
toggleButton.visibility = View.VISIBLE
toggleButton.setImageDrawable(
getDrawable(
if (intervalTimerState.isRunning) R.drawable.ic_pause_inset
else R.drawable.ic_play_inset
ContextCompat.getDrawable(
this,
if (intervalTimerState.isRunning) R.drawable.ic_pause
else R.drawable.ic_play_arrow
)
)
}
if (intervalTimerState.vibrate) {
vibrator.vibrate(
VibrationEffect.createWaveform(
longArrayOf(0L, 100L, 50L, 100L),
-1
)
)
if (intervalTimerState.vibrate) {
vibrator.vibrate(
VibrationEffect.createWaveform(
longArrayOf(0L, 100L, 50L, 100L),
-1
)
)
}
}
is IntervalTimerState.ExitState -> timeRemaining.text = "Exit"
}
is IntervalTimerState.ExitState -> timeRemaining.text = "Exit"
}
}
@ -91,12 +116,16 @@ class MainActivity : WearableActivity(), DataClient.OnDataChangedListener {
}
}
override fun onEnterAmbient(ambientDetails: Bundle?) {
super.onEnterAmbient(ambientDetails)
}
override fun getAmbientCallback(): AmbientModeSupport.AmbientCallback =
object : AmbientModeSupport.AmbientCallback() {
override fun onEnterAmbient(ambientDetails: Bundle?) {
super.onEnterAmbient(ambientDetails)
renderState()
}
override fun onExitAmbient() {
super.onExitAmbient()
}
override fun onExitAmbient() {
super.onExitAmbient()
renderState()
}
}
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/background_round_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/background_round_pressed" android:state_focused="true" />
<item android:drawable="@drawable/background_round_normal" />
</selector>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:height="48dp"
android:width="48dp" />
<solid android:color="#40000000" />
</shape>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:height="48dp"
android:width="48dp" />
<solid android:color="#80000000" />
</shape>

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_pause"
android:insetTop="5dp"
android:insetBottom="5dp" />

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_play_arrow"
android:insetTop="5dp"
android:insetBottom="5dp" />

View file

@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/timerRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorSurface">
android:background="@color/colorSurface"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/timeRemaining"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_gravity="center"
android:autoSizeTextType="uniform"
android:fontFamily="monospace"
android:gravity="center"
android:padding="16dp"
android:textAlignment="center"
android:textColor="@color/colorOnSurface"
@ -21,14 +21,13 @@
<ImageButton
android:id="@+id/toggleButton"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:backgroundTint="#40000000"
android:background="@drawable/background_round"
android:contentDescription="@string/toggle_timer"
android:fitsSystemWindows="true"
android:foregroundGravity="center"
android:onClick="toggleTimer"
android:padding="8dp"
android:src="@drawable/ic_play_arrow" />
</FrameLayout>
</LinearLayout>

View file

@ -1,5 +1,3 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.Wearable" />
</resources>