Add support for ambient mode on wearable
This commit is contained in:
parent
26c6977fd6
commit
fbf60ac13e
11 changed files with 214 additions and 53 deletions
123
.idea/codeStyles/Project.xml
Normal file
123
.idea/codeStyles/Project.xml
Normal 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>
|
|
@ -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 =
|
||||
|
|
|
@ -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'
|
||||
}
|
|
@ -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,19 +56,38 @@ class MainActivity : WearableActivity(), DataClient.OnDataChangedListener {
|
|||
.dataMap
|
||||
.toIntervalTimerState()
|
||||
?: return@forEach
|
||||
lastState = intervalTimerState
|
||||
renderState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderState() {
|
||||
val intervalTimerState = lastState ?: return
|
||||
when (intervalTimerState) {
|
||||
is IntervalTimerState.LoadingState -> timeRemaining.text = "Loading"
|
||||
is IntervalTimerState.TimerRunningState -> {
|
||||
val backgroundColor =
|
||||
resources.getColor(intervalTimerState.phase.colorRes, theme)
|
||||
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(
|
||||
|
@ -74,7 +100,6 @@ class MainActivity : WearableActivity(), DataClient.OnDataChangedListener {
|
|||
is IntervalTimerState.ExitState -> timeRemaining.text = "Exit"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun toggleTimer(@Suppress("UNUSED_PARAMETER") view: View) {
|
||||
nodeClient.connectedNodes.addOnSuccessListener { nodes ->
|
||||
|
@ -91,12 +116,16 @@ class MainActivity : WearableActivity(), DataClient.OnDataChangedListener {
|
|||
}
|
||||
}
|
||||
|
||||
override fun getAmbientCallback(): AmbientModeSupport.AmbientCallback =
|
||||
object : AmbientModeSupport.AmbientCallback() {
|
||||
override fun onEnterAmbient(ambientDetails: Bundle?) {
|
||||
super.onEnterAmbient(ambientDetails)
|
||||
renderState()
|
||||
}
|
||||
|
||||
override fun onExitAmbient() {
|
||||
super.onExitAmbient()
|
||||
|
||||
renderState()
|
||||
}
|
||||
}
|
||||
}
|
6
wear/src/main/res/drawable/background_round.xml
Normal file
6
wear/src/main/res/drawable/background_round.xml
Normal 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>
|
8
wear/src/main/res/drawable/background_round_normal.xml
Normal file
8
wear/src/main/res/drawable/background_round_normal.xml
Normal 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>
|
8
wear/src/main/res/drawable/background_round_pressed.xml
Normal file
8
wear/src/main/res/drawable/background_round_pressed.xml
Normal 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>
|
|
@ -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" />
|
|
@ -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" />
|
|
@ -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>
|
|
@ -1,5 +1,3 @@
|
|||
<resources>
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.Wearable" />
|
||||
|
||||
</resources>
|
Loading…
Reference in a new issue