@shaykec/app-agent 1.0.8 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/android-customizer.md +9 -1
- package/.claude/agents/catalog-analyzer.md +57 -0
- package/.claude/agents/ios-customizer.md +9 -1
- package/.claude/agents/react-native-customizer.md +71 -0
- package/.claude/skills/android-customizer/SKILL.md +108 -23
- package/.claude/skills/bug-fixer/SKILL.md +59 -0
- package/.claude/skills/catalog-analyzer/SKILL.md +96 -0
- package/.claude/skills/customization-planner/SKILL.md +44 -5
- package/.claude/skills/design-selector/SKILL.md +3 -1
- package/.claude/skills/design-system/SKILL.md +1 -1
- package/.claude/skills/exploratory-tester/SKILL.md +82 -0
- package/.claude/skills/ios-customizer/SKILL.md +123 -23
- package/.claude/skills/module-integrator/SKILL.md +1 -1
- package/.claude/skills/react-native-customizer/SKILL.md +97 -11
- package/.claude/skills/test-planner/SKILL.md +72 -0
- package/.cursor/agents/README.md +3 -1
- package/.cursor/agents/android-customizer.md +15 -11
- package/.cursor/agents/catalog-analyzer.md +83 -0
- package/.cursor/agents/ios-customizer.md +15 -10
- package/.cursor/agents/react-native-customizer.md +170 -0
- package/.cursor/mcp.json +2 -10
- package/.cursor/rules/safety-guardrails.mdc +1 -1
- package/.cursor/rules/workflow.mdc +52 -18
- package/.cursor/skills/android-customizer/SKILL.md +46 -22
- package/.cursor/skills/bug-fixer/SKILL.md +189 -0
- package/.cursor/skills/catalog-analyzer/SKILL.md +222 -0
- package/.cursor/skills/customization-planner/SKILL.md +55 -8
- package/.cursor/skills/design-selector/SKILL.md +6 -5
- package/.cursor/skills/design-system/SKILL.md +8 -7
- package/.cursor/skills/exploratory-tester/SKILL.md +223 -0
- package/.cursor/skills/ios-customizer/SKILL.md +50 -15
- package/.cursor/skills/module-integrator/SKILL.md +2 -2
- package/.cursor/skills/output-validator/SKILL.md +1 -1
- package/.cursor/skills/react-native-customizer/SKILL.md +115 -25
- package/.cursor/skills/test-planner/SKILL.md +199 -0
- package/AGENTS.md +32 -11
- package/CLAUDE.md +78 -33
- package/README.md +77 -11
- package/designs/DESIGN_CATALOG.md +17 -15
- package/designs/DESIGN_PRINCIPLES.md +53 -0
- package/designs/brands/accessible-high-contrast.md +14 -0
- package/designs/brands/corporate-professional.md +14 -0
- package/designs/brands/dark-luxe.md +14 -0
- package/designs/brands/kids-playful.md +14 -0
- package/designs/brands/medical-clinical.md +14 -0
- package/designs/brands/modern-minimal.md +14 -0
- package/designs/brands/nature-organic.md +14 -0
- package/designs/brands/neo-brutalist.md +14 -0
- package/designs/brands/retro-vintage.md +14 -0
- package/designs/brands/soft-gradient.md +14 -0
- package/designs/brands/sport-athletic.md +14 -0
- package/designs/brands/tech-dynamic.md +14 -0
- package/designs/brands/vibrant-playful.md +14 -0
- package/dist/cli.d.ts +4 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +91 -1
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/engines/claude-engine.d.ts.map +1 -1
- package/dist/engines/claude-engine.js +16 -4
- package/dist/engines/claude-engine.js.map +1 -1
- package/dist/engines/types.d.ts +1 -1
- package/dist/engines/types.d.ts.map +1 -1
- package/dist/engines/types.js +31 -2
- package/dist/engines/types.js.map +1 -1
- package/dist/github.d.ts +3 -0
- package/dist/github.d.ts.map +1 -1
- package/dist/github.js +47 -4
- package/dist/github.js.map +1 -1
- package/dist/index.js +217 -9
- package/dist/index.js.map +1 -1
- package/dist/prompt-builder.d.ts +11 -1
- package/dist/prompt-builder.d.ts.map +1 -1
- package/dist/prompt-builder.js +216 -1
- package/dist/prompt-builder.js.map +1 -1
- package/dist/validator.d.ts +7 -2
- package/dist/validator.d.ts.map +1 -1
- package/dist/validator.js +61 -41
- package/dist/validator.js.map +1 -1
- package/dist/workspace.js +2 -2
- package/dist/workspace.js.map +1 -1
- package/package.json +2 -2
- package/prompts/agent-prompt.md +35 -18
- package/prompts/deep-test-agent-prompt.md +122 -0
- package/prompts/fix-agent-prompt.md +90 -0
- package/prompts/quick-agent-prompt.md +32 -2
- package/prompts/scratch-agent-prompt.md +5 -8
- package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/core/animation/AnimatedTransitionsModifiers.kt +188 -0
- package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/core/animation/AnimatedTransitionsModifiers.kt +188 -0
- package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/conversations/ConversationsScreen.kt +1 -1
- package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/core/animation/AnimatedTransitionsModifiers.kt +188 -0
- package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/features/navigation/MainScreen.kt +1 -0
- package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/core/animation/AnimatedTransitionsModifiers.kt +188 -0
- package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/navigation/MainNavigation.kt +5 -1
- package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/core/animation/AnimatedTransitionsModifiers.kt +188 -0
- package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/core/animation/AnimatedTransitionsModifiers.kt +188 -0
- package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/core/animation/MotionPreferencesScreen.kt +3 -3
- package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/features/navigation/Navigation.kt +1 -1
- package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/features/settings/SettingsScreen.kt +1 -1
- package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/core/animation/AnimatedTransitionsModifiers.kt +188 -0
- package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/core/animation/AnimatedTransitionsModifiers.kt +188 -0
- package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/core/animation/AnimatedTransitionsModifiers.kt +188 -0
- package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/core/animation/AnimatedTransitionsModifiers.kt +188 -0
- package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/features/settings/SettingsScreen.kt +3 -2
- package/templates/android/ReferenceTemplate/app/src/main/kotlin/com/appship/reference/core/animation/AnimatedTransitionsModifiers.kt +188 -0
- package/templates/android/ReferenceTemplate/app/src/main/kotlin/com/appship/reference/features/settings/SettingsScreen.kt +1 -1
- package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/core/animation/AnimatedTransitionsModifiers.kt +188 -0
- package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/features/cart/CartScreen.kt +3 -2
- package/templates/android/Skeleton/TESTING_MANIFEST.md +2 -1
- package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/MainActivity.kt +23 -2
- package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/core/animation/AnimatedTransitionsModifiers.kt +188 -0
- package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/core/theme/AppearanceManager.kt +42 -0
- package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/features/profile/ProfileScreen.kt +20 -8
- package/templates/android/Skeleton/tests/03_detail_screen.yaml +3 -2
- package/templates/android/Skeleton/tests/04_favorites.yaml +3 -2
- package/templates/android/Skeleton/tests/08_full_e2e.yaml +9 -2
- package/templates/android/Skeleton/tests/09_dark_mode.yaml +50 -0
- package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/core/animation/AnimatedTransitionsModifiers.kt +188 -0
- package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/core/animation/AnimatedTransitionsModifiers.kt +188 -0
- package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/settings/SettingsScreen.kt +3 -2
- package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/core/animation/AnimatedTransitionsModifiers.kt +188 -0
- package/templates/ios/BookTemplate/BookTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
- package/templates/ios/ChatTemplate/ChatTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
- package/templates/ios/DashTemplate/DashTemplate/App/AppConfig.swift +1 -0
- package/templates/ios/DashTemplate/DashTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
- package/templates/ios/DashTemplate/DashTemplate/Core/Strings.swift +13 -0
- package/templates/ios/DashTemplate/DashTemplate.xcodeproj/project.pbxproj +32 -20
- package/templates/ios/FamilyTemplate/FamilyTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
- package/templates/ios/FinanceTemplate/FinanceTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
- package/templates/ios/FinanceTemplate/FinanceTemplate/Core/Strings.swift +42 -0
- package/templates/ios/FinanceTemplate/FinanceTemplate.xcodeproj/project.pbxproj +36 -30
- package/templates/ios/GameTemplate/GameTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
- package/templates/ios/HealthTemplate/HealthTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
- package/templates/ios/LearnTemplate/LearnTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
- package/templates/ios/MapTemplate/MapTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
- package/templates/ios/MediaTemplate/MediaTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
- package/templates/ios/ReferenceTemplate/ReferenceTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
- package/templates/ios/ReferenceTemplate/ReferenceTemplate/Core/Strings.swift +12 -0
- package/templates/ios/ReferenceTemplate/ReferenceTemplate/Features/SkeletonLoading/SkeletonLoadingView.swift +2 -37
- package/templates/ios/ShopTemplate/ShopTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
- package/templates/ios/Skeleton/Skeleton/Core/Animation/AnimatedTransitionsView.swift +201 -0
- package/templates/ios/Skeleton/tests/08_full_e2e.yaml +4 -0
- package/templates/ios/Skeleton/tests/09_dark_mode.yaml +52 -0
- package/templates/ios/SocialTemplate/SocialTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
- package/templates/ios/TaskTemplate/TaskTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
- package/templates/ios/TrackTemplate/TrackTemplate/Core/Animation/AnimatedTransitionsView.swift +201 -0
- package/templates/react-native/BookTemplate/src/animation/useAnimatedList.ts +219 -2
- package/templates/react-native/BookTemplate/src/animation/useMotionPreferences.ts +23 -9
- package/templates/react-native/BookTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
- package/templates/react-native/ChatTemplate/src/animation/useAnimatedList.ts +219 -2
- package/templates/react-native/ChatTemplate/src/animation/useMotionPreferences.ts +23 -9
- package/templates/react-native/ChatTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
- package/templates/react-native/DashTemplate/src/animation/useAnimatedList.ts +219 -2
- package/templates/react-native/DashTemplate/src/animation/useMotionPreferences.ts +23 -9
- package/templates/react-native/DashTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
- package/templates/react-native/FamilyTemplate/src/animation/useAnimatedList.ts +219 -2
- package/templates/react-native/FamilyTemplate/src/animation/useMotionPreferences.ts +23 -9
- package/templates/react-native/FamilyTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
- package/templates/react-native/FinanceTemplate/src/animation/useAnimatedList.ts +219 -2
- package/templates/react-native/FinanceTemplate/src/animation/useMotionPreferences.ts +23 -9
- package/templates/react-native/FinanceTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
- package/templates/react-native/GameTemplate/src/animation/useAnimatedList.ts +219 -2
- package/templates/react-native/GameTemplate/src/animation/useMotionPreferences.ts +23 -9
- package/templates/react-native/GameTemplate/src/screens/GameDetail/GameDetailScreen.tsx +2 -1
- package/templates/react-native/GameTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
- package/templates/react-native/HealthTemplate/src/animation/useAnimatedList.ts +219 -2
- package/templates/react-native/HealthTemplate/src/animation/useMotionPreferences.ts +23 -9
- package/templates/react-native/HealthTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
- package/templates/react-native/HealthTemplate/src/screens/WorkoutDetail/WorkoutDetailScreen.tsx +1 -1
- package/templates/react-native/LearnTemplate/src/animation/useAnimatedList.ts +219 -2
- package/templates/react-native/LearnTemplate/src/animation/useMotionPreferences.ts +23 -9
- package/templates/react-native/LearnTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
- package/templates/react-native/MapTemplate/src/animation/useAnimatedList.ts +219 -2
- package/templates/react-native/MapTemplate/src/animation/useMotionPreferences.ts +23 -9
- package/templates/react-native/MapTemplate/src/screens/Map/MapScreen.tsx +14 -0
- package/templates/react-native/MapTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
- package/templates/react-native/MediaTemplate/src/animation/useAnimatedList.ts +219 -2
- package/templates/react-native/MediaTemplate/src/animation/useMotionPreferences.ts +23 -9
- package/templates/react-native/MediaTemplate/src/screens/PlaylistDetail/PlaylistDetailScreen.tsx +1 -1
- package/templates/react-native/MediaTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
- package/templates/react-native/ReferenceTemplate/src/animation/useAnimatedList.ts +219 -2
- package/templates/react-native/ReferenceTemplate/src/animation/useMotionPreferences.ts +23 -9
- package/templates/react-native/ReferenceTemplate/src/screens/Settings/SettingsScreen.tsx +1 -1
- package/templates/react-native/ShopTemplate/src/animation/useAnimatedList.ts +219 -2
- package/templates/react-native/ShopTemplate/src/animation/useMotionPreferences.ts +23 -9
- package/templates/react-native/ShopTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
- package/templates/react-native/Skeleton/TESTING_MANIFEST.md +2 -1
- package/templates/react-native/Skeleton/src/animation/useAnimatedList.ts +219 -2
- package/templates/react-native/Skeleton/src/animation/useMotionPreferences.ts +23 -9
- package/templates/react-native/Skeleton/src/screens/Profile/ProfileScreen.tsx +1 -1
- package/templates/react-native/Skeleton/tests/07_profile.yaml +3 -2
- package/templates/react-native/Skeleton/tests/08_full_e2e.yaml +12 -1
- package/templates/react-native/Skeleton/tests/09_dark_mode.yaml +46 -0
- package/templates/react-native/SocialTemplate/src/animation/useAnimatedList.ts +219 -2
- package/templates/react-native/SocialTemplate/src/animation/useMotionPreferences.ts +23 -9
- package/templates/react-native/SocialTemplate/src/screens/Feed/FeedScreen.tsx +1 -0
- package/templates/react-native/SocialTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
- package/templates/react-native/TaskTemplate/src/animation/useAnimatedList.ts +219 -2
- package/templates/react-native/TaskTemplate/src/animation/useMotionPreferences.ts +23 -9
- package/templates/react-native/TaskTemplate/src/screens/Profile/ProfileScreen.tsx +1 -1
- package/templates/react-native/TrackTemplate/src/animation/useAnimatedList.ts +219 -2
- package/templates/react-native/TrackTemplate/src/animation/useMotionPreferences.ts +23 -9
- package/templates/react-native/TrackTemplate/src/screens/Settings/SettingsScreen.tsx +1 -1
- package/templates/shared/ios/AnimatedTransitions/AnimatedTransitionsView.swift +233 -93
- package/.claude/agents/template-selector.md +0 -39
- package/.claude/skills/module-selector/SKILL.md +0 -81
- package/.claude/skills/template-selector/SKILL.md +0 -44
- package/.cursor/agents/template-selector.md +0 -52
- package/.cursor/skills/module-selector/SKILL.md +0 -135
- package/.cursor/skills/template-selector/SKILL.md +0 -123
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
package com.appship.skeleton.core.theme
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.SharedPreferences
|
|
5
|
+
import androidx.compose.foundation.isSystemInDarkTheme
|
|
6
|
+
import androidx.compose.runtime.*
|
|
7
|
+
import androidx.compose.runtime.staticCompositionLocalOf
|
|
8
|
+
|
|
9
|
+
// CUSTOMIZE:APPEARANCE - Dark mode and appearance management
|
|
10
|
+
enum class AppearanceMode { SYSTEM, LIGHT, DARK }
|
|
11
|
+
|
|
12
|
+
class AppearanceManager(context: Context) {
|
|
13
|
+
private val prefs: SharedPreferences =
|
|
14
|
+
context.getSharedPreferences("appearance_prefs", Context.MODE_PRIVATE)
|
|
15
|
+
|
|
16
|
+
var appearanceMode: AppearanceMode by mutableStateOf(
|
|
17
|
+
try {
|
|
18
|
+
AppearanceMode.valueOf(prefs.getString("appearance_mode", "SYSTEM") ?: "SYSTEM")
|
|
19
|
+
} catch (_: Exception) {
|
|
20
|
+
AppearanceMode.SYSTEM
|
|
21
|
+
}
|
|
22
|
+
)
|
|
23
|
+
private set
|
|
24
|
+
|
|
25
|
+
fun setMode(mode: AppearanceMode) {
|
|
26
|
+
appearanceMode = mode
|
|
27
|
+
prefs.edit().putString("appearance_mode", mode.name).apply()
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
val LocalAppearanceManager = staticCompositionLocalOf<AppearanceManager> {
|
|
32
|
+
error("No AppearanceManager provided")
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@Composable
|
|
36
|
+
fun resolveIsDarkTheme(appearanceManager: AppearanceManager): Boolean {
|
|
37
|
+
return when (appearanceManager.appearanceMode) {
|
|
38
|
+
AppearanceMode.SYSTEM -> isSystemInDarkTheme()
|
|
39
|
+
AppearanceMode.LIGHT -> false
|
|
40
|
+
AppearanceMode.DARK -> true
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -17,6 +17,8 @@ import androidx.compose.ui.res.stringResource
|
|
|
17
17
|
import androidx.compose.ui.unit.dp
|
|
18
18
|
import com.appship.skeleton.AppConfig
|
|
19
19
|
import com.appship.skeleton.R
|
|
20
|
+
import com.appship.skeleton.core.theme.AppearanceMode
|
|
21
|
+
import com.appship.skeleton.core.theme.LocalAppearanceManager
|
|
20
22
|
|
|
21
23
|
// PLACEHOLDER: Customize this screen for user profile and settings.
|
|
22
24
|
|
|
@@ -94,14 +96,24 @@ fun ProfileScreen(
|
|
|
94
96
|
.testTag("profile_notifications")
|
|
95
97
|
)
|
|
96
98
|
HorizontalDivider()
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
99
|
+
run {
|
|
100
|
+
val appearanceManager = LocalAppearanceManager.current
|
|
101
|
+
val isDark = appearanceManager.appearanceMode == AppearanceMode.DARK
|
|
102
|
+
ListItem(
|
|
103
|
+
headlineContent = { Text("Dark Mode") },
|
|
104
|
+
leadingContent = { Icon(Icons.Default.DarkMode, contentDescription = null) },
|
|
105
|
+
trailingContent = {
|
|
106
|
+
Switch(
|
|
107
|
+
checked = isDark,
|
|
108
|
+
onCheckedChange = { checked ->
|
|
109
|
+
appearanceManager.setMode(if (checked) AppearanceMode.DARK else AppearanceMode.SYSTEM)
|
|
110
|
+
},
|
|
111
|
+
modifier = Modifier.testTag("profile_dark_mode_toggle")
|
|
112
|
+
)
|
|
113
|
+
},
|
|
114
|
+
modifier = Modifier.testTag("profile_appearance")
|
|
115
|
+
)
|
|
116
|
+
}
|
|
105
117
|
if (AppConfig.Features.ENABLE_ANIMATIONS) {
|
|
106
118
|
HorizontalDivider()
|
|
107
119
|
ListItem(
|
|
@@ -7,8 +7,9 @@ appId: ${APP_PACKAGE_NAME}
|
|
|
7
7
|
timeout: 3000
|
|
8
8
|
- assertVisible: "Home"
|
|
9
9
|
- extendedWaitUntil:
|
|
10
|
-
visible:
|
|
11
|
-
|
|
10
|
+
visible:
|
|
11
|
+
id: "home_item_0_card"
|
|
12
|
+
timeout: 5000
|
|
12
13
|
# Favorite an item first
|
|
13
14
|
- tapOn:
|
|
14
15
|
id: "home_item_0_card"
|
|
@@ -8,8 +8,9 @@ appId: ${APP_PACKAGE_NAME}
|
|
|
8
8
|
# Home tab
|
|
9
9
|
- assertVisible: "Home"
|
|
10
10
|
- extendedWaitUntil:
|
|
11
|
-
visible:
|
|
12
|
-
|
|
11
|
+
visible:
|
|
12
|
+
id: "home_item_0_card"
|
|
13
|
+
timeout: 5000
|
|
13
14
|
# Detail from home
|
|
14
15
|
- tapOn:
|
|
15
16
|
id: "home_item_0_card"
|
|
@@ -44,6 +45,12 @@ appId: ${APP_PACKAGE_NAME}
|
|
|
44
45
|
- assertVisible:
|
|
45
46
|
id: "motion_prefs_full_button"
|
|
46
47
|
optional: true
|
|
48
|
+
- assertVisible:
|
|
49
|
+
id: "motion_prefs_reduced_button"
|
|
50
|
+
optional: true
|
|
51
|
+
- assertVisible:
|
|
52
|
+
id: "motion_prefs_off_button"
|
|
53
|
+
optional: true
|
|
47
54
|
- tapOn:
|
|
48
55
|
id: "motion_prefs_reduced_button"
|
|
49
56
|
- back
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
appId: ${APP_PACKAGE_NAME}
|
|
2
|
+
---
|
|
3
|
+
- clearState
|
|
4
|
+
- launchApp
|
|
5
|
+
- extendedWaitUntil:
|
|
6
|
+
visible: "Home"
|
|
7
|
+
timeout: 3000
|
|
8
|
+
# Navigate to Profile
|
|
9
|
+
- tapOn: "Profile"
|
|
10
|
+
- extendedWaitUntil:
|
|
11
|
+
visible:
|
|
12
|
+
id: "profile_avatar"
|
|
13
|
+
timeout: 3000
|
|
14
|
+
# Verify dark mode toggle is visible
|
|
15
|
+
- assertVisible:
|
|
16
|
+
id: "profile_dark_mode_toggle"
|
|
17
|
+
# Toggle dark mode ON
|
|
18
|
+
- tapOn:
|
|
19
|
+
id: "profile_dark_mode_toggle"
|
|
20
|
+
# Verify profile still renders in dark mode
|
|
21
|
+
- assertVisible:
|
|
22
|
+
id: "profile_avatar"
|
|
23
|
+
optional: true
|
|
24
|
+
- assertVisible:
|
|
25
|
+
id: "profile_name"
|
|
26
|
+
optional: true
|
|
27
|
+
# Navigate to Home to verify dark mode persists across tabs
|
|
28
|
+
- tapOn: "Home"
|
|
29
|
+
- extendedWaitUntil:
|
|
30
|
+
visible: "Home"
|
|
31
|
+
timeout: 3000
|
|
32
|
+
# Navigate back to Profile
|
|
33
|
+
- tapOn: "Profile"
|
|
34
|
+
- extendedWaitUntil:
|
|
35
|
+
visible:
|
|
36
|
+
id: "profile_avatar"
|
|
37
|
+
timeout: 3000
|
|
38
|
+
# Verify toggle is still ON (dark mode persisted)
|
|
39
|
+
- assertVisible:
|
|
40
|
+
id: "profile_dark_mode_toggle"
|
|
41
|
+
# Toggle dark mode OFF (back to system)
|
|
42
|
+
- tapOn:
|
|
43
|
+
id: "profile_dark_mode_toggle"
|
|
44
|
+
# Verify profile renders normally
|
|
45
|
+
- assertVisible:
|
|
46
|
+
id: "profile_avatar"
|
|
47
|
+
optional: true
|
|
48
|
+
- assertVisible:
|
|
49
|
+
id: "profile_name"
|
|
50
|
+
optional: true
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
package com.appship.social.core.animation
|
|
2
2
|
|
|
3
|
+
import android.view.HapticFeedbackConstants
|
|
3
4
|
import androidx.compose.animation.*
|
|
4
5
|
import androidx.compose.animation.core.*
|
|
6
|
+
import androidx.compose.foundation.clickable
|
|
7
|
+
import androidx.compose.foundation.gestures.detectTapGestures
|
|
8
|
+
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
9
|
+
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
|
5
10
|
import androidx.compose.foundation.layout.offset
|
|
6
11
|
import androidx.compose.runtime.*
|
|
7
12
|
import androidx.compose.ui.Modifier
|
|
8
13
|
import androidx.compose.ui.composed
|
|
9
14
|
import androidx.compose.ui.draw.alpha
|
|
10
15
|
import androidx.compose.ui.draw.scale
|
|
16
|
+
import androidx.compose.ui.draw.shadow
|
|
17
|
+
import androidx.compose.ui.graphics.Color
|
|
18
|
+
import androidx.compose.ui.input.pointer.pointerInput
|
|
19
|
+
import androidx.compose.ui.platform.LocalView
|
|
11
20
|
import androidx.compose.ui.unit.IntOffset
|
|
21
|
+
import androidx.compose.ui.unit.dp
|
|
12
22
|
|
|
13
23
|
/**
|
|
14
24
|
* Staggered appear animation for list items.
|
|
@@ -118,3 +128,181 @@ fun Modifier.bounceOnChange(
|
|
|
118
128
|
|
|
119
129
|
this.scale(bounceAnim)
|
|
120
130
|
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Scale-down press feedback for tappable elements.
|
|
134
|
+
* Scales to 0.95 on press with spring return animation.
|
|
135
|
+
*/
|
|
136
|
+
fun Modifier.scaleOnPress(): Modifier = composed {
|
|
137
|
+
val interactionSource = remember { MutableInteractionSource() }
|
|
138
|
+
val isPressed by interactionSource.collectIsPressedAsState()
|
|
139
|
+
|
|
140
|
+
val scale by animateFloatAsState(
|
|
141
|
+
targetValue = if (isPressed) 0.95f else 1f,
|
|
142
|
+
animationSpec = spring(
|
|
143
|
+
dampingRatio = Spring.DampingRatioMediumBouncy,
|
|
144
|
+
stiffness = Spring.StiffnessMedium
|
|
145
|
+
),
|
|
146
|
+
label = "pressScale"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
this
|
|
150
|
+
.scale(scale)
|
|
151
|
+
.clickable(
|
|
152
|
+
interactionSource = interactionSource,
|
|
153
|
+
indication = null
|
|
154
|
+
) { }
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Triggers haptic feedback on tap.
|
|
159
|
+
*/
|
|
160
|
+
fun Modifier.hapticFeedback(): Modifier = composed {
|
|
161
|
+
val view = LocalView.current
|
|
162
|
+
this.pointerInput(Unit) {
|
|
163
|
+
detectTapGestures {
|
|
164
|
+
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Slide-from-side + fade-in animation on appear.
|
|
171
|
+
*/
|
|
172
|
+
fun Modifier.slideAndFade(
|
|
173
|
+
delayMs: Int = 0,
|
|
174
|
+
durationMs: Int = 400
|
|
175
|
+
): Modifier = composed {
|
|
176
|
+
var isVisible by remember { mutableStateOf(false) }
|
|
177
|
+
|
|
178
|
+
val alpha by animateFloatAsState(
|
|
179
|
+
targetValue = if (isVisible) 1f else 0f,
|
|
180
|
+
animationSpec = tween(
|
|
181
|
+
durationMillis = durationMs,
|
|
182
|
+
delayMillis = delayMs,
|
|
183
|
+
easing = FastOutSlowInEasing
|
|
184
|
+
),
|
|
185
|
+
label = "slideAndFadeAlpha"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
val offsetX by animateIntAsState(
|
|
189
|
+
targetValue = if (isVisible) 0 else 40,
|
|
190
|
+
animationSpec = tween(
|
|
191
|
+
durationMillis = durationMs,
|
|
192
|
+
delayMillis = delayMs,
|
|
193
|
+
easing = FastOutSlowInEasing
|
|
194
|
+
),
|
|
195
|
+
label = "slideAndFadeOffsetX"
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
LaunchedEffect(Unit) {
|
|
199
|
+
isVisible = true
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
this
|
|
203
|
+
.alpha(alpha)
|
|
204
|
+
.offset { IntOffset(offsetX, 0) }
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Subtle colored shadow for cards and elevated surfaces.
|
|
209
|
+
* Uses Material 3 elevation with optional color tinting.
|
|
210
|
+
*/
|
|
211
|
+
fun Modifier.cardShadow(
|
|
212
|
+
color: Color = Color.Black,
|
|
213
|
+
elevation: Float = 4f
|
|
214
|
+
): Modifier = composed {
|
|
215
|
+
this.shadow(
|
|
216
|
+
elevation = elevation.dp,
|
|
217
|
+
ambientColor = color.copy(alpha = 0.08f),
|
|
218
|
+
spotColor = color.copy(alpha = 0.12f),
|
|
219
|
+
shape = androidx.compose.foundation.shape.RoundedCornerShape(12.dp)
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Deeper shadow for floating elements (FABs, modals).
|
|
225
|
+
*/
|
|
226
|
+
fun Modifier.elevatedShadow(
|
|
227
|
+
color: Color = Color.Black
|
|
228
|
+
): Modifier = composed {
|
|
229
|
+
this.shadow(
|
|
230
|
+
elevation = 8.dp,
|
|
231
|
+
ambientColor = color.copy(alpha = 0.12f),
|
|
232
|
+
spotColor = color.copy(alpha = 0.16f),
|
|
233
|
+
shape = androidx.compose.foundation.shape.RoundedCornerShape(16.dp)
|
|
234
|
+
)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Shimmer loading overlay for skeleton placeholder views.
|
|
239
|
+
* Sweeps a translucent gradient across the content repeatedly.
|
|
240
|
+
*/
|
|
241
|
+
fun Modifier.shimmer(
|
|
242
|
+
durationMs: Int = 1500
|
|
243
|
+
): Modifier = composed {
|
|
244
|
+
val transition = rememberInfiniteTransition(label = "shimmer")
|
|
245
|
+
val translateAnim by transition.animateFloat(
|
|
246
|
+
initialValue = -1f,
|
|
247
|
+
targetValue = 2f,
|
|
248
|
+
animationSpec = infiniteRepeatable(
|
|
249
|
+
animation = tween(durationMillis = durationMs, easing = LinearEasing),
|
|
250
|
+
repeatMode = RepeatMode.Restart
|
|
251
|
+
),
|
|
252
|
+
label = "shimmerTranslate"
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
this.alpha(0.3f + 0.7f * ((translateAnim + 1f) / 3f).coerceIn(0f, 1f))
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Repeating scale pulse animation for live indicators and notifications.
|
|
260
|
+
*/
|
|
261
|
+
fun Modifier.pulse(
|
|
262
|
+
intensity: Float = 0.05f,
|
|
263
|
+
durationMs: Int = 1000
|
|
264
|
+
): Modifier = composed {
|
|
265
|
+
val transition = rememberInfiniteTransition(label = "pulse")
|
|
266
|
+
val scale by transition.animateFloat(
|
|
267
|
+
initialValue = 1f,
|
|
268
|
+
targetValue = 1f + intensity,
|
|
269
|
+
animationSpec = infiniteRepeatable(
|
|
270
|
+
animation = tween(durationMillis = durationMs, easing = FastOutSlowInEasing),
|
|
271
|
+
repeatMode = RepeatMode.Reverse
|
|
272
|
+
),
|
|
273
|
+
label = "pulseScale"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
this.scale(scale)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Heart/like bounce effect when toggled active.
|
|
281
|
+
* Scales up to 1.3x then springs back. Perfect for favorite buttons.
|
|
282
|
+
*/
|
|
283
|
+
fun Modifier.heartBounce(
|
|
284
|
+
isActive: Boolean
|
|
285
|
+
): Modifier = composed {
|
|
286
|
+
var previousActive by remember { mutableStateOf(isActive) }
|
|
287
|
+
var animatedScale by remember { mutableFloatStateOf(1f) }
|
|
288
|
+
|
|
289
|
+
val bounceAnim by animateFloatAsState(
|
|
290
|
+
targetValue = animatedScale,
|
|
291
|
+
animationSpec = spring(
|
|
292
|
+
dampingRatio = Spring.DampingRatioMediumBouncy,
|
|
293
|
+
stiffness = Spring.StiffnessMedium
|
|
294
|
+
),
|
|
295
|
+
label = "heartBounce"
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
LaunchedEffect(isActive) {
|
|
299
|
+
if (isActive && !previousActive) {
|
|
300
|
+
animatedScale = 1.3f
|
|
301
|
+
kotlinx.coroutines.delay(200)
|
|
302
|
+
animatedScale = 1f
|
|
303
|
+
}
|
|
304
|
+
previousActive = isActive
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
this.scale(bounceAnim)
|
|
308
|
+
}
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
package com.appship.task.core.animation
|
|
2
2
|
|
|
3
|
+
import android.view.HapticFeedbackConstants
|
|
3
4
|
import androidx.compose.animation.*
|
|
4
5
|
import androidx.compose.animation.core.*
|
|
6
|
+
import androidx.compose.foundation.clickable
|
|
7
|
+
import androidx.compose.foundation.gestures.detectTapGestures
|
|
8
|
+
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
9
|
+
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
|
5
10
|
import androidx.compose.foundation.layout.offset
|
|
6
11
|
import androidx.compose.runtime.*
|
|
7
12
|
import androidx.compose.ui.Modifier
|
|
8
13
|
import androidx.compose.ui.composed
|
|
9
14
|
import androidx.compose.ui.draw.alpha
|
|
10
15
|
import androidx.compose.ui.draw.scale
|
|
16
|
+
import androidx.compose.ui.draw.shadow
|
|
17
|
+
import androidx.compose.ui.graphics.Color
|
|
18
|
+
import androidx.compose.ui.input.pointer.pointerInput
|
|
19
|
+
import androidx.compose.ui.platform.LocalView
|
|
11
20
|
import androidx.compose.ui.unit.IntOffset
|
|
21
|
+
import androidx.compose.ui.unit.dp
|
|
12
22
|
|
|
13
23
|
/**
|
|
14
24
|
* Staggered appear animation for list items.
|
|
@@ -118,3 +128,181 @@ fun Modifier.bounceOnChange(
|
|
|
118
128
|
|
|
119
129
|
this.scale(bounceAnim)
|
|
120
130
|
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Scale-down press feedback for tappable elements.
|
|
134
|
+
* Scales to 0.95 on press with spring return animation.
|
|
135
|
+
*/
|
|
136
|
+
fun Modifier.scaleOnPress(): Modifier = composed {
|
|
137
|
+
val interactionSource = remember { MutableInteractionSource() }
|
|
138
|
+
val isPressed by interactionSource.collectIsPressedAsState()
|
|
139
|
+
|
|
140
|
+
val scale by animateFloatAsState(
|
|
141
|
+
targetValue = if (isPressed) 0.95f else 1f,
|
|
142
|
+
animationSpec = spring(
|
|
143
|
+
dampingRatio = Spring.DampingRatioMediumBouncy,
|
|
144
|
+
stiffness = Spring.StiffnessMedium
|
|
145
|
+
),
|
|
146
|
+
label = "pressScale"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
this
|
|
150
|
+
.scale(scale)
|
|
151
|
+
.clickable(
|
|
152
|
+
interactionSource = interactionSource,
|
|
153
|
+
indication = null
|
|
154
|
+
) { }
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Triggers haptic feedback on tap.
|
|
159
|
+
*/
|
|
160
|
+
fun Modifier.hapticFeedback(): Modifier = composed {
|
|
161
|
+
val view = LocalView.current
|
|
162
|
+
this.pointerInput(Unit) {
|
|
163
|
+
detectTapGestures {
|
|
164
|
+
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Slide-from-side + fade-in animation on appear.
|
|
171
|
+
*/
|
|
172
|
+
fun Modifier.slideAndFade(
|
|
173
|
+
delayMs: Int = 0,
|
|
174
|
+
durationMs: Int = 400
|
|
175
|
+
): Modifier = composed {
|
|
176
|
+
var isVisible by remember { mutableStateOf(false) }
|
|
177
|
+
|
|
178
|
+
val alpha by animateFloatAsState(
|
|
179
|
+
targetValue = if (isVisible) 1f else 0f,
|
|
180
|
+
animationSpec = tween(
|
|
181
|
+
durationMillis = durationMs,
|
|
182
|
+
delayMillis = delayMs,
|
|
183
|
+
easing = FastOutSlowInEasing
|
|
184
|
+
),
|
|
185
|
+
label = "slideAndFadeAlpha"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
val offsetX by animateIntAsState(
|
|
189
|
+
targetValue = if (isVisible) 0 else 40,
|
|
190
|
+
animationSpec = tween(
|
|
191
|
+
durationMillis = durationMs,
|
|
192
|
+
delayMillis = delayMs,
|
|
193
|
+
easing = FastOutSlowInEasing
|
|
194
|
+
),
|
|
195
|
+
label = "slideAndFadeOffsetX"
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
LaunchedEffect(Unit) {
|
|
199
|
+
isVisible = true
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
this
|
|
203
|
+
.alpha(alpha)
|
|
204
|
+
.offset { IntOffset(offsetX, 0) }
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Subtle colored shadow for cards and elevated surfaces.
|
|
209
|
+
* Uses Material 3 elevation with optional color tinting.
|
|
210
|
+
*/
|
|
211
|
+
fun Modifier.cardShadow(
|
|
212
|
+
color: Color = Color.Black,
|
|
213
|
+
elevation: Float = 4f
|
|
214
|
+
): Modifier = composed {
|
|
215
|
+
this.shadow(
|
|
216
|
+
elevation = elevation.dp,
|
|
217
|
+
ambientColor = color.copy(alpha = 0.08f),
|
|
218
|
+
spotColor = color.copy(alpha = 0.12f),
|
|
219
|
+
shape = androidx.compose.foundation.shape.RoundedCornerShape(12.dp)
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Deeper shadow for floating elements (FABs, modals).
|
|
225
|
+
*/
|
|
226
|
+
fun Modifier.elevatedShadow(
|
|
227
|
+
color: Color = Color.Black
|
|
228
|
+
): Modifier = composed {
|
|
229
|
+
this.shadow(
|
|
230
|
+
elevation = 8.dp,
|
|
231
|
+
ambientColor = color.copy(alpha = 0.12f),
|
|
232
|
+
spotColor = color.copy(alpha = 0.16f),
|
|
233
|
+
shape = androidx.compose.foundation.shape.RoundedCornerShape(16.dp)
|
|
234
|
+
)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Shimmer loading overlay for skeleton placeholder views.
|
|
239
|
+
* Sweeps a translucent gradient across the content repeatedly.
|
|
240
|
+
*/
|
|
241
|
+
fun Modifier.shimmer(
|
|
242
|
+
durationMs: Int = 1500
|
|
243
|
+
): Modifier = composed {
|
|
244
|
+
val transition = rememberInfiniteTransition(label = "shimmer")
|
|
245
|
+
val translateAnim by transition.animateFloat(
|
|
246
|
+
initialValue = -1f,
|
|
247
|
+
targetValue = 2f,
|
|
248
|
+
animationSpec = infiniteRepeatable(
|
|
249
|
+
animation = tween(durationMillis = durationMs, easing = LinearEasing),
|
|
250
|
+
repeatMode = RepeatMode.Restart
|
|
251
|
+
),
|
|
252
|
+
label = "shimmerTranslate"
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
this.alpha(0.3f + 0.7f * ((translateAnim + 1f) / 3f).coerceIn(0f, 1f))
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Repeating scale pulse animation for live indicators and notifications.
|
|
260
|
+
*/
|
|
261
|
+
fun Modifier.pulse(
|
|
262
|
+
intensity: Float = 0.05f,
|
|
263
|
+
durationMs: Int = 1000
|
|
264
|
+
): Modifier = composed {
|
|
265
|
+
val transition = rememberInfiniteTransition(label = "pulse")
|
|
266
|
+
val scale by transition.animateFloat(
|
|
267
|
+
initialValue = 1f,
|
|
268
|
+
targetValue = 1f + intensity,
|
|
269
|
+
animationSpec = infiniteRepeatable(
|
|
270
|
+
animation = tween(durationMillis = durationMs, easing = FastOutSlowInEasing),
|
|
271
|
+
repeatMode = RepeatMode.Reverse
|
|
272
|
+
),
|
|
273
|
+
label = "pulseScale"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
this.scale(scale)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Heart/like bounce effect when toggled active.
|
|
281
|
+
* Scales up to 1.3x then springs back. Perfect for favorite buttons.
|
|
282
|
+
*/
|
|
283
|
+
fun Modifier.heartBounce(
|
|
284
|
+
isActive: Boolean
|
|
285
|
+
): Modifier = composed {
|
|
286
|
+
var previousActive by remember { mutableStateOf(isActive) }
|
|
287
|
+
var animatedScale by remember { mutableFloatStateOf(1f) }
|
|
288
|
+
|
|
289
|
+
val bounceAnim by animateFloatAsState(
|
|
290
|
+
targetValue = animatedScale,
|
|
291
|
+
animationSpec = spring(
|
|
292
|
+
dampingRatio = Spring.DampingRatioMediumBouncy,
|
|
293
|
+
stiffness = Spring.StiffnessMedium
|
|
294
|
+
),
|
|
295
|
+
label = "heartBounce"
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
LaunchedEffect(isActive) {
|
|
299
|
+
if (isActive && !previousActive) {
|
|
300
|
+
animatedScale = 1.3f
|
|
301
|
+
kotlinx.coroutines.delay(200)
|
|
302
|
+
animatedScale = 1f
|
|
303
|
+
}
|
|
304
|
+
previousActive = isActive
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
this.scale(bounceAnim)
|
|
308
|
+
}
|
|
@@ -6,6 +6,7 @@ import androidx.compose.material3.*
|
|
|
6
6
|
import androidx.compose.runtime.*
|
|
7
7
|
import androidx.compose.ui.Modifier
|
|
8
8
|
import androidx.compose.foundation.clickable
|
|
9
|
+
import androidx.compose.material.icons.Icons
|
|
9
10
|
import androidx.compose.material.icons.filled.*
|
|
10
11
|
import androidx.compose.ui.platform.testTag
|
|
11
12
|
import androidx.compose.ui.res.stringResource
|
|
@@ -69,8 +70,8 @@ fun SettingsScreen(
|
|
|
69
70
|
item {
|
|
70
71
|
ListItem(
|
|
71
72
|
headlineContent = { Text(stringResource(R.string.motion_prefs_animation_level)) },
|
|
72
|
-
leadingContent = { Icon(Icons.Default.
|
|
73
|
-
trailingContent = { Icon(Icons.Default.
|
|
73
|
+
leadingContent = { Icon(Icons.Default.Star, contentDescription = null) },
|
|
74
|
+
trailingContent = { Icon(Icons.Default.ArrowForward, contentDescription = null) },
|
|
74
75
|
modifier = Modifier
|
|
75
76
|
.clickable { onMotionPreferencesClick() }
|
|
76
77
|
.testTag("profile_motion_preferences")
|