Skip to content

Commit d1030b8

Browse files
authored
[Install App prompt] Improve UI according to redlines (#2026)
1 parent 4042bc4 commit d1030b8

File tree

9 files changed

+206
-106
lines changed

9 files changed

+206
-106
lines changed

datalayer/phone-ui/api/current.api

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ package com.google.android.horologist.datalayer.phone.ui {
33

44
@com.google.android.horologist.annotations.ExperimentalHorologistApi public final class PhoneUiDataLayerHelper {
55
ctor public PhoneUiDataLayerHelper();
6-
method public android.content.Intent getInstallPromptIntent(android.content.Context context, String appName, String appPackageName, String watchName, String message, @DrawableRes int image);
7-
method public void showInstallAppPrompt(android.app.Activity activity, String appName, String appPackageName, String watchName, String message, @DrawableRes int image, optional int requestCode);
6+
method public android.content.Intent getInstallPromptIntent(android.content.Context context, String appPackageName, @DrawableRes int image, String topMessage, String bottomMessage);
7+
method public void showInstallAppPrompt(android.app.Activity activity, String appPackageName, @DrawableRes int image, String topMessage, String bottomMessage, optional int requestCode);
88
}
99

1010
}
@@ -20,7 +20,7 @@ package com.google.android.horologist.datalayer.phone.ui.play {
2020
package com.google.android.horologist.datalayer.phone.ui.prompt.installapp {
2121

2222
public final class InstallAppBottomSheetKt {
23-
method @androidx.compose.runtime.Composable public static void InstallAppBottomSheet(String message, String appName, String watchName, kotlin.jvm.functions.Function0<kotlin.Unit>? icon, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function0<kotlin.Unit> onConfirmation, optional androidx.compose.material3.SheetState sheetState);
23+
method @androidx.compose.runtime.Composable public static void InstallAppBottomSheet(kotlin.jvm.functions.Function0<kotlin.Unit>? image, String topMessage, String bottomMessage, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function0<kotlin.Unit> onConfirmation, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SheetState sheetState);
2424
}
2525

2626
}

datalayer/phone-ui/src/main/java/com/google/android/horologist/datalayer/phone/ui/PhoneUiDataLayerHelper.kt

+8-12
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,18 @@ public class PhoneUiDataLayerHelper {
4242
*/
4343
public fun showInstallAppPrompt(
4444
activity: Activity,
45-
appName: String,
4645
appPackageName: String,
47-
watchName: String,
48-
message: String,
4946
@DrawableRes image: Int,
47+
topMessage: String,
48+
bottomMessage: String,
5049
requestCode: Int = NO_RESULT_REQUESTED_REQUEST_CODE,
5150
) {
5251
val intent = getInstallPromptIntent(
5352
context = activity,
54-
appName = appName,
5553
appPackageName = appPackageName,
56-
watchName = watchName,
57-
message = message,
5854
image = image,
55+
topMessage = topMessage,
56+
bottomMessage = bottomMessage,
5957
)
6058
activity.startActivityForResult(
6159
intent,
@@ -97,17 +95,15 @@ public class PhoneUiDataLayerHelper {
9795
*/
9896
public fun getInstallPromptIntent(
9997
context: Context,
100-
appName: String,
10198
appPackageName: String,
102-
watchName: String,
103-
message: String,
10499
@DrawableRes image: Int,
100+
topMessage: String,
101+
bottomMessage: String,
105102
): Intent = InstallAppBottomSheetActivity.getIntent(
106103
context = context,
107-
appName = appName,
108104
appPackageName = appPackageName,
109-
watchName = watchName,
110-
message = message,
111105
image = image,
106+
topMessage = topMessage,
107+
bottomMessage = bottomMessage,
112108
)
113109
}

datalayer/phone-ui/src/main/java/com/google/android/horologist/datalayer/phone/ui/prompt/installapp/InstallAppBottomSheet.kt

+170-60
Original file line numberDiff line numberDiff line change
@@ -16,116 +16,228 @@
1616

1717
package com.google.android.horologist.datalayer.phone.ui.prompt.installapp
1818

19+
import android.content.res.Configuration
20+
import androidx.compose.foundation.layout.Arrangement
1921
import androidx.compose.foundation.layout.Box
2022
import androidx.compose.foundation.layout.Column
23+
import androidx.compose.foundation.layout.Row
24+
import androidx.compose.foundation.layout.Spacer
2125
import androidx.compose.foundation.layout.fillMaxWidth
22-
import androidx.compose.foundation.layout.heightIn
26+
import androidx.compose.foundation.layout.height
2327
import androidx.compose.foundation.layout.padding
28+
import androidx.compose.foundation.rememberScrollState
29+
import androidx.compose.foundation.verticalScroll
2430
import androidx.compose.material.icons.Icons
2531
import androidx.compose.material.icons.filled.Email
2632
import androidx.compose.material3.Button
2733
import androidx.compose.material3.ExperimentalMaterial3Api
2834
import androidx.compose.material3.Icon
2935
import androidx.compose.material3.MaterialTheme
3036
import androidx.compose.material3.ModalBottomSheet
31-
import androidx.compose.material3.OutlinedButton
3237
import androidx.compose.material3.SheetState
3338
import androidx.compose.material3.Text
39+
import androidx.compose.material3.TextButton
3440
import androidx.compose.material3.rememberModalBottomSheetState
3541
import androidx.compose.runtime.Composable
3642
import androidx.compose.ui.Alignment
3743
import androidx.compose.ui.Modifier
44+
import androidx.compose.ui.platform.LocalConfiguration
3845
import androidx.compose.ui.res.stringResource
3946
import androidx.compose.ui.text.style.TextAlign
4047
import androidx.compose.ui.tooling.preview.Preview
4148
import androidx.compose.ui.unit.dp
4249
import com.google.android.horologist.datalayer.phone.ui.R
4350

51+
// Constants from the redlines.
52+
private val PADDING_GREEN = 12.dp
53+
private val PADDING_PINK = 16.dp
54+
private val PADDING_PURPLE = 24.dp
55+
private val PADDING_BLUE = 32.dp
56+
4457
@OptIn(ExperimentalMaterial3Api::class)
4558
@Composable
4659
public fun InstallAppBottomSheet(
47-
message: String,
48-
appName: String,
49-
watchName: String,
50-
icon: @Composable (() -> Unit)?,
60+
image: @Composable (() -> Unit)?,
61+
topMessage: String,
62+
bottomMessage: String,
5163
onDismissRequest: () -> Unit,
5264
onConfirmation: () -> Unit,
65+
modifier: Modifier = Modifier,
5366
sheetState: SheetState = rememberModalBottomSheetState(),
5467
) {
5568
ModalBottomSheet(
5669
onDismissRequest = onDismissRequest,
70+
modifier = modifier,
5771
sheetState = sheetState,
72+
dragHandle = null,
5873
) {
59-
InstallAppBottomSheetContent(
60-
message = message,
61-
appName = appName,
62-
watchName = watchName,
63-
icon = icon,
64-
onDismissRequest = onDismissRequest,
65-
onConfirmation = onConfirmation,
66-
)
74+
val configuration = LocalConfiguration.current
75+
when (configuration.orientation) {
76+
Configuration.ORIENTATION_PORTRAIT -> {
77+
InstallAppBottomSheetPortraitContent(
78+
image = image,
79+
topMessage = topMessage,
80+
bottomMessage = bottomMessage,
81+
onDismissRequest = onDismissRequest,
82+
onConfirmation = onConfirmation,
83+
)
84+
}
85+
86+
else -> {
87+
InstallAppBottomSheetLandscapeContent(
88+
image = image,
89+
topMessage = topMessage,
90+
bottomMessage = bottomMessage,
91+
onDismissRequest = onDismissRequest,
92+
onConfirmation = onConfirmation,
93+
)
94+
}
95+
}
6796
}
6897
}
6998

7099
@Composable
71-
internal fun InstallAppBottomSheetContent(
72-
message: String,
73-
appName: String,
74-
watchName: String,
75-
icon: @Composable (() -> Unit)?,
100+
internal fun InstallAppBottomSheetPortraitContent(
101+
image: @Composable (() -> Unit)?,
102+
topMessage: String,
103+
bottomMessage: String,
76104
onDismissRequest: () -> Unit,
77105
onConfirmation: () -> Unit,
106+
modifier: Modifier = Modifier,
78107
) {
79-
Column(modifier = Modifier.padding(horizontal = 20.dp)) {
80-
icon?.let {
108+
Column(
109+
modifier = modifier
110+
.padding(PADDING_PINK)
111+
.fillMaxWidth()
112+
.verticalScroll(rememberScrollState()),
113+
) {
114+
image?.let {
81115
Box(
82116
modifier = Modifier
83-
.heightIn(min = 0.dp, max = 180.dp)
84-
.padding(vertical = 20.dp)
117+
.padding(top = PADDING_PURPLE)
85118
.align(Alignment.CenterHorizontally),
86119
) {
87-
icon()
120+
image()
88121
}
89122
}
90123

91-
Text(
92-
text = message,
93-
modifier = Modifier
94-
.fillMaxWidth()
95-
.padding(bottom = 10.dp),
96-
textAlign = TextAlign.Center,
97-
style = MaterialTheme.typography.titleMedium,
98-
)
99-
100-
Text(
101-
text = stringResource(
102-
id = R.string.horologist_install_app_prompt_title,
103-
appName,
104-
watchName,
105-
),
106-
modifier = Modifier.fillMaxWidth(),
107-
textAlign = TextAlign.Center,
108-
)
109-
110-
Box(
124+
if (topMessage.isNotBlank()) {
125+
Text(
126+
text = topMessage,
127+
modifier = Modifier
128+
.padding(top = PADDING_PURPLE)
129+
.fillMaxWidth(),
130+
color = MaterialTheme.colorScheme.onSurface,
131+
textAlign = TextAlign.Center,
132+
maxLines = 3,
133+
style = MaterialTheme.typography.titleLarge,
134+
)
135+
}
136+
137+
if (bottomMessage.isNotBlank()) {
138+
Text(
139+
text = bottomMessage,
140+
modifier = Modifier
141+
.padding(top = PADDING_PINK)
142+
.fillMaxWidth(),
143+
color = MaterialTheme.colorScheme.onSurfaceVariant,
144+
textAlign = TextAlign.Center,
145+
maxLines = 3,
146+
style = MaterialTheme.typography.bodyLarge,
147+
)
148+
}
149+
150+
Spacer(modifier = Modifier.height(PADDING_PURPLE))
151+
152+
Row(
111153
modifier = Modifier
112-
.padding(top = 20.dp, bottom = 20.dp)
154+
.padding(horizontal = PADDING_PINK)
113155
.fillMaxWidth(),
156+
horizontalArrangement = Arrangement.End,
114157
) {
115-
OutlinedButton(
158+
TextButton(
116159
onClick = onDismissRequest,
117160
modifier = Modifier
118-
.padding(start = 8.dp)
119-
.align(Alignment.CenterStart),
161+
.padding(end = PADDING_GREEN),
120162
) {
121163
Text(stringResource(id = R.string.horologist_install_app_prompt_cancel_btn_label))
122164
}
123165

124166
Button(
125167
onClick = onConfirmation,
168+
) {
169+
Text(stringResource(id = R.string.horologist_install_app_prompt_ok_btn_label))
170+
}
171+
}
172+
}
173+
}
174+
175+
@Composable
176+
internal fun InstallAppBottomSheetLandscapeContent(
177+
image: @Composable (() -> Unit)?,
178+
topMessage: String,
179+
bottomMessage: String,
180+
onDismissRequest: () -> Unit,
181+
onConfirmation: () -> Unit,
182+
modifier: Modifier = Modifier,
183+
) {
184+
Column(
185+
modifier = modifier
186+
.padding(horizontal = PADDING_PURPLE)
187+
.padding(top = PADDING_BLUE)
188+
.verticalScroll(rememberScrollState()),
189+
) {
190+
Row {
191+
image?.let {
192+
Box(modifier = Modifier.padding(end = PADDING_PURPLE)) {
193+
image()
194+
}
195+
}
196+
197+
Column {
198+
if (topMessage.isNotBlank()) {
199+
Text(
200+
text = topMessage,
201+
modifier = Modifier
202+
.fillMaxWidth(),
203+
color = MaterialTheme.colorScheme.onSurface,
204+
textAlign = TextAlign.Start,
205+
maxLines = 3,
206+
style = MaterialTheme.typography.titleLarge,
207+
)
208+
}
209+
210+
if (bottomMessage.isNotBlank()) {
211+
Text(
212+
text = bottomMessage,
213+
modifier = Modifier
214+
.padding(top = PADDING_PINK)
215+
.fillMaxWidth(),
216+
color = MaterialTheme.colorScheme.onSurfaceVariant,
217+
textAlign = TextAlign.Start,
218+
maxLines = 3,
219+
style = MaterialTheme.typography.bodyLarge,
220+
)
221+
}
222+
}
223+
}
224+
225+
Row(
226+
modifier = Modifier
227+
.padding(top = PADDING_BLUE, bottom = PADDING_PINK)
228+
.fillMaxWidth(),
229+
horizontalArrangement = Arrangement.End,
230+
) {
231+
TextButton(
232+
onClick = onDismissRequest,
126233
modifier = Modifier
127-
.padding(end = 8.dp)
128-
.align(Alignment.CenterEnd),
234+
.padding(end = PADDING_GREEN),
235+
) {
236+
Text(stringResource(id = R.string.horologist_install_app_prompt_cancel_btn_label))
237+
}
238+
239+
Button(
240+
onClick = onConfirmation,
129241
) {
130242
Text(stringResource(id = R.string.horologist_install_app_prompt_ok_btn_label))
131243
}
@@ -136,11 +248,10 @@ internal fun InstallAppBottomSheetContent(
136248
@Preview(showBackground = true)
137249
@Composable
138250
private fun InstallAppBottomSheetContentPreview() {
139-
InstallAppBottomSheetContent(
140-
message = "Stay productive and manage emails right from your wrist.",
141-
appName = "Gmail",
142-
watchName = "Pixel Watch",
143-
icon = { Icon(Icons.Default.Email, contentDescription = null) },
251+
InstallAppBottomSheetPortraitContent(
252+
image = { Icon(Icons.Default.Email, contentDescription = null) },
253+
topMessage = "Stay productive and manage emails right from your wrist.",
254+
bottomMessage = "Add the Gmail app to your Wear OS watch for easy access wherever you are.",
144255
onDismissRequest = { },
145256
onConfirmation = { },
146257
)
@@ -149,11 +260,10 @@ private fun InstallAppBottomSheetContentPreview() {
149260
@Preview(showBackground = true)
150261
@Composable
151262
private fun InstallAppBottomSheetContentPreviewNoIcon() {
152-
InstallAppBottomSheetContent(
153-
message = "Stay productive and manage emails right from your wrist.",
154-
appName = "Gmail",
155-
watchName = "Pixel Watch",
156-
icon = null,
263+
InstallAppBottomSheetPortraitContent(
264+
image = null,
265+
topMessage = "Stay productive and manage emails right from your wrist.",
266+
bottomMessage = "Add the Gmail app to your Wear OS watch for easy access wherever you are.",
157267
onDismissRequest = { },
158268
onConfirmation = { },
159269
)

0 commit comments

Comments
 (0)