Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
11a08e9
Extract compact layout, medium layout, and shared components into the…
devbridie Aug 28, 2025
0498d27
Add remote configuration for disabling XR layouts
devbridie Aug 28, 2025
b0a3b06
Add XR libraries and a new core/xr project with XR utils
devbridie Aug 28, 2025
92a8b89
Add Spatial layout type and a spatial layout for the Home screen
devbridie Aug 28, 2025
45300c1
Add transparent squiggle to match design spec
devbridie Aug 29, 2025
5191b97
Separate XR components into xr namespace
devbridie Aug 29, 2025
512f78a
Fix broken previews in HomeScreen.kt
devbridie Aug 29, 2025
4ce5edb
Create XR composition locals that will never throw to work around b/4…
devbridie Aug 29, 2025
3a2b2b0
Refactor the XR disable flag to XR enable flag
devbridie Sep 1, 2025
6d1b598
Match new design spec.
devbridie Sep 2, 2025
80e97bd
Refactor TopAppBar to accept multiple actions.
devbridie Sep 2, 2025
3bccd2d
Add To Full Space button to TopAppBar
devbridie Sep 2, 2025
cea059e
Move To Full Space string to :core:xr.
devbridie Sep 2, 2025
91210a2
Refactor To Home Space Mode description to stringResource
devbridie Sep 2, 2025
a4d6be7
Hide To Full Space Mode button when XR is not enabled
devbridie Sep 2, 2025
e100dce
Workaround for mainPanelEntity appearing when you have transitioning …
devbridie Aug 21, 2025
e1899f6
Disable shared transitions in the Spatial layout
devbridie Sep 4, 2025
791d36f
Fix HomeScreenScreenshotTest
devbridie Sep 4, 2025
9ce239b
Merge branch 'main' into feature/xr/home-layout
devbridie Sep 8, 2025
3754280
Add xr_feature_enabled in remote_config_defaults.xml
devbridie Sep 8, 2025
6ed3da4
Fix HomeScreenTest
devbridie Sep 8, 2025
3a712dd
Fix incorrect color namespace
devbridie Sep 8, 2025
a1f5582
Fix release variant compilation
devbridie Sep 8, 2025
e75f21a
Merge remote-tracking branch 'upstream/main' into feature/xr/home-layout
devbridie Sep 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ dependencies {
implementation(projects.core.theme)
implementation(projects.core.util)

// library must be compileOnly, see
// https://developer.android.com/develop/xr/jetpack-xr-sdk/getting-started#enable-minification
compileOnly(libs.androidx.xr.extensions)

baselineProfile(projects.benchmark)

// Android Instrumented Tests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ interface RemoteConfigDataSource {
fun getBotBackgroundInstructionPrompt(): String

fun watchfaceFeatureEnabled(): Boolean

fun isXrEnabled(): Boolean
}

@Singleton
Expand Down Expand Up @@ -117,4 +119,8 @@ class RemoteConfigDataSourceImpl @Inject constructor() : RemoteConfigDataSource
override fun watchfaceFeatureEnabled(): Boolean {
return remoteConfig.getBoolean("watchface_feature_enabled")
}

override fun isXrEnabled(): Boolean {
return remoteConfig.getBoolean("xr_feature_enabled")
}
}
4 changes: 4 additions & 0 deletions core/network/src/main/res/xml/remote_config_defaults.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
<key>background_vibes_feature_enabled</key>
<value>false</value>
</entry>
<entry>
<key>xr_feature_enabled</key>
<value>false</value>
</entry>
<entry>
<key>bot_background_instruction_prompt</key>
<value>Add the input image android bot as the main subject to the result,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,8 @@ class TestRemoteConfigDataSource(private val useGeminiNano: Boolean) : RemoteCon
override fun watchfaceFeatureEnabled(): Boolean {
return true
}

override fun isXrEnabled(): Boolean {
return false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.tooling.preview.Devices.PIXEL_TABLET
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import com.android.developers.androidify.theme.AndroidifyTheme
Expand Down Expand Up @@ -78,6 +80,31 @@ fun SquiggleBackground(
}
}

/**
* Background squiggle that tries to fit in its parent.
*/
@Composable
fun SquiggleBackgroundFull() {
val vectorBackground =
rememberVectorPainter(ImageVector.vectorResource(R.drawable.squiggle_full))
Box(modifier = Modifier.fillMaxSize()) {
Image(
painter = vectorBackground,
contentDescription = null,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Fit,
)
}
}

@Preview(device = PIXEL_TABLET)
@Composable
fun SquiggleFullImagePreview() {
AndroidifyTheme {
SquiggleBackgroundFull()
}
}

@LargeScreensPreview
@Composable
private fun SquiggleBackgroundLargePreview() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,17 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
Expand All @@ -57,10 +54,9 @@ fun AndroidifyTopAppBar(
titleText: String = stringResource(R.string.androidify_title),
isMediumWindowSize: Boolean = false,
backEnabled: Boolean = false,
aboutEnabled: Boolean = true,
expandedCenterButtons: @Composable () -> Unit = {},
onBackPressed: () -> Unit = {},
onAboutClicked: () -> Unit = {},
actions: (@Composable () -> Unit)? = null,
) {
if (isMediumWindowSize) {
Box(
Expand Down Expand Up @@ -95,16 +91,16 @@ fun AndroidifyTopAppBar(
expandedCenterButtons()
}

if (aboutEnabled) {
AboutButton(
modifier = Modifier
.align(Alignment.CenterEnd)
.background(
color = MaterialTheme.colorScheme.surfaceContainerLowest,
shape = CircleShape,
),
onAboutClicked = onAboutClicked,
)
Row(
modifier = Modifier
.align(Alignment.CenterEnd)
.background(
color = MaterialTheme.colorScheme.surfaceContainerLowest,
shape = CircleShape,
),

) {
actions?.invoke()
}
}
} else {
Expand All @@ -124,9 +120,7 @@ fun AndroidifyTopAppBar(
}
},
actions = {
if (aboutEnabled) {
AboutButton(onAboutClicked = onAboutClicked)
}
actions?.invoke()
},
colors = TopAppBarDefaults.topAppBarColors(containerColor = MaterialTheme.colorScheme.surfaceContainerLowest),
)
Expand All @@ -143,46 +137,14 @@ private fun BackButton(onBackPressed: () -> Unit) {
}
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AndroidifyTranslucentTopAppBar(
modifier: Modifier = Modifier,
titleText: String = stringResource(R.string.androidify_title),
isMediumSizeLayout: Boolean = false,
) {
if (isMediumSizeLayout) {
TopAppBar(
title = {
Spacer(Modifier.statusBarsPadding())
AndroidifyTitle(titleText)
},
modifier = modifier.clip(
MaterialTheme.shapes.large.copy(topStart = CornerSize(0f), topEnd = CornerSize(0f)),
),
colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
)
} else {
CenterAlignedTopAppBar(
title = {
Spacer(Modifier.statusBarsPadding())
AndroidifyTitle(titleText)
},
modifier = modifier.clip(
MaterialTheme.shapes.large.copy(topStart = CornerSize(0f), topEnd = CornerSize(0f)),
),
colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
)
}
}

@Composable
private fun AndroidifyTitle(text: String) {
Text(text, fontWeight = FontWeight.Bold)
}

@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
private fun AboutButton(modifier: Modifier = Modifier, onAboutClicked: () -> Unit = {}) {
fun AboutButton(modifier: Modifier = Modifier, onAboutClicked: () -> Unit = {}) {
val sharedTransitionScope = LocalSharedTransitionScope.current
with(sharedTransitionScope) {
IconButton(
Expand Down
35 changes: 35 additions & 0 deletions core/theme/src/main/res/drawable/squiggle_full.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2025 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.
-->
<!-- The squiggle but cropped to the viewport. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="1425dp"
android:height="822dp"
android:viewportWidth="1290"
android:viewportHeight="668">
<group
android:translateX="-66.87"
android:translateY="-76.47">
<path
android:fillColor="#34A853"
android:pathData="M1308.7,428.8L1040.3,129.2A184.5,161.6 89.4,0 0,811.8 131.8L811.8,131.8A184.5,161.6 89.4,0 0,814.7 392.7L1083.1,692.4A184.5,161.6 89.4,0 0,1311.6 689.8L1311.6,689.8A184.5,161.6 89.4,0 0,1308.7 428.8z" />
<path
android:fillColor="#34A853"
android:pathData="M959.2,428.8L690.8,129.2A184.5,161.6 89.4,0 0,462.3 131.8L462.3,131.8A184.5,161.6 89.4,0 0,465.2 392.7L733.6,692.4A184.5,161.6 89.4,0 0,962.2 689.8L962.2,689.8A184.5,161.6 89.4,0 0,959.2 428.8z" />
<path
android:fillColor="#34A853"
android:pathData="M609.7,429L341.3,129.4A184.5,161.6 89.4,0 0,112.8 131.9L112.8,131.9A184.5,161.6 89.4,0 0,115.7 392.9L384.1,692.5A184.5,161.6 89.4,0 0,612.7 690L612.7,690A184.5,161.6 89.4,0 0,609.7 429z" />
</group>
</vector>
1 change: 1 addition & 0 deletions core/xr/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
51 changes: 51 additions & 0 deletions core/xr/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2025 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.
*/
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
}
android {
namespace = "com.android.developers.androidify.xr"
compileSdk = libs.versions.compileSdk.get().toInt()
defaultConfig {
minSdk = libs.versions.minSdk.get().toInt()
}

buildFeatures {
compose = true
buildConfig = true
}

compileOptions {
sourceCompatibility = JavaVersion.toVersion(libs.versions.javaVersion.get())
targetCompatibility = JavaVersion.toVersion(libs.versions.javaVersion.get())
}
kotlinOptions {
jvmTarget = libs.versions.jvmTarget.get()
}

}

dependencies {
implementation(libs.androidx.xr.compose)

implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation(projects.core.util)
implementation(projects.core.theme)
}
21 changes: 21 additions & 0 deletions core/xr/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
19 changes: 19 additions & 0 deletions core/xr/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2025 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2025 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.android.developers.androidify.xr

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.xr.compose.platform.LocalSession
import androidx.xr.scenecore.scene
import kotlinx.coroutines.delay

/*
* A composable that attempts to continually hide the mainPanel.
*
* This is a temporary workaround for b/440325404, that causes the mainPanelEntity when an
* ApplicationSubspace transitions out of the composition due to a race condition when transitioning
* the two hierarchies.
*/
@Composable
fun MainPanelWorkaround() {
val session = LocalSession.current
LaunchedEffect(null) {
while (true) {
delay(100L)
session?.scene?.mainPanelEntity?.setEnabled(false)
}
}
}
Loading