@@ -105,6 +105,7 @@ import {
105105const ACCESSIBILITY_REFRESH_MS = 1500 ;
106106const REACT_NATIVE_ACCESSIBILITY_REFRESH_MS = 500 ;
107107const FLUTTER_ACCESSIBILITY_REFRESH_MS = 1000 ;
108+ const ANDROID_METADATA_REFRESH_MS = 1000 ;
108109const DEFAULT_ACCESSIBILITY_MAX_DEPTH = 10 ;
109110const LOGICAL_INSPECTOR_MAX_DEPTH = 80 ;
110111const FLUTTER_INSPECTOR_MAX_DEPTH = 48 ;
@@ -457,8 +458,12 @@ export function AppShell({
457458 udid : string ;
458459 } | null > ( null ) ;
459460 const touchMoveFrameRef = useRef ( 0 ) ;
461+ const refreshRef = useRef ( refresh ) ;
462+ const previousAndroidDisplayKeyRef = useRef ( "" ) ;
463+ const previousAndroidViewportSizeKeyRef = useRef ( "" ) ;
460464 const canvasSize = useElementSize ( outerCanvasElement ) ;
461465 const zoomDockSize = useElementSize ( zoomDockElement ) ;
466+ refreshRef . current = refresh ;
462467
463468 const handleOuterCanvasRef = useCallback ( ( node : HTMLDivElement | null ) => {
464469 outerCanvasRef . current = node ;
@@ -689,10 +694,14 @@ export function AppShell({
689694 selectedSimulator != null && shouldRenderNativeChrome ( selectedSimulator ) ;
690695 const viewportChromeProfile = shouldRenderChrome ? chromeProfile : null ;
691696 const isAndroidViewport = isAndroidSimulator ( selectedSimulator ) ;
697+ const androidDisplayKey =
698+ isAndroidViewport && selectedSimulator
699+ ? androidDisplayKeyForSimulator ( selectedSimulator )
700+ : "" ;
692701 const effectiveDeviceNaturalSize = useMemo ( ( ) => {
693702 const displaySize = simulatorDisplaySize ( selectedSimulator ) ;
694- if ( isAndroidViewport && displaySize ) {
695- return displaySize ;
703+ if ( isAndroidViewport ) {
704+ return deviceNaturalSize ?? displaySize ;
696705 }
697706 return (
698707 deviceNaturalSize ??
@@ -710,6 +719,10 @@ export function AppShell({
710719 selectedSimulator ,
711720 shouldRenderChrome ,
712721 ] ) ;
722+ const androidViewportSizeKey =
723+ isAndroidViewport && effectiveDeviceNaturalSize
724+ ? `${ Math . round ( effectiveDeviceNaturalSize . width ) } x${ Math . round ( effectiveDeviceNaturalSize . height ) } `
725+ : "" ;
713726
714727 const zoomDockReservedHeight =
715728 zoomDockElement && typeof window !== "undefined"
@@ -1065,6 +1078,55 @@ export function AppShell({
10651078 } ) ;
10661079 } , [ isAndroidViewport , simulatorRotationQuarterTurns ] ) ;
10671080
1081+ useEffect ( ( ) => {
1082+ if ( ! isAndroidViewport || ! selectedSimulator ?. isBooted ) {
1083+ return ;
1084+ }
1085+
1086+ let cancelled = false ;
1087+ const refreshAndroidMetadata = ( ) => {
1088+ if ( cancelled || document . visibilityState !== "visible" ) {
1089+ return ;
1090+ }
1091+ void refreshRef . current ( ) ;
1092+ } ;
1093+
1094+ const intervalId = window . setInterval (
1095+ refreshAndroidMetadata ,
1096+ ANDROID_METADATA_REFRESH_MS ,
1097+ ) ;
1098+ return ( ) => {
1099+ cancelled = true ;
1100+ window . clearInterval ( intervalId ) ;
1101+ } ;
1102+ } , [ isAndroidViewport , selectedSimulator ?. isBooted , selectedSimulator ?. udid ] ) ;
1103+
1104+ useEffect ( ( ) => {
1105+ if ( ! isAndroidViewport || ! androidDisplayKey ) {
1106+ previousAndroidDisplayKeyRef . current = "" ;
1107+ return ;
1108+ }
1109+
1110+ const previousKey = previousAndroidDisplayKeyRef . current ;
1111+ previousAndroidDisplayKeyRef . current = androidDisplayKey ;
1112+ if ( previousKey && previousKey !== androidDisplayKey ) {
1113+ beginZoomAnimation ( ) ;
1114+ }
1115+ } , [ androidDisplayKey , isAndroidViewport ] ) ;
1116+
1117+ useEffect ( ( ) => {
1118+ if ( ! isAndroidViewport || ! androidViewportSizeKey ) {
1119+ previousAndroidViewportSizeKeyRef . current = "" ;
1120+ return ;
1121+ }
1122+
1123+ const previousKey = previousAndroidViewportSizeKeyRef . current ;
1124+ previousAndroidViewportSizeKeyRef . current = androidViewportSizeKey ;
1125+ if ( previousKey && previousKey !== androidViewportSizeKey ) {
1126+ beginZoomAnimation ( ) ;
1127+ }
1128+ } , [ androidViewportSizeKey , isAndroidViewport ] ) ;
1129+
10681130 useEffect ( ( ) => {
10691131 setChromeLoaded ( ! chromeRequired ) ;
10701132 } , [ chromeRequired , chromeUrl ] ) ;
@@ -1096,7 +1158,12 @@ export function AppShell({
10961158 return ( ) => {
10971159 cancelled = true ;
10981160 } ;
1099- } , [ selectedSimulator ?. udid ] ) ;
1161+ } , [
1162+ selectedSimulator ?. privateDisplay ?. displayHeight ,
1163+ selectedSimulator ?. privateDisplay ?. displayWidth ,
1164+ selectedSimulator ?. privateDisplay ?. rotationQuarterTurns ,
1165+ selectedSimulator ?. udid ,
1166+ ] ) ;
11001167
11011168 useEffect ( ( ) => {
11021169 if ( ! menuOpen ) {
@@ -1329,7 +1396,7 @@ export function AppShell({
13291396 const screenOnlyStyle =
13301397 ! viewportChromeProfile && chromeProfile && chromeProfile . screenWidth > 0
13311398 ? isAndroidViewport
1332- ? androidScreenRadiusStyle ( chromeProfile )
1399+ ? androidScreenRadiusStyle ( chromeProfile , effectiveDeviceNaturalSize )
13331400 : ( {
13341401 borderRadius : `${ Math . min (
13351402 chromeProfile . cornerRadius *
@@ -1906,6 +1973,7 @@ export function AppShell({
19061973 void runAction ( async ( ) => {
19071974 await rotateRight ( selectedSimulator . udid ) ;
19081975 setRotationQuarterTurns ( 0 ) ;
1976+ beginZoomAnimation ( ) ;
19091977 await refresh ( ) ;
19101978 } , false ) ;
19111979 return ;
@@ -2101,12 +2169,17 @@ export function AppShell({
21012169
21022170function androidScreenRadiusStyle (
21032171 chromeProfile : ChromeProfile ,
2172+ displaySize : Size | null ,
21042173) : CSSProperties | null {
2105- if ( chromeProfile . screenWidth <= 0 ) {
2174+ const screenWidth =
2175+ displaySize && displaySize . width > 0
2176+ ? displaySize . width
2177+ : chromeProfile . screenWidth ;
2178+ if ( screenWidth <= 0 ) {
21062179 return null ;
21072180 }
21082181
2109- const scale = DEVICE_SCREEN_WIDTH / chromeProfile . screenWidth ;
2182+ const scale = DEVICE_SCREEN_WIDTH / screenWidth ;
21102183 const maxRadius = DEVICE_SCREEN_WIDTH / 2 ;
21112184 const radii = chromeProfile . cornerRadii ;
21122185 const topLeft = scaledScreenRadius (
@@ -2152,6 +2225,19 @@ function scaledScreenRadius(radius: number, scale: number, maxRadius: number) {
21522225 return Math . min ( radius * scale , maxRadius ) ;
21532226}
21542227
2228+ function androidDisplayKeyForSimulator ( simulator : SimulatorMetadata ) : string {
2229+ const display = simulator . privateDisplay ;
2230+ if ( ! display ) {
2231+ return simulator . udid ;
2232+ }
2233+ return [
2234+ simulator . udid ,
2235+ Math . round ( display . displayWidth ) ,
2236+ Math . round ( display . displayHeight ) ,
2237+ display . rotationQuarterTurns ?? 0 ,
2238+ ] . join ( "|" ) ;
2239+ }
2240+
21552241function readDeviceQueryParam ( ) : string | undefined {
21562242 if ( typeof window === "undefined" ) {
21572243 return undefined ;
0 commit comments