mirror of
https://github.com/home-assistant/android
synced 2024-09-06 17:23:32 +00:00
Push Wear users to install app on phone (#3388)
* Push Wear users to install app on phone - Push Wear OS users to install the app on their phone if they don't already have it, as the sign in experience is a lot better and less sensitive to errors - Allow using 'Advanced' if the user cannot or doesn't want to install the app on their phone * Fix import * Add time
This commit is contained in:
parent
4984c9c1a3
commit
b508114d75
|
@ -14,6 +14,7 @@
|
|||
<string name="add_ssid">Add</string>
|
||||
<string name="add_ssid_name_suggestion">Add %1$s</string>
|
||||
<string name="add_widget">Add widget</string>
|
||||
<string name="advanced">Advanced</string>
|
||||
<string name="all_entities">All entities</string>
|
||||
<string name="allow">Allow</string>
|
||||
<string name="always_show_first_view_on_app_start">Always show first view on app start</string>
|
||||
|
@ -263,7 +264,9 @@
|
|||
<string name="input_cloud">Use Home Assistant Cloud</string>
|
||||
<string name="input_url_hint">https://example.duckdns.org:8123</string>
|
||||
<string name="input_url">Home Assistant URL</string>
|
||||
<string name="install">Install</string>
|
||||
<string name="install_app">Install App on Wear Device</string>
|
||||
<string name="install_phone_to_continue">Install Home Assistant on your phone to continue</string>
|
||||
<plurals name="interval_hours">
|
||||
<item quantity="one">%d hour</item>
|
||||
<item quantity="other">%d hours</item>
|
||||
|
@ -370,9 +373,9 @@
|
|||
<string name="media_player">Media player</string>
|
||||
<string name="media_player_widget_desc">Control any media player and see current now playing image</string>
|
||||
<string name="message_checking">Checking Wear Devices with App</string>
|
||||
<string name="message_missing_all">The Wear app is missing on your watch, click the button below to install the app.\n\nNote: Currently the Wear OS app requires you to be enrolled in the beta for the phone app. If the button does not work then please join the beta: https://play.google.com/apps/testing/io.homeassistant.companion.android</string>
|
||||
<string name="message_missing_all">The Wear app is missing on your watch, click the button below to install the app.</string>
|
||||
<string name="message_no_connected_nodes">No connected Wear devices, please make sure Bluetooth is on and your watch is paired.</string>
|
||||
<string name="message_some_installed">The Wear app is installed on some of your Wear devices: (%1$s)\n\nClick the button below to install the app on the other devices.\n\nNote: Currently the Wear OS app requires you to be enrolled in the beta for the phone app. If the button does not work then please join the beta: https://play.google.com/apps/testing/io.homeassistant.companion.android</string>
|
||||
<string name="message_some_installed">The Wear app is installed on some of your Wear devices: (%1$s)\n\nClick the button below to install the app on the other devices.</string>
|
||||
<string name="missing_command_permission">Please open the Home Assistant app and send the command again in order to grant the proper permissions. You will be taken to a page to either grant the Home Assistant app the permission, or you will need to select Permissions from the details page and then grant the missing permission. For command_bluetooth the name of the permission is Nearby devices. If you are attempting to use command_activity to make a phone call you will also need to grant Phone permissions.</string>
|
||||
<string name="areas">Areas</string>
|
||||
<string name="more_entities">More entities</string>
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
<activity android:name=".onboarding.OnboardingActivity" />
|
||||
<activity android:name=".onboarding.integration.MobileAppIntegrationActivity" />
|
||||
<activity android:name=".onboarding.manual.ManualSetupActivity" />
|
||||
<activity android:name=".onboarding.phoneinstall.PhoneInstallActivity" />
|
||||
<activity android:name=".complications.ComplicationConfigActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
|
|
|
@ -21,7 +21,7 @@ import com.google.android.gms.wearable.Wearable
|
|||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.homeassistant.companion.android.R
|
||||
import io.homeassistant.companion.android.onboarding.integration.MobileAppIntegrationActivity
|
||||
import io.homeassistant.companion.android.onboarding.manual.ManualSetupActivity
|
||||
import io.homeassistant.companion.android.onboarding.phoneinstall.PhoneInstallActivity
|
||||
import io.homeassistant.companion.android.util.LoadingView
|
||||
import kotlinx.coroutines.guava.await
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -49,6 +49,7 @@ class OnboardingActivity : AppCompatActivity(), OnboardingView {
|
|||
private lateinit var loadingView: LoadingView
|
||||
|
||||
private var phoneSignInAvailable = false
|
||||
private var phoneInstallOpened = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -69,7 +70,7 @@ class OnboardingActivity : AppCompatActivity(), OnboardingView {
|
|||
if (phoneSignInAvailable) {
|
||||
startPhoneSignIn(null)
|
||||
} else {
|
||||
startManualSetup()
|
||||
requestPhoneAppInstall()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,9 +108,7 @@ class OnboardingActivity : AppCompatActivity(), OnboardingView {
|
|||
Wearable.getDataClient(this).removeListener(presenter)
|
||||
}
|
||||
|
||||
private fun startManualSetup() {
|
||||
startActivity(ManualSetupActivity.newInstance(this))
|
||||
}
|
||||
private fun requestPhoneAppInstall() = startActivity(PhoneInstallActivity.newInstance(this))
|
||||
|
||||
private fun startPhoneSignIn(instance: HomeAssistantInstance?) {
|
||||
lifecycleScope.launch {
|
||||
|
@ -131,7 +130,7 @@ class OnboardingActivity : AppCompatActivity(), OnboardingView {
|
|||
if (instance != null) {
|
||||
presenter.onInstanceClickedWithoutApp(this@OnboardingActivity, instance.url.toString())
|
||||
} else {
|
||||
startManualSetup()
|
||||
requestPhoneAppInstall()
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Unable to open sign in activity on phone", e)
|
||||
|
@ -241,6 +240,11 @@ class OnboardingActivity : AppCompatActivity(), OnboardingView {
|
|||
|
||||
Log.d(TAG, "requestPhoneSignIn: found ${capabilityInfo.nodes.size} nodes")
|
||||
phoneSignInAvailable = capabilityInfo.nodes.size > 0
|
||||
|
||||
if (!phoneSignInAvailable && !phoneInstallOpened) {
|
||||
phoneInstallOpened = true
|
||||
requestPhoneAppInstall()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package io.homeassistant.companion.android.onboarding.phoneinstall
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.wear.activity.ConfirmationActivity
|
||||
import androidx.wear.remote.interactions.RemoteActivityHelper
|
||||
import io.homeassistant.companion.android.BuildConfig
|
||||
import io.homeassistant.companion.android.onboarding.manual.ManualSetupActivity
|
||||
import io.homeassistant.companion.android.theme.WearAppTheme
|
||||
import kotlinx.coroutines.guava.await
|
||||
import kotlinx.coroutines.launch
|
||||
import io.homeassistant.companion.android.common.R as commonR
|
||||
|
||||
class PhoneInstallActivity : AppCompatActivity() {
|
||||
companion object {
|
||||
private const val TAG = "PhoneInstallActivity"
|
||||
|
||||
fun newInstance(context: Context): Intent {
|
||||
return Intent(context, PhoneInstallActivity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var remoteActivityHelper: RemoteActivityHelper
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContent {
|
||||
WearAppTheme {
|
||||
PhoneInstallView(
|
||||
onInstall = ::openPlayStoreOnPhone,
|
||||
onRefresh = {
|
||||
finish() // OnboardingActivity will refresh when resumed
|
||||
},
|
||||
onAdvanced = {
|
||||
startActivity(ManualSetupActivity.newInstance(this@PhoneInstallActivity))
|
||||
finish()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
remoteActivityHelper = RemoteActivityHelper(this)
|
||||
}
|
||||
|
||||
private fun openPlayStoreOnPhone() {
|
||||
lifecycleScope.launch {
|
||||
var success = true
|
||||
try {
|
||||
remoteActivityHelper.startRemoteActivity(
|
||||
Intent(Intent.ACTION_VIEW).apply {
|
||||
addCategory(Intent.CATEGORY_DEFAULT)
|
||||
addCategory(Intent.CATEGORY_BROWSABLE)
|
||||
data = Uri.parse("https://play.google.com/store/apps/details?id=${BuildConfig.APPLICATION_ID}")
|
||||
},
|
||||
null // a Wear device only has one companion device so this is not needed
|
||||
).await()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to open remote activity", e)
|
||||
success = false
|
||||
}
|
||||
val confirmation =
|
||||
Intent(this@PhoneInstallActivity, ConfirmationActivity::class.java).apply {
|
||||
putExtra(
|
||||
ConfirmationActivity.EXTRA_ANIMATION_TYPE,
|
||||
if (success) { ConfirmationActivity.OPEN_ON_PHONE_ANIMATION } else { ConfirmationActivity.FAILURE_ANIMATION }
|
||||
)
|
||||
if (success) {
|
||||
putExtra(ConfirmationActivity.EXTRA_ANIMATION_DURATION_MILLIS, 2000)
|
||||
}
|
||||
putExtra(
|
||||
ConfirmationActivity.EXTRA_MESSAGE,
|
||||
getString(if (success) { commonR.string.continue_on_phone } else { commonR.string.failed_phone_connection })
|
||||
)
|
||||
}
|
||||
startActivity(confirmation)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package io.homeassistant.companion.android.onboarding.phoneinstall
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Devices
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.wear.compose.material.Button
|
||||
import androidx.wear.compose.material.ButtonDefaults
|
||||
import androidx.wear.compose.material.MaterialTheme
|
||||
import androidx.wear.compose.material.PositionIndicator
|
||||
import androidx.wear.compose.material.Scaffold
|
||||
import androidx.wear.compose.material.Text
|
||||
import androidx.wear.compose.material.rememberScalingLazyListState
|
||||
import io.homeassistant.companion.android.R
|
||||
import io.homeassistant.companion.android.home.views.TimeText
|
||||
import io.homeassistant.companion.android.views.ThemeLazyColumn
|
||||
import io.homeassistant.companion.android.common.R as commonR
|
||||
|
||||
@Composable
|
||||
fun PhoneInstallView(
|
||||
onInstall: () -> Unit,
|
||||
onRefresh: () -> Unit,
|
||||
onAdvanced: () -> Unit
|
||||
) {
|
||||
val scrollState = rememberScalingLazyListState()
|
||||
Scaffold(
|
||||
positionIndicator = {
|
||||
if (scrollState.isScrollInProgress) {
|
||||
PositionIndicator(scalingLazyListState = scrollState)
|
||||
}
|
||||
},
|
||||
timeText = { TimeText(visible = !scrollState.isScrollInProgress) }
|
||||
) {
|
||||
Box(modifier = Modifier.background(MaterialTheme.colors.background)) {
|
||||
ThemeLazyColumn(state = scrollState) {
|
||||
item {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.app_icon),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(48.dp)
|
||||
)
|
||||
}
|
||||
item {
|
||||
Text(
|
||||
text = stringResource(commonR.string.install_phone_to_continue),
|
||||
style = MaterialTheme.typography.title3,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 16.dp)
|
||||
)
|
||||
}
|
||||
item {
|
||||
Button(
|
||||
onClick = onInstall,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(commonR.string.install))
|
||||
}
|
||||
}
|
||||
item {
|
||||
Button(
|
||||
onClick = onRefresh,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = ButtonDefaults.secondaryButtonColors()
|
||||
) {
|
||||
Text(stringResource(commonR.string.refresh))
|
||||
}
|
||||
}
|
||||
item {
|
||||
Button(
|
||||
onClick = onAdvanced,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp),
|
||||
colors = ButtonDefaults.secondaryButtonColors()
|
||||
) {
|
||||
Text(stringResource(commonR.string.advanced))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(device = Devices.WEAR_OS_LARGE_ROUND)
|
||||
@Composable
|
||||
fun PhoneInstallViewPreview() {
|
||||
PhoneInstallView(
|
||||
onInstall = { },
|
||||
onRefresh = { },
|
||||
onAdvanced = { }
|
||||
)
|
||||
}
|
Loading…
Reference in a new issue