1import { ExpoConfig } from '@expo/config-types'; 2import assert from 'assert'; 3 4import { assignColorValue } from './Colors'; 5import { ResourceXML } from './Resources'; 6import { assignStylesValue, getAppThemeLightNoActionBarGroup } from './Styles'; 7import { ConfigPlugin } from '../Plugin.types'; 8import { withAndroidColors, withAndroidStyles } from '../plugins/android-plugins'; 9 10// https://developer.android.com/reference/android/R.attr#colorPrimaryDark 11const COLOR_PRIMARY_DARK_KEY = 'colorPrimaryDark'; 12// https://developer.android.com/reference/android/R.attr#windowTranslucentStatus 13const WINDOW_TRANSLUCENT_STATUS = 'android:windowTranslucentStatus'; 14// https://developer.android.com/reference/android/R.attr#windowLightStatusBar 15const WINDOW_LIGHT_STATUS_BAR = 'android:windowLightStatusBar'; 16 17export const withStatusBar: ConfigPlugin = (config) => { 18 config = withStatusBarColors(config); 19 config = withStatusBarStyles(config); 20 return config; 21}; 22 23const withStatusBarColors: ConfigPlugin = (config) => { 24 return withAndroidColors(config, (config) => { 25 config.modResults = setStatusBarColors(config, config.modResults); 26 return config; 27 }); 28}; 29 30const withStatusBarStyles: ConfigPlugin = (config) => { 31 return withAndroidStyles(config, (config) => { 32 config.modResults = setStatusBarStyles(config, config.modResults); 33 return config; 34 }); 35}; 36 37export function setStatusBarColors( 38 config: Pick<ExpoConfig, 'androidStatusBar'>, 39 colors: ResourceXML 40): ResourceXML { 41 return assignColorValue(colors, { 42 name: COLOR_PRIMARY_DARK_KEY, 43 value: getStatusBarColor(config), 44 }); 45} 46 47export function setStatusBarStyles( 48 config: Pick<ExpoConfig, 'androidStatusBar'>, 49 styles: ResourceXML 50): ResourceXML { 51 const hexString = getStatusBarColor(config); 52 const floatElement = getStatusBarTranslucent(config); 53 54 styles = assignStylesValue(styles, { 55 parent: getAppThemeLightNoActionBarGroup(), 56 name: WINDOW_LIGHT_STATUS_BAR, 57 targetApi: '23', 58 value: 'true', 59 // Default is light-content, don't need to do anything to set it 60 add: getStatusBarStyle(config) === 'dark-content', 61 }); 62 63 styles = assignStylesValue(styles, { 64 parent: getAppThemeLightNoActionBarGroup(), 65 name: WINDOW_TRANSLUCENT_STATUS, 66 value: 'true', 67 // translucent status bar set in theme 68 add: floatElement, 69 }); 70 71 styles = assignStylesValue(styles, { 72 parent: getAppThemeLightNoActionBarGroup(), 73 name: COLOR_PRIMARY_DARK_KEY, 74 value: `@color/${COLOR_PRIMARY_DARK_KEY}`, 75 // Remove the color if translucent is used 76 add: !!hexString, 77 }); 78 79 return styles; 80} 81 82export function getStatusBarColor(config: Pick<ExpoConfig, 'androidStatusBar'>) { 83 const backgroundColor = config.androidStatusBar?.backgroundColor; 84 if (backgroundColor) { 85 // Drop support for translucent 86 assert( 87 backgroundColor !== 'translucent', 88 `androidStatusBar.backgroundColor must be a valid hex string, instead got: "${backgroundColor}"` 89 ); 90 } 91 return backgroundColor; 92} 93 94/** 95 * Specifies whether the status bar should be "translucent". When true, the status bar is drawn with `position: absolute` and a gray underlay, when false `position: relative` (pushes content down). 96 * 97 * @default false 98 * @param config 99 * @returns 100 */ 101export function getStatusBarTranslucent(config: Pick<ExpoConfig, 'androidStatusBar'>): boolean { 102 return config.androidStatusBar?.translucent ?? false; 103} 104 105export function getStatusBarStyle(config: Pick<ExpoConfig, 'androidStatusBar'>) { 106 return config.androidStatusBar?.barStyle || 'light-content'; 107} 108