diff --git a/app.json b/app.json index fdae57a..f7aa7ce 100644 --- a/app.json +++ b/app.json @@ -12,7 +12,10 @@ "supportsTablet": true, "bundleIdentifier": "com.anonymous.digitalpilates", "infoPlist": { - "ITSAppUsesNonExemptEncryption": false + "ITSAppUsesNonExemptEncryption": false, + "NSCameraUsageDescription": "应用需要使用相机以拍摄您的体态照片用于AI测评。", + "NSPhotoLibraryUsageDescription": "应用需要访问相册以选择您的体态照片用于AI测评。", + "NSPhotoLibraryAddUsageDescription": "应用需要写入相册以保存拍摄的体态照片(可选)。" } }, "android": { diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index 14744cf..31aab2f 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -4,9 +4,13 @@ import React from 'react'; import { Text, TouchableOpacity, View } from 'react-native'; import { IconSymbol } from '@/components/ui/IconSymbol'; +import { Colors } from '@/constants/Colors'; import { TAB_BAR_BOTTOM_OFFSET, TAB_BAR_HEIGHT } from '@/constants/TabBar'; +import { useColorScheme } from '@/hooks/useColorScheme'; export default function TabLayout() { + const theme = (useColorScheme() ?? 'light') as 'light' | 'dark'; + const colorTokens = Colors[theme]; const pathname = usePathname(); return ( @@ -19,9 +23,9 @@ export default function TabLayout() { return { headerShown: false, - tabBarActiveTintColor: '#192126', + tabBarActiveTintColor: colorTokens.tabIconSelected, tabBarButton: (props) => { - const { children, onPress } = props; + const { onPress } = props; const handlePress = (event: any) => { if (process.env.EXPO_OS === 'ios') { @@ -30,9 +34,28 @@ export default function TabLayout() { onPress && onPress(event); }; + // 基于 routeName 设置图标与标题,避免 tabBarIcon 的包装导致文字裁剪 + const getIconAndTitle = () => { + switch (routeName) { + case 'index': + return { icon: 'house.fill', title: '首页' } as const; + case 'explore': + return { icon: 'paperplane.fill', title: '探索' } as const; + case 'personal': + return { icon: 'person.fill', title: '个人' } as const; + default: + return { icon: 'circle', title: '' } as const; + } + }; + + const { icon, title } = getIconAndTitle(); + const activeContentColor = colorTokens.onPrimary; + const inactiveContentColor = colorTokens.tabIconDefault; + return ( - {children} + + + {isSelected && !!title && ( + + {title} + + )} + ); }, @@ -55,7 +98,7 @@ export default function TabLayout() { bottom: TAB_BAR_BOTTOM_OFFSET, height: TAB_BAR_HEIGHT, borderRadius: 34, - backgroundColor: '#192126', + backgroundColor: colorTokens.tabBarBackground, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.2, diff --git a/app/ai-posture-assessment.tsx b/app/ai-posture-assessment.tsx index be557e0..d888d98 100644 --- a/app/ai-posture-assessment.tsx +++ b/app/ai-posture-assessment.tsx @@ -1,9 +1,13 @@ import { Ionicons } from '@expo/vector-icons'; +import { BlurView } from 'expo-blur'; +import * as ImagePicker from 'expo-image-picker'; import { useRouter } from 'expo-router'; -import React from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { + Alert, Image, - ImageBackground, + Linking, + Platform, ScrollView, StyleSheet, Text, @@ -14,49 +18,157 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { Colors } from '@/constants/Colors'; -type Exercise = { - id: string; - title: string; - duration: string; - imageUri: string; +type PoseView = 'front' | 'side' | 'back'; + +type UploadState = { + front?: string | null; + side?: string | null; + back?: string | null; }; -const EXERCISES: Exercise[] = [ - { - id: '1', - title: 'Jumping Jacks', - duration: '00:30', - imageUri: - 'https://images.unsplash.com/photo-1546483875-ad9014c88eba?q=80&w=400&auto=format&fit=crop', - }, - { - id: '2', - title: 'Squats', - duration: '00:45', - imageUri: - 'https://images.unsplash.com/photo-1583454110551-21f2fa2f36f0?q=80&w=400&auto=format&fit=crop', - }, - { - id: '3', - title: 'Backward Lunge', - duration: '00:40', - imageUri: - 'https://images.unsplash.com/photo-1597074866923-5c3bfa3b6c46?q=80&w=400&auto=format&fit=crop', - }, - { - id: '4', - title: 'High Knees', - duration: '00:30', - imageUri: - 'https://images.unsplash.com/photo-1596357395104-5bcae0b1a5eb?q=80&w=400&auto=format&fit=crop', - }, -]; +type Sample = { uri: string; correct: boolean }; + +const SAMPLES: Record = { + front: [ + { uri: 'https://images.unsplash.com/photo-1594737625785-c6683fc87c73?w=400&q=80&auto=format', correct: true }, + { uri: 'https://images.unsplash.com/photo-1544716278-ca5e3f4abd8c?w=400&q=80&auto=format', correct: false }, + { uri: 'https://images.unsplash.com/photo-1571019614242-c5c5dee9f50b?w=400&q=80&auto=format', correct: false }, + ], + side: [ + { uri: 'https://images.unsplash.com/photo-1554463529-e27854014799?w=400&q=80&auto=format', correct: true }, + { uri: 'https://images.unsplash.com/photo-1596357395104-5bcae0b1a5eb?w=400&q=80&auto=format', correct: false }, + { uri: 'https://images.unsplash.com/photo-1526506118085-60ce8714f8c5?w=400&q=80&auto=format', correct: false }, + ], + back: [ + { uri: 'https://images.unsplash.com/photo-1517836357463-d25dfeac3438?w=400&q=80&auto=format', correct: true }, + { uri: 'https://images.unsplash.com/photo-1571721797421-f4c9f2b13107?w=400&q=80&auto=format', correct: false }, + { uri: 'https://images.unsplash.com/photo-1518611012118-696072aa579a?w=400&q=80&auto=format', correct: false }, + ], +}; export default function AIPostureAssessmentScreen() { const router = useRouter(); const insets = useSafeAreaInsets(); + const theme = Colors.dark; - const theme = Colors.dark; // 该页面采用深色视觉 + const [uploadState, setUploadState] = useState({}); + const canStart = useMemo( + () => Boolean(uploadState.front && uploadState.side && uploadState.back), + [uploadState] + ); + + const [cameraPerm, setCameraPerm] = useState(null); + const [libraryPerm, setLibraryPerm] = useState(null); + const [libraryAccess, setLibraryAccess] = useState<'all' | 'limited' | 'none' | null>(null); + const [cameraCanAsk, setCameraCanAsk] = useState(null); + const [libraryCanAsk, setLibraryCanAsk] = useState(null); + + useEffect(() => { + (async () => { + const cam = await ImagePicker.getCameraPermissionsAsync(); + const lib = await ImagePicker.getMediaLibraryPermissionsAsync(); + setCameraPerm(cam.status); + setLibraryPerm(lib.status); + setLibraryAccess( + (lib as any).accessPrivileges ?? (lib.status === 'granted' ? 'all' : 'none') + ); + setCameraCanAsk(cam.canAskAgain); + setLibraryCanAsk(lib.canAskAgain); + })(); + }, []); + + async function requestAllPermissions() { + try { + const cam = await ImagePicker.requestCameraPermissionsAsync(); + const lib = await ImagePicker.requestMediaLibraryPermissionsAsync(); + setCameraPerm(cam.status); + setLibraryPerm(lib.status); + setLibraryAccess( + (lib as any).accessPrivileges ?? (lib.status === 'granted' ? 'all' : 'none') + ); + setCameraCanAsk(cam.canAskAgain); + setLibraryCanAsk(lib.canAskAgain); + const libGranted = lib.status === 'granted' || (lib as any).accessPrivileges === 'limited'; + if (cam.status !== 'granted' || !libGranted) { + Alert.alert( + '权限未完全授予', + '请在系统设置中授予相机与相册权限以完成上传', + [ + { text: '取消', style: 'cancel' }, + { text: '去设置', onPress: () => Linking.openSettings() }, + ] + ); + } + } catch { } + } + + async function requestPermissionAndPick(source: 'camera' | 'library', key: PoseView) { + try { + if (source === 'camera') { + const resp = await ImagePicker.requestCameraPermissionsAsync(); + setCameraPerm(resp.status); + setCameraCanAsk(resp.canAskAgain); + if (resp.status !== 'granted') { + Alert.alert( + '权限不足', + '需要相机权限以拍摄照片', + resp.canAskAgain + ? [{ text: '好的' }] + : [ + { text: '取消', style: 'cancel' }, + { text: '去设置', onPress: () => Linking.openSettings() }, + ] + ); + return; + } + const result = await ImagePicker.launchCameraAsync({ + allowsEditing: true, + quality: 0.8, + aspect: [3, 4], + }); + if (!result.canceled) { + setUploadState((s) => ({ ...s, [key]: result.assets[0]?.uri ?? null })); + } + } else { + const resp = await ImagePicker.requestMediaLibraryPermissionsAsync(); + setLibraryPerm(resp.status); + setLibraryAccess( + (resp as any).accessPrivileges ?? (resp.status === 'granted' ? 'all' : 'none') + ); + setLibraryCanAsk(resp.canAskAgain); + const libGranted = resp.status === 'granted' || (resp as any).accessPrivileges === 'limited'; + if (!libGranted) { + Alert.alert( + '权限不足', + '需要相册权限以选择照片', + resp.canAskAgain + ? [{ text: '好的' }] + : [ + { text: '取消', style: 'cancel' }, + { text: '去设置', onPress: () => Linking.openSettings() }, + ] + ); + return; + } + const result = await ImagePicker.launchImageLibraryAsync({ + allowsEditing: true, + quality: 0.8, + aspect: [3, 4], + }); + if (!result.canceled) { + setUploadState((s) => ({ ...s, [key]: result.assets[0]?.uri ?? null })); + } + } + } catch (e) { + Alert.alert('发生错误', '选择图片失败,请重试'); + } + } + + function handleStart() { + if (!canStart) return; + // TODO: 调用后端或进入分析页面 + Alert.alert('开始测评', '已收集三视角照片,准备开始AI体态分析'); + } return ( @@ -69,7 +181,7 @@ export default function AIPostureAssessmentScreen() { > - AI体态评估 + AI体态测评 @@ -77,104 +189,142 @@ export default function AIPostureAssessmentScreen() { contentContainerStyle={{ paddingBottom: insets.bottom + 120 }} showsVerticalScrollIndicator={false} > - {/* Hero */} - - - - + {/* Permissions Banner (iOS 优先提示) */} + {Platform.OS === 'ios' && ( + (cameraPerm !== 'granted' || !(libraryPerm === 'granted' || libraryAccess === 'limited')) && ( + + 需要相机与相册权限 + + 授权后可拍摄或选择三视角全身照片用于AI体态测评。 + + + {((cameraCanAsk ?? true) || (libraryCanAsk ?? true)) ? ( + + 一键授权 + + ) : ( + Linking.openSettings()}> + 去设置开启 + + )} + requestPermissionAndPick('library', 'front')}> + 稍后再说 + + + + ) + )} - {/* Floating stats */} - - } - label="Time" - value="20 min" - /> - - } - label="Burn" - value="95 kcal" - /> - - - - {/* Title & description */} - - Lower Body Training + {/* Intro */} + + 上传标准姿势照片 - The lower abdomen and hips are the most difficult areas of the body to reduce when we are on - a diet. Even so, in this area, especially the legs as a whole, you can reduce weight even if - you don't use tools. + 请依次上传正面、侧面与背面全身照。保持光线均匀、背景简洁,身体立正自然放松。 - {/* Rounds header */} - - Rounds - 1/8 - + {/* Upload sections */} + requestPermissionAndPick('camera', 'front')} + onPickLibrary={() => requestPermissionAndPick('library', 'front')} + samples={SAMPLES.front} + /> - {/* Exercise list */} - - {EXERCISES.map((item) => ( - ) - )} - + requestPermissionAndPick('camera', 'side')} + onPickLibrary={() => requestPermissionAndPick('library', 'side')} + samples={SAMPLES.side} + /> + + requestPermissionAndPick('camera', 'back')} + onPickLibrary={() => requestPermissionAndPick('library', 'back')} + samples={SAMPLES.back} + /> {/* Bottom CTA */} { }} - style={[styles.bottomCta, { backgroundColor: theme.primary }]} + disabled={!canStart} + activeOpacity={1} + onPress={handleStart} + style={[ + styles.bottomCta, + { backgroundColor: canStart ? theme.primary : theme.neutral300 }, + ]} > - Lets Workout + + {canStart ? '开始测评' : '请先完成三视角上传'} + ); } -function StatCard({ - icon, +function UploadTile({ label, value, + onPickCamera, + onPickLibrary, + samples, }: { - icon: React.ReactNode; label: string; - value: string; + value?: string | null; + onPickCamera: () => void; + onPickLibrary: () => void; + samples: Sample[]; }) { return ( - - {icon} - - {label} - {value} + + + {label} + {value ? ( + 可长按替换 + ) : ( + 需上传此视角 + )} - - ); -} -function ExerciseItem({ exercise }: { exercise: Exercise }) { - return ( - - - - {exercise.title} - {exercise.duration} - - - + + {value ? ( + + ) : ( + + + + + 拍摄或选择照片 + 点击拍摄,长按从相册选择 + + )} + + + 示例 + + {samples.map((s, idx) => ( + + + + {s.correct ? '正确示范' : '错误示范'} + + + ))} + + ); } @@ -183,6 +333,57 @@ const styles = StyleSheet.create({ screen: { flex: 1, }, + permBanner: { + marginTop: 12, + marginHorizontal: 16, + padding: 14, + borderRadius: 16, + backgroundColor: 'rgba(255,255,255,0.04)' + }, + permTitle: { + color: '#ECEDEE', + fontSize: 16, + fontWeight: '700', + }, + permDesc: { + color: 'rgba(255,255,255,0.75)', + marginTop: 6, + fontSize: 13, + }, + permActions: { + flexDirection: 'row', + gap: 10, + marginTop: 10, + }, + permPrimary: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + paddingHorizontal: 14, + height: 40, + borderRadius: 12, + backgroundColor: '#BBF246', + }, + permPrimaryText: { + color: '#192126', + fontSize: 14, + fontWeight: '800', + }, + permSecondary: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + paddingHorizontal: 14, + height: 40, + borderRadius: 12, + borderWidth: 1, + borderColor: 'rgba(255,255,255,0.18)', + }, + permSecondaryText: { + color: 'rgba(255,255,255,0.85)', + fontSize: 14, + fontWeight: '700', + }, header: { flexDirection: 'row', alignItems: 'center', @@ -202,73 +403,13 @@ const styles = StyleSheet.create({ color: '#ECEDEE', fontWeight: '700', }, - heroContainer: { - marginTop: 14, - marginHorizontal: 16, - }, - heroImage: { - height: 260, - borderRadius: 28, - overflow: 'hidden', - justifyContent: 'flex-end', - }, - heroOverlay: { - ...StyleSheet.absoluteFillObject, - backgroundColor: 'rgba(0,0,0,0.18)', - borderRadius: 28, - }, - statsFloating: { - position: 'absolute', - left: 22, - right: 22, - bottom: -26, - height: 72, - borderRadius: 20, - backgroundColor: '#1E262C', - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-around', - paddingHorizontal: 14, - shadowColor: '#000', - shadowOpacity: 0.3, - shadowRadius: 10, - shadowOffset: { width: 0, height: 6 }, - elevation: 6, - }, - divider: { - width: 1, - height: 36, - backgroundColor: 'rgba(255,255,255,0.08)', - }, - statCard: { - flexDirection: 'row', - alignItems: 'center', - }, - statIconWrap: { - width: 36, - height: 36, - borderRadius: 12, - backgroundColor: '#BBF246', - alignItems: 'center', - justifyContent: 'center', - }, - statLabel: { - color: 'rgba(255,255,255,0.75)', - fontSize: 12, - marginBottom: 2, - }, - statValue: { - color: '#ECEDEE', - fontSize: 16, - fontWeight: '700', - }, - contentSection: { - marginTop: 46, + introBox: { + marginTop: 12, paddingHorizontal: 20, - gap: 12, + gap: 10, }, title: { - fontSize: 28, + fontSize: 26, color: '#ECEDEE', fontWeight: '800', }, @@ -277,53 +418,98 @@ const styles = StyleSheet.create({ lineHeight: 22, color: 'rgba(255,255,255,0.75)', }, - roundsHeader: { - marginTop: 18, - paddingHorizontal: 20, + section: { + marginTop: 16, + paddingHorizontal: 16, + gap: 12, + }, + sectionHeader: { + paddingHorizontal: 4, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', }, - roundsTitle: { + sectionTitle: { color: '#ECEDEE', - fontSize: 22, + fontSize: 18, fontWeight: '700', }, - roundsCount: { - color: 'rgba(255,255,255,0.6)', - fontSize: 16, - }, - exerciseItem: { - flexDirection: 'row', - alignItems: 'center', - backgroundColor: '#1E262C', - padding: 12, - borderRadius: 18, - }, - exerciseThumb: { - width: 56, - height: 56, - borderRadius: 12, - }, - exerciseTitle: { - color: '#ECEDEE', - fontSize: 16, - fontWeight: '600', - }, - exerciseDuration: { - color: 'rgba(255,255,255,0.6)', - marginTop: 4, + retakeHint: { + color: 'rgba(255,255,255,0.55)', fontSize: 13, }, - exercisePlayButton: { + uploader: { + height: 220, + borderRadius: 18, + borderWidth: 1, + borderStyle: 'dashed', + borderColor: 'rgba(255,255,255,0.18)', + backgroundColor: '#1E262C', + overflow: 'hidden', + }, + preview: { + width: '100%', + height: '100%', + }, + placeholder: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + gap: 8, + }, + plusBadge: { width: 36, height: 36, borderRadius: 18, - borderWidth: 1, - borderColor: 'rgba(255,255,255,0.2)', alignItems: 'center', justifyContent: 'center', - backgroundColor: '#192126', + backgroundColor: '#BBF246', + }, + placeholderTitle: { + color: '#ECEDEE', + fontSize: 16, + fontWeight: '700', + }, + placeholderDesc: { + color: 'rgba(255,255,255,0.65)', + fontSize: 12, + }, + sampleBox: { + marginTop: 8, + borderRadius: 16, + padding: 12, + backgroundColor: 'rgba(255,255,255,0.04)', + }, + sampleTitle: { + color: 'rgba(255,255,255,0.8)', + fontSize: 14, + marginBottom: 8, + fontWeight: '600', + }, + sampleRow: { + flexDirection: 'row', + gap: 10, + }, + sampleItem: { + flex: 1, + }, + sampleImg: { + width: '100%', + height: 90, + borderRadius: 12, + backgroundColor: '#111', + }, + sampleTag: { + alignSelf: 'flex-start', + paddingHorizontal: 8, + paddingVertical: 4, + borderRadius: 8, + marginTop: 6, + }, + sampleTagText: { + color: '#192126', + fontSize: 12, + fontWeight: '700', }, bottomCtaWrap: { position: 'absolute', @@ -338,10 +524,8 @@ const styles = StyleSheet.create({ justifyContent: 'center', }, bottomCtaText: { - color: '#192126', fontSize: 18, fontWeight: '800', }, }); - diff --git a/constants/Colors.ts b/constants/Colors.ts index 2fe3936..5dd93b8 100644 --- a/constants/Colors.ts +++ b/constants/Colors.ts @@ -1,33 +1,103 @@ /** - * Below are the colors that are used in the app. The colors are defined in the light and dark mode. - * There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc. + * 应用全局配色规范(来自设计规范图)。 + * 说明:保持原有导出结构不变,同时扩展更完整的语义令牌与原子调色板。 */ -const primaryColor = '#BBF246'; // 应用主题色 +// 原子调色板(与设计图一致) +export const palette = { + // Primary + primary: '#BBF246', + ink: '#192126', + + // Secondary / Neutrals + neutral100: '#888F92', + neutral200: '#5E6468', + neutral300: '#384046', + + // Accents + purple: '#A48AED', + red: '#ED4747', + orange: '#FCC46F', + blue: '#95CCE3', +} as const; + +const primaryColor = palette.primary; // 应用主题色 const tintColorLight = primaryColor; -const tintColorDark = '#fff'; +const tintColorDark = '#FFFFFF'; export const Colors = { light: { + // 基础文本/背景 text: '#11181C', - background: '#fff', + textSecondary: palette.neutral300, + textMuted: palette.neutral200, + background: '#FFFFFF', + surface: '#FFFFFF', + card: '#FFFFFF', + + // 品牌与可交互主色 tint: tintColorLight, primary: primaryColor, + onPrimary: palette.ink, // 与主色搭配的前景色(按钮文字/图标) + + // 中性色与辅助 + neutral100: palette.neutral100, + neutral200: palette.neutral200, + neutral300: palette.neutral300, + + // 状态/反馈色 + success: palette.primary, + warning: palette.orange, + danger: palette.red, + info: palette.blue, + accentPurple: palette.purple, + + // 结构色 + border: palette.neutral100 + '33', // 20% 透明度 + separator: palette.neutral100 + '33', icon: '#687076', + + // Tab 相关(保持兼容) tabIconDefault: '#687076', - tabIconSelected: '#192126', // tab 激活时的文字/图标颜色(深色,在亮色背景上显示) - tabBarBackground: '#192126', // tab 栏背景色 + tabIconSelected: palette.ink, // tab 激活时的文字/图标颜色(深色,在亮色背景上显示) + tabBarBackground: palette.ink, // tab 栏背景色 tabBarActiveBackground: primaryColor, // tab 激活时的背景色 }, dark: { + // 基础文本/背景 text: '#ECEDEE', + textSecondary: palette.neutral100, + textMuted: '#9BA1A6', background: '#151718', + surface: '#1A1D1E', + card: '#1A1D1E', + + // 品牌与可交互主色 tint: tintColorDark, primary: primaryColor, + onPrimary: palette.ink, + + // 中性色与辅助 + neutral100: palette.neutral100, + neutral200: palette.neutral200, + neutral300: palette.neutral300, + + // 状态/反馈色 + success: palette.primary, + warning: palette.orange, + danger: palette.red, + info: palette.blue, + accentPurple: palette.purple, + + // 结构色 + border: '#2A2F32', + separator: '#2A2F32', icon: '#9BA1A6', + + // Tab 相关(保持兼容) tabIconDefault: '#9BA1A6', - tabIconSelected: '#192126', // 在亮色背景上使用深色文字 - tabBarBackground: '#192126', + tabIconSelected: palette.ink, // 在亮色背景上使用深色文字 + tabBarBackground: palette.ink, tabBarActiveBackground: primaryColor, }, -}; +} as const; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 92e8bb2..e1f741b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -3,6 +3,9 @@ PODS: - DoubleConversion (1.1.6) - EXConstants (17.1.7): - ExpoModulesCore + - EXImageLoader (5.1.0): + - ExpoModulesCore + - React-Core - Expo (53.0.20): - DoubleConversion - ExpoModulesCore @@ -49,6 +52,8 @@ PODS: - SDWebImageAVIFCoder (~> 0.11.0) - SDWebImageSVGCoder (~> 1.7.0) - SDWebImageWebPCoder (~> 0.14.6) + - ExpoImagePicker (16.1.4): + - ExpoModulesCore - ExpoKeepAwake (14.1.4): - ExpoModulesCore - ExpoLinearGradient (14.1.5): @@ -2100,6 +2105,7 @@ DEPENDENCIES: - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - EXConstants (from `../node_modules/expo-constants/ios`) + - EXImageLoader (from `../node_modules/expo-image-loader/ios`) - Expo (from `../node_modules/expo`) - ExpoAsset (from `../node_modules/expo-asset/ios`) - ExpoBlur (from `../node_modules/expo-blur/ios`) @@ -2108,6 +2114,7 @@ DEPENDENCIES: - ExpoHaptics (from `../node_modules/expo-haptics/ios`) - ExpoHead (from `../node_modules/expo-router/ios`) - ExpoImage (from `../node_modules/expo-image/ios`) + - ExpoImagePicker (from `../node_modules/expo-image-picker/ios`) - ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`) - ExpoLinearGradient (from `../node_modules/expo-linear-gradient/ios`) - ExpoLinking (from `../node_modules/expo-linking/ios`) @@ -2212,6 +2219,8 @@ EXTERNAL SOURCES: :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" EXConstants: :path: "../node_modules/expo-constants/ios" + EXImageLoader: + :path: "../node_modules/expo-image-loader/ios" Expo: :path: "../node_modules/expo" ExpoAsset: @@ -2228,6 +2237,8 @@ EXTERNAL SOURCES: :path: "../node_modules/expo-router/ios" ExpoImage: :path: "../node_modules/expo-image/ios" + ExpoImagePicker: + :path: "../node_modules/expo-image-picker/ios" ExpoKeepAwake: :path: "../node_modules/expo-keep-awake/ios" ExpoLinearGradient: @@ -2400,6 +2411,7 @@ SPEC CHECKSUMS: boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90 DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb EXConstants: 98bcf0f22b820f9b28f9fee55ff2daededadd2f8 + EXImageLoader: 4d3d3284141f1a45006cc4d0844061c182daf7ee Expo: a40d525c930dd1c8a158e082756ee071955baccb ExpoAsset: ef06e880126c375f580d4923fdd1cdf4ee6ee7d6 ExpoBlur: 3c8885b9bf9eef4309041ec87adec48b5f1986a9 @@ -2408,6 +2420,7 @@ SPEC CHECKSUMS: ExpoHaptics: 0ff6e0d83cd891178a306e548da1450249d54500 ExpoHead: a7b66cbaeeb51f4a85338d335a0f5467e29a2c90 ExpoImage: e4102c93d1dbe99ff54b075452d1bc9d6ec21b7c + ExpoImagePicker: 0963da31800c906e01c03e25d7c849f16ebf02a2 ExpoKeepAwake: bf0811570c8da182bfb879169437d4de298376e7 ExpoLinearGradient: 7734c8059972fcf691fb4330bcdf3390960a152d ExpoLinking: d5c183998ca6ada66ff45e407e0f965b398a8902 diff --git a/ios/digitalpilates.xcodeproj/project.pbxproj b/ios/digitalpilates.xcodeproj/project.pbxproj index f5b494d..7e059f0 100644 --- a/ios/digitalpilates.xcodeproj/project.pbxproj +++ b/ios/digitalpilates.xcodeproj/project.pbxproj @@ -8,28 +8,28 @@ /* Begin PBXBuildFile section */ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 27B482E4BA415859EA8B0372 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8732A3660B00244505674555 /* ExpoModulesProvider.swift */; }; + 2C9C524987451393B76B9C7E /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 7EC44F9488C227087AA8DF97 /* PrivacyInfo.xcprivacy */; }; 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; }; - 7F5A026795C1BF28BEBBFA4E /* libPods-digitalpilates.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FDAB65677BEFC9C3E52449E8 /* libPods-digitalpilates.a */; }; + 6B6021A2D1EB466803BE19D7 /* libPods-digitalpilates.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F1A078ADDB1BCB06E0DBEFDA /* libPods-digitalpilates.a */; }; BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; }; - CBE4D0B57478826DADA47BC9 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = C59D7448330DC33ACFEECB09 /* PrivacyInfo.xcprivacy */; }; + DC3BFC72D3A68C7493D5B44A /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D1B5F0EC906D7A2F599549 /* ExpoModulesProvider.swift */; }; F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11748412D0307B40044C1D9 /* AppDelegate.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 031BAB3982A98E78D1493449 /* Pods-digitalpilates.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-digitalpilates.debug.xcconfig"; path = "Target Support Files/Pods-digitalpilates/Pods-digitalpilates.debug.xcconfig"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* digitalpilates.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = digitalpilates.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = digitalpilates/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = digitalpilates/Info.plist; sourceTree = ""; }; - 7398BB3C47424238F7BEF8F9 /* Pods-digitalpilates.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-digitalpilates.release.xcconfig"; path = "Target Support Files/Pods-digitalpilates/Pods-digitalpilates.release.xcconfig"; sourceTree = ""; }; - 8732A3660B00244505674555 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-digitalpilates/ExpoModulesProvider.swift"; sourceTree = ""; }; + 4D6B8E20DD8E5677F8B2EAA1 /* Pods-digitalpilates.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-digitalpilates.debug.xcconfig"; path = "Target Support Files/Pods-digitalpilates/Pods-digitalpilates.debug.xcconfig"; sourceTree = ""; }; + 7EC44F9488C227087AA8DF97 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = digitalpilates/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 83D1B5F0EC906D7A2F599549 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-digitalpilates/ExpoModulesProvider.swift"; sourceTree = ""; }; AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = digitalpilates/SplashScreen.storyboard; sourceTree = ""; }; BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = ""; }; - C59D7448330DC33ACFEECB09 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = digitalpilates/PrivacyInfo.xcprivacy; sourceTree = ""; }; + EA6A757B2DE1747F7B3664B4 /* Pods-digitalpilates.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-digitalpilates.release.xcconfig"; path = "Target Support Files/Pods-digitalpilates/Pods-digitalpilates.release.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; F11748412D0307B40044C1D9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = digitalpilates/AppDelegate.swift; sourceTree = ""; }; F11748442D0722820044C1D9 /* digitalpilates-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "digitalpilates-Bridging-Header.h"; path = "digitalpilates/digitalpilates-Bridging-Header.h"; sourceTree = ""; }; - FDAB65677BEFC9C3E52449E8 /* libPods-digitalpilates.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-digitalpilates.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + F1A078ADDB1BCB06E0DBEFDA /* libPods-digitalpilates.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-digitalpilates.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -37,7 +37,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 7F5A026795C1BF28BEBBFA4E /* libPods-digitalpilates.a in Frameworks */, + 6B6021A2D1EB466803BE19D7 /* libPods-digitalpilates.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -53,7 +53,7 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */, 13B07FB61A68108700A75B9A /* Info.plist */, AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */, - C59D7448330DC33ACFEECB09 /* PrivacyInfo.xcprivacy */, + 7EC44F9488C227087AA8DF97 /* PrivacyInfo.xcprivacy */, ); name = digitalpilates; sourceTree = ""; @@ -62,17 +62,19 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - FDAB65677BEFC9C3E52449E8 /* libPods-digitalpilates.a */, + F1A078ADDB1BCB06E0DBEFDA /* libPods-digitalpilates.a */, ); name = Frameworks; sourceTree = ""; }; - 7B79F82457416491183F46D6 /* digitalpilates */ = { + 3EE8D66219D64F4A63E8298D /* Pods */ = { isa = PBXGroup; children = ( - 8732A3660B00244505674555 /* ExpoModulesProvider.swift */, + 4D6B8E20DD8E5677F8B2EAA1 /* Pods-digitalpilates.debug.xcconfig */, + EA6A757B2DE1747F7B3664B4 /* Pods-digitalpilates.release.xcconfig */, ); - name = digitalpilates; + name = Pods; + path = Pods; sourceTree = ""; }; 832341AE1AAA6A7D00B99B32 /* Libraries */ = { @@ -89,8 +91,8 @@ 832341AE1AAA6A7D00B99B32 /* Libraries */, 83CBBA001A601CBA00E9B192 /* Products */, 2D16E6871FA4F8E400B85C8A /* Frameworks */, - 8D14961AE80832AA51F9FFA3 /* Pods */, - F7E6B913F90880BFADBE192E /* ExpoModulesProviders */, + 3EE8D66219D64F4A63E8298D /* Pods */, + F899CC3CCA86CFEC0C4F53F7 /* ExpoModulesProviders */, ); indentWidth = 2; sourceTree = ""; @@ -105,15 +107,6 @@ name = Products; sourceTree = ""; }; - 8D14961AE80832AA51F9FFA3 /* Pods */ = { - isa = PBXGroup; - children = ( - 031BAB3982A98E78D1493449 /* Pods-digitalpilates.debug.xcconfig */, - 7398BB3C47424238F7BEF8F9 /* Pods-digitalpilates.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; BB2F792B24A3F905000567C9 /* Supporting */ = { isa = PBXGroup; children = ( @@ -123,10 +116,18 @@ path = digitalpilates/Supporting; sourceTree = ""; }; - F7E6B913F90880BFADBE192E /* ExpoModulesProviders */ = { + DFAD2B7142CEC38E9ED66053 /* digitalpilates */ = { isa = PBXGroup; children = ( - 7B79F82457416491183F46D6 /* digitalpilates */, + 83D1B5F0EC906D7A2F599549 /* ExpoModulesProvider.swift */, + ); + name = digitalpilates; + sourceTree = ""; + }; + F899CC3CCA86CFEC0C4F53F7 /* ExpoModulesProviders */ = { + isa = PBXGroup; + children = ( + DFAD2B7142CEC38E9ED66053 /* digitalpilates */, ); name = ExpoModulesProviders; sourceTree = ""; @@ -139,13 +140,13 @@ buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "digitalpilates" */; buildPhases = ( 08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */, - E959B4C3D4071EECDD504F2E /* [Expo] Configure project */, + 60F566376E07CDAA8138E40B /* [Expo] Configure project */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */, - 679509E8E2F87FD68CDC2915 /* [CP] Embed Pods Frameworks */, + 761236F3114550442BC2DA44 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -195,7 +196,7 @@ BB2F792D24A3F905000567C9 /* Expo.plist in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */, - CBE4D0B57478826DADA47BC9 /* PrivacyInfo.xcprivacy in Resources */, + 2C9C524987451393B76B9C7E /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -239,7 +240,26 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 679509E8E2F87FD68CDC2915 /* [CP] Embed Pods Frameworks */ = { + 60F566376E07CDAA8138E40B /* [Expo] Configure project */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "[Expo] Configure project"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-digitalpilates/expo-configure-project.sh\"\n"; + }; + 761236F3114550442BC2DA44 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -295,25 +315,6 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-digitalpilates/Pods-digitalpilates-resources.sh\"\n"; showEnvVarsInLog = 0; }; - E959B4C3D4071EECDD504F2E /* [Expo] Configure project */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "[Expo] Configure project"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-digitalpilates/expo-configure-project.sh\"\n"; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -322,7 +323,7 @@ buildActionMask = 2147483647; files = ( F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */, - 27B482E4BA415859EA8B0372 /* ExpoModulesProvider.swift in Sources */, + DC3BFC72D3A68C7493D5B44A /* ExpoModulesProvider.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -331,13 +332,12 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 031BAB3982A98E78D1493449 /* Pods-digitalpilates.debug.xcconfig */; + baseConfigurationReference = 4D6B8E20DD8E5677F8B2EAA1 /* Pods-digitalpilates.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = digitalpilates/digitalpilates.entitlements; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 756WVXJ6MT; ENABLE_BITCODE = NO; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", @@ -368,13 +368,12 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7398BB3C47424238F7BEF8F9 /* Pods-digitalpilates.release.xcconfig */; + baseConfigurationReference = EA6A757B2DE1747F7B3664B4 /* Pods-digitalpilates.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = digitalpilates/digitalpilates.entitlements; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 756WVXJ6MT; INFOPLIST_FILE = digitalpilates/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/ios/digitalpilates/Info.plist b/ios/digitalpilates/Info.plist index 8b18d51..56ab7c2 100644 --- a/ios/digitalpilates/Info.plist +++ b/ios/digitalpilates/Info.plist @@ -1,86 +1,91 @@ - - CADisableMinimumFrameDurationOnPhone - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - digital-pilates - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0.0 - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleURLSchemes - - digitalpilates - digital-pilates - - - - CFBundleVersion - 1 - LSMinimumSystemVersion - 12.0 - LSRequiresIPhoneOS - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - NSAllowsLocalNetworking - - - NSHealthShareUsageDescription - 应用需要访问您的健康数据(步数与能量消耗)以展示运动统计。 - NSHealthUpdateUsageDescription - Allow $(PRODUCT_NAME) to update health info - NSUserActivityTypes - - $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route - - UILaunchStoryboardName - SplashScreen - UIRequiredDeviceCapabilities - - arm64 - - UIRequiresFullScreen - - UIStatusBarStyle - UIStatusBarStyleDefault - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIUserInterfaceStyle - Automatic - UIViewControllerBasedStatusBarAppearance - - - ITSAppUsesNonExemptEncryption - - - + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + digital-pilates + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleURLSchemes + + digitalpilates + com.anonymous.digitalpilates + + + + CFBundleVersion + 1 + ITSAppUsesNonExemptEncryption + + LSMinimumSystemVersion + 12.0 + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSAllowsLocalNetworking + + + NSCameraUsageDescription + 应用需要使用相机以拍摄您的体态照片用于AI测评。 + NSHealthShareUsageDescription + 应用需要访问您的健康数据(步数与能量消耗)以展示运动统计。 + NSHealthUpdateUsageDescription + Allow $(PRODUCT_NAME) to update health info + NSPhotoLibraryAddUsageDescription + 应用需要写入相册以保存拍摄的体态照片(可选)。 + NSPhotoLibraryUsageDescription + 应用需要访问相册以选择您的体态照片用于AI测评。 + NSUserActivityTypes + + $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route + + UILaunchStoryboardName + SplashScreen + UIRequiredDeviceCapabilities + + arm64 + + UIRequiresFullScreen + + UIStatusBarStyle + UIStatusBarStyleDefault + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIUserInterfaceStyle + Automatic + UIViewControllerBasedStatusBarAppearance + + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 656fd1d..a365080 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "expo-font": "~13.3.2", "expo-haptics": "~14.1.4", "expo-image": "~2.4.0", + "expo-image-picker": "~16.1.4", "expo-linear-gradient": "^14.1.5", "expo-linking": "~7.1.7", "expo-router": "~5.1.4", @@ -6301,6 +6302,27 @@ } } }, + "node_modules/expo-image-loader": { + "version": "5.1.0", + "resolved": "https://mirrors.tencent.com/npm/expo-image-loader/-/expo-image-loader-5.1.0.tgz", + "integrity": "sha512-sEBx3zDQIODWbB5JwzE7ZL5FJD+DK3LVLWBVJy6VzsqIA6nDEnSFnsnWyCfCTSvbGigMATs1lgkC2nz3Jpve1Q==", + "license": "MIT", + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-image-picker": { + "version": "16.1.4", + "resolved": "https://mirrors.tencent.com/npm/expo-image-picker/-/expo-image-picker-16.1.4.tgz", + "integrity": "sha512-bTmmxtw1AohUT+HxEBn2vYwdeOrj1CLpMXKjvi9FKSoSbpcarT4xxI0z7YyGwDGHbrJqyyic3I9TTdP2J2b4YA==", + "license": "MIT", + "dependencies": { + "expo-image-loader": "~5.1.0" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-keep-awake": { "version": "14.1.4", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-14.1.4.tgz", diff --git a/package.json b/package.json index a7bbc1f..708a278 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,8 @@ "react-native-safe-area-context": "5.4.0", "react-native-screens": "~4.11.1", "react-native-web": "~0.20.0", - "react-native-webview": "13.13.5" + "react-native-webview": "13.13.5", + "expo-image-picker": "~16.1.4" }, "devDependencies": { "@babel/core": "^7.25.2",