@amplitude/wizard 1.0.0-beta.2 → 1.0.0
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/README.md +171 -74
- package/dist/bin.js +338 -222
- package/dist/src/lib/agent-interface.js +64 -9
- package/dist/src/lib/agent-runner.js +1 -10
- package/dist/src/lib/api.d.ts +22 -4
- package/dist/src/lib/api.js +114 -12
- package/dist/src/lib/commandments.js +14 -1
- package/dist/src/lib/constants.d.ts +6 -5
- package/dist/src/lib/constants.js +13 -13
- package/dist/src/lib/credential-resolution.d.ts +45 -0
- package/dist/src/lib/credential-resolution.js +311 -0
- package/dist/src/lib/exit-codes.d.ts +10 -0
- package/dist/src/lib/exit-codes.js +12 -0
- package/dist/src/lib/health-checks/statuspage.d.ts +1 -0
- package/dist/src/lib/health-checks/statuspage.js +5 -1
- package/dist/src/lib/mode-config.d.ts +14 -0
- package/dist/src/lib/mode-config.js +14 -0
- package/dist/src/lib/session-checkpoint.d.ts +27 -0
- package/dist/src/lib/session-checkpoint.js +134 -0
- package/dist/src/lib/wizard-session.d.ts +44 -1
- package/dist/src/lib/wizard-session.js +70 -14
- package/dist/src/lib/wizard-tools.js +19 -4
- package/dist/src/steps/add-mcp-server-to-clients/clients/claude.d.ts +3 -0
- package/dist/src/steps/add-mcp-server-to-clients/clients/claude.js +6 -0
- package/dist/src/steps/add-mcp-server-to-clients/clients/cursor.js +3 -1
- package/dist/src/ui/agent-ui.d.ts +91 -0
- package/dist/src/ui/agent-ui.js +277 -0
- package/dist/src/ui/logging-ui.js +1 -1
- package/dist/src/ui/tui/App.d.ts +12 -0
- package/dist/src/ui/tui/App.js +29 -18
- package/dist/src/ui/tui/components/AmplitudeLogo.js +16 -17
- package/dist/src/ui/tui/components/AmplitudeTextLogo.d.ts +0 -2
- package/dist/src/ui/tui/components/AmplitudeTextLogo.js +53 -18
- package/dist/src/ui/tui/components/BrailleSpinner.d.ts +8 -0
- package/dist/src/ui/tui/components/BrailleSpinner.js +15 -0
- package/dist/src/ui/tui/components/ConsoleView.d.ts +8 -11
- package/dist/src/ui/tui/components/ConsoleView.js +51 -34
- package/dist/src/ui/tui/components/HeaderBar.d.ts +12 -0
- package/dist/src/ui/tui/components/HeaderBar.js +17 -0
- package/dist/src/ui/tui/components/JourneyStepper.d.ts +16 -0
- package/dist/src/ui/tui/components/JourneyStepper.js +83 -0
- package/dist/src/ui/tui/components/KeyHintBar.d.ts +19 -0
- package/dist/src/ui/tui/components/KeyHintBar.js +20 -0
- package/dist/src/ui/tui/console-commands.d.ts +1 -2
- package/dist/src/ui/tui/console-commands.js +48 -7
- package/dist/src/ui/tui/flows.d.ts +1 -1
- package/dist/src/ui/tui/flows.js +1 -1
- package/dist/src/ui/tui/hooks/useAsyncEffect.d.ts +15 -0
- package/dist/src/ui/tui/hooks/useAsyncEffect.js +35 -0
- package/dist/src/ui/tui/hooks/useWizardStore.d.ts +9 -0
- package/dist/src/ui/tui/hooks/useWizardStore.js +11 -0
- package/dist/src/ui/tui/ink-ui.js +1 -1
- package/dist/src/ui/tui/primitives/DissolveTransition.js +4 -5
- package/dist/src/ui/tui/primitives/EventPlanViewer.d.ts +3 -1
- package/dist/src/ui/tui/primitives/EventPlanViewer.js +8 -3
- package/dist/src/ui/tui/primitives/ProgressList.js +1 -1
- package/dist/src/ui/tui/primitives/SlashCommandInput.js +19 -4
- package/dist/src/ui/tui/primitives/SplitView.d.ts +2 -1
- package/dist/src/ui/tui/primitives/SplitView.js +10 -2
- package/dist/src/ui/tui/primitives/TabContainer.js +10 -2
- package/dist/src/ui/tui/primitives/index.d.ts +0 -1
- package/dist/src/ui/tui/primitives/index.js +0 -1
- package/dist/src/ui/tui/router.js +1 -1
- package/dist/src/ui/tui/screen-registry.d.ts +0 -7
- package/dist/src/ui/tui/screen-registry.js +13 -4
- package/dist/src/ui/tui/screens/ActivationOptionsScreen.d.ts +2 -2
- package/dist/src/ui/tui/screens/ActivationOptionsScreen.js +8 -8
- package/dist/src/ui/tui/screens/AuthScreen.js +57 -27
- package/dist/src/ui/tui/screens/ChecklistScreen.d.ts +2 -12
- package/dist/src/ui/tui/screens/ChecklistScreen.js +22 -33
- package/dist/src/ui/tui/screens/DataIngestionCheckScreen.d.ts +3 -12
- package/dist/src/ui/tui/screens/DataIngestionCheckScreen.js +109 -39
- package/dist/src/ui/tui/screens/DataSetupScreen.d.ts +3 -3
- package/dist/src/ui/tui/screens/DataSetupScreen.js +17 -10
- package/dist/src/ui/tui/screens/IntroScreen.d.ts +5 -3
- package/dist/src/ui/tui/screens/IntroScreen.js +132 -41
- package/dist/src/ui/tui/screens/LoginScreen.d.ts +1 -1
- package/dist/src/ui/tui/screens/LoginScreen.js +4 -4
- package/dist/src/ui/tui/screens/LogoutScreen.d.ts +4 -2
- package/dist/src/ui/tui/screens/LogoutScreen.js +17 -5
- package/dist/src/ui/tui/screens/McpScreen.d.ts +4 -4
- package/dist/src/ui/tui/screens/McpScreen.js +25 -17
- package/dist/src/ui/tui/screens/OutageScreen.d.ts +1 -1
- package/dist/src/ui/tui/screens/OutageScreen.js +5 -5
- package/dist/src/ui/tui/screens/OutroScreen.d.ts +5 -0
- package/dist/src/ui/tui/screens/OutroScreen.js +21 -14
- package/dist/src/ui/tui/screens/RegionSelectScreen.js +15 -13
- package/dist/src/ui/tui/screens/RunScreen.d.ts +7 -5
- package/dist/src/ui/tui/screens/RunScreen.js +102 -157
- package/dist/src/ui/tui/screens/SettingsOverrideScreen.d.ts +1 -1
- package/dist/src/ui/tui/screens/SettingsOverrideScreen.js +6 -5
- package/dist/src/ui/tui/screens/SetupScreen.d.ts +1 -1
- package/dist/src/ui/tui/screens/SetupScreen.js +7 -7
- package/dist/src/ui/tui/screens/SlackScreen.d.ts +2 -2
- package/dist/src/ui/tui/screens/SlackScreen.js +60 -35
- package/dist/src/ui/tui/session-constants.d.ts +41 -0
- package/dist/src/ui/tui/session-constants.js +38 -0
- package/dist/src/ui/tui/start-tui.d.ts +3 -1
- package/dist/src/ui/tui/start-tui.js +14 -10
- package/dist/src/ui/tui/store.d.ts +2 -1
- package/dist/src/ui/tui/store.js +33 -7
- package/dist/src/ui/tui/styles.d.ts +75 -19
- package/dist/src/ui/tui/styles.js +101 -19
- package/dist/src/ui/tui/utils/classify-error.d.ts +14 -0
- package/dist/src/ui/tui/utils/classify-error.js +90 -0
- package/dist/src/ui/tui/utils/diagnostics.d.ts +21 -0
- package/dist/src/ui/tui/utils/diagnostics.js +72 -0
- package/dist/src/ui/tui/utils/with-retry.d.ts +12 -0
- package/dist/src/ui/tui/utils/with-retry.js +32 -0
- package/dist/src/ui/tui/utils/with-timeout.d.ts +10 -0
- package/dist/src/ui/tui/utils/with-timeout.js +24 -0
- package/dist/src/utils/ampli-settings.d.ts +1 -1
- package/dist/src/utils/ampli-settings.js +15 -5
- package/dist/src/utils/api-key-store.js +5 -5
- package/dist/src/utils/atomic-write.d.ts +15 -0
- package/dist/src/utils/atomic-write.js +34 -0
- package/dist/src/utils/setup-utils.js +2 -2
- package/dist/src/utils/token-refresh.d.ts +22 -0
- package/dist/src/utils/token-refresh.js +79 -0
- package/dist/src/utils/wizard-abort.js +6 -1
- package/package.json +6 -6
- package/skills/instrumentation/add-analytics-instrumentation/SKILL.md +142 -0
- package/skills/instrumentation/diff-intake/SKILL.md +128 -0
- package/skills/instrumentation/discover-analytics-patterns/SKILL.md +185 -0
- package/skills/instrumentation/discover-event-surfaces/SKILL.md +322 -0
- package/skills/instrumentation/discover-event-surfaces/references/best-practices.md +563 -0
- package/skills/instrumentation/instrument-events/SKILL.md +169 -0
- package/skills/instrumentation/instrument-events/references/best-practices.md +563 -0
- package/skills/integration/integration-android/SKILL.md +49 -0
- package/skills/integration/integration-android/references/EXAMPLE.md +1977 -0
- package/skills/integration/integration-android/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-android/references/analytics.md +1778 -0
- package/skills/integration/integration-android/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-android/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-android/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-android/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-angular/SKILL.md +49 -0
- package/skills/integration/integration-angular/references/EXAMPLE.md +899 -0
- package/skills/integration/integration-angular/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-angular/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-angular/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-angular/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-angular/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-angular/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-astro-hybrid/SKILL.md +56 -0
- package/skills/integration/integration-astro-hybrid/references/EXAMPLE.md +1095 -0
- package/skills/integration/integration-astro-hybrid/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-astro-hybrid/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-astro-hybrid/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-astro-hybrid/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-astro-hybrid/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-astro-hybrid/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-astro-ssr/SKILL.md +52 -0
- package/skills/integration/integration-astro-ssr/references/EXAMPLE.md +1106 -0
- package/skills/integration/integration-astro-ssr/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-astro-ssr/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-astro-ssr/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-astro-ssr/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-astro-ssr/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-astro-ssr/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-astro-static/SKILL.md +49 -0
- package/skills/integration/integration-astro-static/references/EXAMPLE.md +910 -0
- package/skills/integration/integration-astro-static/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-astro-static/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-astro-static/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-astro-static/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-astro-static/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-astro-static/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-astro-view-transitions/SKILL.md +51 -0
- package/skills/integration/integration-astro-view-transitions/references/EXAMPLE.md +979 -0
- package/skills/integration/integration-astro-view-transitions/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-astro-view-transitions/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-astro-view-transitions/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-astro-view-transitions/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-astro-view-transitions/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-astro-view-transitions/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-django/SKILL.md +57 -0
- package/skills/integration/integration-django/references/EXAMPLE.md +1005 -0
- package/skills/integration/integration-django/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-django/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-django/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-django/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-django/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-django/references/python.md +1424 -0
- package/skills/integration/integration-expo/SKILL.md +53 -0
- package/skills/integration/integration-expo/references/EXAMPLE.md +1291 -0
- package/skills/integration/integration-expo/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-expo/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-expo/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-expo/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-expo/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-expo/references/react-native-sdk.md +2819 -0
- package/skills/integration/integration-fastapi/SKILL.md +57 -0
- package/skills/integration/integration-fastapi/references/EXAMPLE.md +1389 -0
- package/skills/integration/integration-fastapi/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-fastapi/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-fastapi/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-fastapi/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-fastapi/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-fastapi/references/python.md +1424 -0
- package/skills/integration/integration-flask/SKILL.md +56 -0
- package/skills/integration/integration-flask/references/EXAMPLE.md +1130 -0
- package/skills/integration/integration-flask/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-flask/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-flask/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-flask/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-flask/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-flask/references/python.md +1424 -0
- package/skills/integration/integration-javascript_node/SKILL.md +54 -0
- package/skills/integration/integration-javascript_node/references/EXAMPLE.md +365 -0
- package/skills/integration/integration-javascript_node/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-javascript_node/references/analytics.md +1778 -0
- package/skills/integration/integration-javascript_node/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-javascript_node/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-javascript_node/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-javascript_node/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-javascript_web/SKILL.md +58 -0
- package/skills/integration/integration-javascript_web/references/EXAMPLE.md +451 -0
- package/skills/integration/integration-javascript_web/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-javascript_web/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-javascript_web/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-javascript_web/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-javascript_web/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-javascript_web/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-laravel/SKILL.md +52 -0
- package/skills/integration/integration-laravel/references/EXAMPLE.md +2039 -0
- package/skills/integration/integration-laravel/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-laravel/references/analytics.md +1778 -0
- package/skills/integration/integration-laravel/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-laravel/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-laravel/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-laravel/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-nextjs-app-router/SKILL.md +54 -0
- package/skills/integration/integration-nextjs-app-router/references/EXAMPLE.md +673 -0
- package/skills/integration/integration-nextjs-app-router/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-nextjs-app-router/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-nextjs-app-router/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-nextjs-app-router/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-nextjs-app-router/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-nextjs-app-router/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-nextjs-pages-router/SKILL.md +54 -0
- package/skills/integration/integration-nextjs-pages-router/references/EXAMPLE.md +735 -0
- package/skills/integration/integration-nextjs-pages-router/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-nextjs-pages-router/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-nextjs-pages-router/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-nextjs-pages-router/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-nextjs-pages-router/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-nextjs-pages-router/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-nuxt-3.6/SKILL.md +46 -0
- package/skills/integration/integration-nuxt-3.6/references/EXAMPLE.md +8422 -0
- package/skills/integration/integration-nuxt-3.6/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-nuxt-3.6/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-nuxt-3.6/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-nuxt-3.6/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-nuxt-3.6/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-nuxt-3.6/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-nuxt-4/SKILL.md +46 -0
- package/skills/integration/integration-nuxt-4/references/EXAMPLE.md +8670 -0
- package/skills/integration/integration-nuxt-4/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-nuxt-4/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-nuxt-4/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-nuxt-4/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-nuxt-4/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-nuxt-4/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-python/SKILL.md +53 -0
- package/skills/integration/integration-python/references/EXAMPLE.md +445 -0
- package/skills/integration/integration-python/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-python/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-python/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-python/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-python/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-python/references/python.md +1424 -0
- package/skills/integration/integration-react-native/SKILL.md +49 -0
- package/skills/integration/integration-react-native/references/EXAMPLE.md +2253 -0
- package/skills/integration/integration-react-native/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-react-native/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-react-native/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-react-native/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-react-native/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-react-native/references/react-native-sdk.md +2819 -0
- package/skills/integration/integration-react-react-router-6/SKILL.md +53 -0
- package/skills/integration/integration-react-react-router-6/references/EXAMPLE.md +570 -0
- package/skills/integration/integration-react-react-router-6/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-react-react-router-6/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-react-react-router-6/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-react-react-router-6/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-react-react-router-6/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-react-react-router-6/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-react-react-router-7-data/SKILL.md +53 -0
- package/skills/integration/integration-react-react-router-7-data/references/EXAMPLE.md +830 -0
- package/skills/integration/integration-react-react-router-7-data/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-react-react-router-7-data/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-react-react-router-7-data/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-react-react-router-7-data/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-react-react-router-7-data/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-react-react-router-7-data/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-react-react-router-7-declarative/SKILL.md +53 -0
- package/skills/integration/integration-react-react-router-7-declarative/references/EXAMPLE.md +609 -0
- package/skills/integration/integration-react-react-router-7-declarative/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-react-react-router-7-declarative/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-react-react-router-7-declarative/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-react-react-router-7-declarative/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-react-react-router-7-declarative/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-react-react-router-7-declarative/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-react-react-router-7-framework/SKILL.md +53 -0
- package/skills/integration/integration-react-react-router-7-framework/references/EXAMPLE.md +1081 -0
- package/skills/integration/integration-react-react-router-7-framework/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-react-react-router-7-framework/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-react-react-router-7-framework/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-react-react-router-7-framework/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-react-react-router-7-framework/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-react-react-router-7-framework/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-react-tanstack-router-code-based/SKILL.md +57 -0
- package/skills/integration/integration-react-tanstack-router-code-based/references/EXAMPLE.md +659 -0
- package/skills/integration/integration-react-tanstack-router-code-based/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-react-tanstack-router-code-based/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-react-tanstack-router-code-based/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-react-tanstack-router-code-based/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-react-tanstack-router-code-based/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-react-tanstack-router-code-based/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-react-tanstack-router-file-based/SKILL.md +57 -0
- package/skills/integration/integration-react-tanstack-router-file-based/references/EXAMPLE.md +777 -0
- package/skills/integration/integration-react-tanstack-router-file-based/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-react-tanstack-router-file-based/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-react-tanstack-router-file-based/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-react-tanstack-router-file-based/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-react-tanstack-router-file-based/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-react-tanstack-router-file-based/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-react-vite/SKILL.md +53 -0
- package/skills/integration/integration-react-vite/references/EXAMPLE.md +542 -0
- package/skills/integration/integration-react-vite/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-react-vite/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-react-vite/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-react-vite/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-react-vite/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-react-vite/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-ruby/SKILL.md +50 -0
- package/skills/integration/integration-ruby/references/EXAMPLE.md +420 -0
- package/skills/integration/integration-ruby/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-ruby/references/analytics.md +1778 -0
- package/skills/integration/integration-ruby/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-ruby/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-ruby/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-ruby/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-ruby-on-rails/SKILL.md +55 -0
- package/skills/integration/integration-ruby-on-rails/references/EXAMPLE.md +1013 -0
- package/skills/integration/integration-ruby-on-rails/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-ruby-on-rails/references/analytics.md +1778 -0
- package/skills/integration/integration-ruby-on-rails/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-ruby-on-rails/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-ruby-on-rails/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-ruby-on-rails/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-sveltekit/SKILL.md +47 -0
- package/skills/integration/integration-sveltekit/references/EXAMPLE.md +14121 -0
- package/skills/integration/integration-sveltekit/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-sveltekit/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-sveltekit/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-sveltekit/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-sveltekit/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-sveltekit/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-swift/SKILL.md +49 -0
- package/skills/integration/integration-swift/references/EXAMPLE.md +660 -0
- package/skills/integration/integration-swift/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-swift/references/analytics.md +1778 -0
- package/skills/integration/integration-swift/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-swift/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-swift/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-swift/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-tanstack-start/SKILL.md +58 -0
- package/skills/integration/integration-tanstack-start/references/EXAMPLE.md +998 -0
- package/skills/integration/integration-tanstack-start/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-tanstack-start/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-tanstack-start/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-tanstack-start/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-tanstack-start/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-tanstack-start/references/browser-sdk-2.md +4680 -0
- package/skills/integration/integration-vue-3/SKILL.md +46 -0
- package/skills/integration/integration-vue-3/references/EXAMPLE.md +846 -0
- package/skills/integration/integration-vue-3/references/amplitude-quickstart.md +1845 -0
- package/skills/integration/integration-vue-3/references/basic-integration-1.0-begin.md +43 -0
- package/skills/integration/integration-vue-3/references/basic-integration-1.1-edit.md +35 -0
- package/skills/integration/integration-vue-3/references/basic-integration-1.2-revise.md +23 -0
- package/skills/integration/integration-vue-3/references/basic-integration-1.3-conclude.md +57 -0
- package/skills/integration/integration-vue-3/references/browser-sdk-2.md +4680 -0
- package/skills/taxonomy/amplitude-quickstart-taxonomy-agent/SKILL.md +228 -0
- package/dist/src/ui/tui/components/TitleBar.d.ts +0 -8
- package/dist/src/ui/tui/components/TitleBar.js +0 -27
- package/dist/src/ui/tui/primitives/KagiSmallWebViewer.d.ts +0 -7
- package/dist/src/ui/tui/primitives/KagiSmallWebViewer.js +0 -101
- package/dist/src/utils/anthropic-status.d.ts +0 -17
- package/dist/src/utils/anthropic-status.js +0 -51
|
@@ -0,0 +1,1977 @@
|
|
|
1
|
+
# Amplitude Android Example Project
|
|
2
|
+
|
|
3
|
+
Repository: https://github.com/amplitude/context-hub
|
|
4
|
+
Path: basics/android
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## README.md
|
|
9
|
+
|
|
10
|
+
# Amplitude Android example
|
|
11
|
+
|
|
12
|
+
This is an Android example demonstrating Amplitude integration with product analytics using Kotlin and Jetpack Compose.
|
|
13
|
+
|
|
14
|
+
This example uses the Amplitude Android SDK (`analytics-android`) to provide analytics integration with simplified configuration.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **Product Analytics**: Track user events and behaviors
|
|
19
|
+
- **User Authentication**: Demo login system with Amplitude user identification
|
|
20
|
+
- **Event Tracking**: Examples of custom event tracking throughout the app
|
|
21
|
+
|
|
22
|
+
## Getting Started
|
|
23
|
+
|
|
24
|
+
### 1. Prerequisites
|
|
25
|
+
|
|
26
|
+
- Android Studio (latest stable version)
|
|
27
|
+
- Android SDK (API level 24 or higher)
|
|
28
|
+
- JDK 11 or higher
|
|
29
|
+
- Gradle 8.0 or higher
|
|
30
|
+
- An [Amplitude account](https://amplitude.com/signup)
|
|
31
|
+
|
|
32
|
+
### 2. Configure Environment Variables
|
|
33
|
+
|
|
34
|
+
The Amplitude configuration is stored in `local.properties` (this file is gitignored):
|
|
35
|
+
|
|
36
|
+
```properties
|
|
37
|
+
# Amplitude configuration
|
|
38
|
+
amplitude.apiKey=your_amplitude_api_key
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Alternatively, you can configure Amplitude in your `build.gradle` file:
|
|
42
|
+
|
|
43
|
+
```gradle
|
|
44
|
+
android {
|
|
45
|
+
defaultConfig {
|
|
46
|
+
buildConfigField "String", "AMPLITUDE_API_KEY", "\"your_amplitude_api_key\""
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Get your Amplitude API key from your [Amplitude project settings](https://app.amplitude.com/).
|
|
52
|
+
|
|
53
|
+
### 3. Build and Run
|
|
54
|
+
|
|
55
|
+
1. Open the project in Android Studio
|
|
56
|
+
2. Sync Gradle files
|
|
57
|
+
3. Run the app on an emulator or physical device
|
|
58
|
+
|
|
59
|
+
## Project Structure
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
├── app/
|
|
63
|
+
│ ├── src/
|
|
64
|
+
│ │ ├── main/
|
|
65
|
+
│ │ │ ├── java/com/example/amplitude/
|
|
66
|
+
│ │ │ │ ├── BurritoApplication.kt # Application class with Amplitude initialization
|
|
67
|
+
│ │ │ │ ├── MainActivity.kt # Main activity
|
|
68
|
+
│ │ │ │ ├── ui/
|
|
69
|
+
│ │ │ │ │ ├── screens/
|
|
70
|
+
│ │ │ │ │ │ ├── HomeScreen.kt # Home screen with login
|
|
71
|
+
│ │ │ │ │ │ ├── BurritoScreen.kt # Demo feature screen with event tracking
|
|
72
|
+
│ │ │ │ │ │ └── ProfileScreen.kt # User profile screen
|
|
73
|
+
│ │ │ │ │ └── components/ # Reusable UI components
|
|
74
|
+
│ │ │ ├── res/ # Resources (layouts, strings, etc.)
|
|
75
|
+
│ │ │ └── AndroidManifest.xml # App manifest
|
|
76
|
+
│ │ └── test/ # Unit tests
|
|
77
|
+
│ └── build.gradle # App-level Gradle configuration
|
|
78
|
+
├── build.gradle # Project-level Gradle configuration
|
|
79
|
+
├── settings.gradle # Gradle settings
|
|
80
|
+
└── local.properties # Local configuration (gitignored)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Key Integration Points
|
|
84
|
+
|
|
85
|
+
### Application Initialization (BurritoApplication.kt)
|
|
86
|
+
|
|
87
|
+
Amplitude is initialized in the `Application` class to ensure it's available throughout the app lifecycle:
|
|
88
|
+
|
|
89
|
+
```kotlin
|
|
90
|
+
class BurritoApplication : Application() {
|
|
91
|
+
override fun onCreate() {
|
|
92
|
+
super.onCreate()
|
|
93
|
+
|
|
94
|
+
// Initialize Amplitude early in Application lifecycle
|
|
95
|
+
Amplitude.getInstance().initialize(
|
|
96
|
+
this,
|
|
97
|
+
BuildConfig.AMPLITUDE_API_KEY
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Key Points:**
|
|
104
|
+
- Amplitude is initialized in `onCreate()` to ensure it's initialized as early as possible
|
|
105
|
+
- Configuration is loaded from `BuildConfig` (set in `build.gradle`)
|
|
106
|
+
- The Application class must be registered in `AndroidManifest.xml`
|
|
107
|
+
|
|
108
|
+
### User Identification (AuthViewModel.kt)
|
|
109
|
+
|
|
110
|
+
Users are identified when they log in:
|
|
111
|
+
|
|
112
|
+
```kotlin
|
|
113
|
+
fun login(username: String) {
|
|
114
|
+
Amplitude.getInstance().setUserId(username)
|
|
115
|
+
Amplitude.getInstance().track("User Logged In")
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Key Points:**
|
|
120
|
+
- `setUserId()` is called once when the user logs in or signs up
|
|
121
|
+
- Events are tracked using `track()` with event names and properties
|
|
122
|
+
|
|
123
|
+
### Event Tracking (AuthViewModel.kt)
|
|
124
|
+
|
|
125
|
+
Custom events are tracked throughout the app:
|
|
126
|
+
|
|
127
|
+
```kotlin
|
|
128
|
+
val eventProperties = mapOf(
|
|
129
|
+
"total_considerations" to updatedUser.burritoConsiderations,
|
|
130
|
+
"username" to updatedUser.username
|
|
131
|
+
)
|
|
132
|
+
Amplitude.getInstance().track("Burrito Considered", eventProperties)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Key Points:**
|
|
136
|
+
- Events are tracked with `track()` method
|
|
137
|
+
- Event properties provide context about the event
|
|
138
|
+
- Properties can be strings, numbers, booleans, or dates
|
|
139
|
+
|
|
140
|
+
## Gradle Configuration
|
|
141
|
+
|
|
142
|
+
### App-level build.gradle
|
|
143
|
+
|
|
144
|
+
```gradle
|
|
145
|
+
android {
|
|
146
|
+
defaultConfig {
|
|
147
|
+
// Amplitude configuration
|
|
148
|
+
buildConfigField "String", "AMPLITUDE_API_KEY", "\"${project.findProperty("amplitude.apiKey") ?: ""}\""
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
dependencies {
|
|
153
|
+
// Amplitude Android SDK
|
|
154
|
+
implementation 'com.amplitude:analytics-android:1.+'
|
|
155
|
+
|
|
156
|
+
// Other dependencies...
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Best Practices
|
|
161
|
+
|
|
162
|
+
1. **Initialize Early**: Initialize Amplitude in your `Application.onCreate()` method
|
|
163
|
+
2. **Identify Once**: Call `setUserId()` once when the user logs in or signs up
|
|
164
|
+
3. **Use Meaningful Event Names**: Use clear, descriptive event names (e.g., `User Logged In` instead of `login`)
|
|
165
|
+
4. **Include Context**: Add relevant properties to events for better analysis
|
|
166
|
+
5. **Test in Development**: Use a separate Amplitude project for development/testing
|
|
167
|
+
6. **Respect Privacy**: Be mindful of PII (Personally Identifiable Information) in events and properties
|
|
168
|
+
|
|
169
|
+
## Learn More
|
|
170
|
+
|
|
171
|
+
- [Amplitude Documentation](https://www.docs.developers.amplitude.com/)
|
|
172
|
+
- [Android Documentation](https://developer.android.com)
|
|
173
|
+
- [Amplitude Android SDK](https://github.com/amplitude/Amplitude-Kotlin)
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## app/proguard-rules.pro
|
|
178
|
+
|
|
179
|
+
```pro
|
|
180
|
+
# Add project specific ProGuard rules here.
|
|
181
|
+
# You can control the set of applied configuration files using the
|
|
182
|
+
# proguardFiles setting in build.gradle.
|
|
183
|
+
#
|
|
184
|
+
# For more details, see
|
|
185
|
+
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
186
|
+
|
|
187
|
+
# If your project uses WebView with JS, uncomment the following
|
|
188
|
+
# and specify the fully qualified class name to the JavaScript interface
|
|
189
|
+
# class:
|
|
190
|
+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
191
|
+
# public *;
|
|
192
|
+
#}
|
|
193
|
+
|
|
194
|
+
# Uncomment this to preserve the line number information for
|
|
195
|
+
# debugging stack traces.
|
|
196
|
+
#-keepattributes SourceFile,LineNumberTable
|
|
197
|
+
|
|
198
|
+
# If you keep the line number information, uncomment this to
|
|
199
|
+
# hide the original source file name.
|
|
200
|
+
#-renamesourcefileattribute SourceFile
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## app/src/main/AndroidManifest.xml
|
|
206
|
+
|
|
207
|
+
```xml
|
|
208
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
209
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
210
|
+
xmlns:tools="http://schemas.android.com/tools">
|
|
211
|
+
|
|
212
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
213
|
+
|
|
214
|
+
<application
|
|
215
|
+
android:name=".BurritoApplication"
|
|
216
|
+
android:allowBackup="true"
|
|
217
|
+
android:dataExtractionRules="@xml/data_extraction_rules"
|
|
218
|
+
android:fullBackupContent="@xml/backup_rules"
|
|
219
|
+
android:icon="@mipmap/ic_launcher"
|
|
220
|
+
android:label="@string/app_name"
|
|
221
|
+
android:roundIcon="@mipmap/ic_launcher_round"
|
|
222
|
+
android:supportsRtl="true"
|
|
223
|
+
android:theme="@style/Theme.Amplitude">
|
|
224
|
+
<!-- We have to export the MainActivity to be able to start it from the system launcher.
|
|
225
|
+
Ignores added for semgrep. -->
|
|
226
|
+
<activity
|
|
227
|
+
android:name=".MainActivity"
|
|
228
|
+
android:exported="true"
|
|
229
|
+
android:label="@string/app_name"
|
|
230
|
+
android:theme="@style/Theme.Amplitude"
|
|
231
|
+
tools:ignore="ExportedActivity">
|
|
232
|
+
<intent-filter>
|
|
233
|
+
<action android:name="android.intent.action.MAIN" />
|
|
234
|
+
|
|
235
|
+
<category android:name="android.intent.category.LAUNCHER" />
|
|
236
|
+
</intent-filter>
|
|
237
|
+
</activity>
|
|
238
|
+
</application>
|
|
239
|
+
|
|
240
|
+
</manifest>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## app/src/main/java/com/example/amplitude/BurritoApp.kt
|
|
246
|
+
|
|
247
|
+
```kt
|
|
248
|
+
package com.example.amplitude
|
|
249
|
+
|
|
250
|
+
import android.app.Application
|
|
251
|
+
import com.amplitude.android.Amplitude
|
|
252
|
+
import com.amplitude.android.Configuration
|
|
253
|
+
|
|
254
|
+
class BurritoApplication : Application() {
|
|
255
|
+
lateinit var amplitude: Amplitude
|
|
256
|
+
|
|
257
|
+
override fun onCreate() {
|
|
258
|
+
super.onCreate()
|
|
259
|
+
|
|
260
|
+
// Initialize Amplitude early in Application lifecycle
|
|
261
|
+
amplitude = Amplitude(
|
|
262
|
+
Configuration(
|
|
263
|
+
apiKey = BuildConfig.AMPLITUDE_API_KEY,
|
|
264
|
+
context = applicationContext
|
|
265
|
+
)
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## app/src/main/java/com/example/amplitude/data/User.kt
|
|
275
|
+
|
|
276
|
+
```kt
|
|
277
|
+
package com.example.amplitude.data
|
|
278
|
+
|
|
279
|
+
data class User(
|
|
280
|
+
val username: String,
|
|
281
|
+
val burritoConsiderations: Int = 0
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## app/src/main/java/com/example/amplitude/data/UserRepository.kt
|
|
289
|
+
|
|
290
|
+
```kt
|
|
291
|
+
package com.example.amplitude.data
|
|
292
|
+
|
|
293
|
+
import android.content.Context
|
|
294
|
+
import android.content.SharedPreferences
|
|
295
|
+
import org.json.JSONObject
|
|
296
|
+
|
|
297
|
+
class UserRepository(context: Context) {
|
|
298
|
+
|
|
299
|
+
private val prefs: SharedPreferences = context.getSharedPreferences(
|
|
300
|
+
PREFS_NAME, Context.MODE_PRIVATE
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
companion object {
|
|
304
|
+
private const val PREFS_NAME = "burrito_app_prefs"
|
|
305
|
+
private const val KEY_CURRENT_USERNAME = "current_username"
|
|
306
|
+
private const val KEY_USER_DATA_PREFIX = "user_data_"
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
fun getCurrentUsername(): String? {
|
|
310
|
+
return prefs.getString(KEY_CURRENT_USERNAME, null)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
fun getUser(username: String): User? {
|
|
314
|
+
val json = prefs.getString("$KEY_USER_DATA_PREFIX$username", null) ?: return null
|
|
315
|
+
return try {
|
|
316
|
+
val obj = JSONObject(json)
|
|
317
|
+
User(
|
|
318
|
+
username = obj.getString("username"),
|
|
319
|
+
burritoConsiderations = obj.getInt("burritoConsiderations")
|
|
320
|
+
)
|
|
321
|
+
} catch (e: Exception) {
|
|
322
|
+
null
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
fun saveUser(user: User) {
|
|
327
|
+
val json = JSONObject().apply {
|
|
328
|
+
put("username", user.username)
|
|
329
|
+
put("burritoConsiderations", user.burritoConsiderations)
|
|
330
|
+
}.toString()
|
|
331
|
+
|
|
332
|
+
prefs.edit()
|
|
333
|
+
.putString("$KEY_USER_DATA_PREFIX${user.username}", json)
|
|
334
|
+
.putString(KEY_CURRENT_USERNAME, user.username)
|
|
335
|
+
.apply()
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
fun clearCurrentUser() {
|
|
339
|
+
prefs.edit()
|
|
340
|
+
.remove(KEY_CURRENT_USERNAME)
|
|
341
|
+
.apply()
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
fun getCurrentUser(): User? {
|
|
345
|
+
val username = getCurrentUsername() ?: return null
|
|
346
|
+
return getUser(username)
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## app/src/main/java/com/example/amplitude/MainActivity.kt
|
|
355
|
+
|
|
356
|
+
```kt
|
|
357
|
+
package com.example.amplitude
|
|
358
|
+
|
|
359
|
+
import android.os.Bundle
|
|
360
|
+
import androidx.activity.ComponentActivity
|
|
361
|
+
import androidx.activity.compose.setContent
|
|
362
|
+
import androidx.activity.enableEdgeToEdge
|
|
363
|
+
import androidx.compose.foundation.background
|
|
364
|
+
import androidx.compose.foundation.layout.Box
|
|
365
|
+
import androidx.compose.foundation.layout.fillMaxSize
|
|
366
|
+
import androidx.compose.foundation.layout.padding
|
|
367
|
+
import androidx.compose.material3.Scaffold
|
|
368
|
+
import androidx.compose.runtime.Composable
|
|
369
|
+
import androidx.compose.runtime.collectAsState
|
|
370
|
+
import androidx.compose.runtime.getValue
|
|
371
|
+
import androidx.compose.ui.Modifier
|
|
372
|
+
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
373
|
+
import androidx.navigation.compose.currentBackStackEntryAsState
|
|
374
|
+
import androidx.navigation.compose.rememberNavController
|
|
375
|
+
import com.example.amplitude.navigation.NavGraph
|
|
376
|
+
import com.example.amplitude.navigation.Screen
|
|
377
|
+
import com.example.amplitude.ui.components.AppHeader
|
|
378
|
+
import com.example.amplitude.ui.components.BottomNavBar
|
|
379
|
+
import com.example.amplitude.ui.theme.BackgroundGray
|
|
380
|
+
import com.example.amplitude.ui.theme.AmplitudeTheme
|
|
381
|
+
import com.example.amplitude.viewmodel.AuthViewModel
|
|
382
|
+
|
|
383
|
+
class MainActivity : ComponentActivity() {
|
|
384
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
385
|
+
super.onCreate(savedInstanceState)
|
|
386
|
+
enableEdgeToEdge()
|
|
387
|
+
setContent {
|
|
388
|
+
AmplitudeTheme {
|
|
389
|
+
BurritoApp()
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
@Composable
|
|
396
|
+
fun BurritoApp() {
|
|
397
|
+
val navController = rememberNavController()
|
|
398
|
+
val viewModel: AuthViewModel = viewModel()
|
|
399
|
+
|
|
400
|
+
val isAuthenticated by viewModel.isAuthenticated.collectAsState()
|
|
401
|
+
val currentUser by viewModel.currentUser.collectAsState()
|
|
402
|
+
|
|
403
|
+
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
|
404
|
+
val currentRoute = navBackStackEntry?.destination?.route
|
|
405
|
+
|
|
406
|
+
Scaffold(
|
|
407
|
+
modifier = Modifier.fillMaxSize(),
|
|
408
|
+
topBar = {
|
|
409
|
+
AppHeader(
|
|
410
|
+
isAuthenticated = isAuthenticated,
|
|
411
|
+
username = currentUser?.username,
|
|
412
|
+
currentRoute = currentRoute,
|
|
413
|
+
onNavigate = { route ->
|
|
414
|
+
navController.navigate(route) {
|
|
415
|
+
popUpTo(Screen.Home.route)
|
|
416
|
+
launchSingleTop = true
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
onLogout = {
|
|
420
|
+
viewModel.logout()
|
|
421
|
+
navController.navigate(Screen.Home.route) {
|
|
422
|
+
popUpTo(Screen.Home.route) { inclusive = true }
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
)
|
|
426
|
+
},
|
|
427
|
+
bottomBar = {
|
|
428
|
+
BottomNavBar(
|
|
429
|
+
isAuthenticated = isAuthenticated,
|
|
430
|
+
currentRoute = currentRoute,
|
|
431
|
+
onNavigate = { route ->
|
|
432
|
+
navController.navigate(route) {
|
|
433
|
+
popUpTo(Screen.Home.route)
|
|
434
|
+
launchSingleTop = true
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
)
|
|
438
|
+
},
|
|
439
|
+
containerColor = BackgroundGray
|
|
440
|
+
) { innerPadding ->
|
|
441
|
+
Box(
|
|
442
|
+
modifier = Modifier
|
|
443
|
+
.fillMaxSize()
|
|
444
|
+
.background(BackgroundGray)
|
|
445
|
+
.padding(innerPadding)
|
|
446
|
+
) {
|
|
447
|
+
NavGraph(
|
|
448
|
+
navController = navController,
|
|
449
|
+
viewModel = viewModel
|
|
450
|
+
)
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## app/src/main/java/com/example/amplitude/navigation/NavGraph.kt
|
|
460
|
+
|
|
461
|
+
```kt
|
|
462
|
+
package com.example.amplitude.navigation
|
|
463
|
+
|
|
464
|
+
import androidx.compose.runtime.Composable
|
|
465
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
466
|
+
import androidx.compose.runtime.collectAsState
|
|
467
|
+
import androidx.compose.runtime.getValue
|
|
468
|
+
import androidx.navigation.NavHostController
|
|
469
|
+
import androidx.navigation.compose.NavHost
|
|
470
|
+
import androidx.navigation.compose.composable
|
|
471
|
+
import com.example.amplitude.ui.screens.BurritoScreen
|
|
472
|
+
import com.example.amplitude.ui.screens.HomeScreen
|
|
473
|
+
import com.example.amplitude.ui.screens.ProfileScreen
|
|
474
|
+
import com.example.amplitude.viewmodel.AuthViewModel
|
|
475
|
+
|
|
476
|
+
sealed class Screen(val route: String) {
|
|
477
|
+
object Home : Screen("home")
|
|
478
|
+
object Burrito : Screen("burrito")
|
|
479
|
+
object Profile : Screen("profile")
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
@Composable
|
|
483
|
+
fun NavGraph(
|
|
484
|
+
navController: NavHostController,
|
|
485
|
+
viewModel: AuthViewModel
|
|
486
|
+
) {
|
|
487
|
+
val isAuthenticated by viewModel.isAuthenticated.collectAsState()
|
|
488
|
+
val currentUser by viewModel.currentUser.collectAsState()
|
|
489
|
+
|
|
490
|
+
NavHost(
|
|
491
|
+
navController = navController,
|
|
492
|
+
startDestination = Screen.Home.route
|
|
493
|
+
) {
|
|
494
|
+
composable(Screen.Home.route) {
|
|
495
|
+
HomeScreen(
|
|
496
|
+
isAuthenticated = isAuthenticated,
|
|
497
|
+
username = currentUser?.username,
|
|
498
|
+
onLogin = { username -> viewModel.login(username) }
|
|
499
|
+
)
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
composable(Screen.Burrito.route) {
|
|
503
|
+
if (!isAuthenticated) {
|
|
504
|
+
LaunchedEffect(Unit) {
|
|
505
|
+
navController.navigate(Screen.Home.route) {
|
|
506
|
+
popUpTo(Screen.Home.route) { inclusive = true }
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
} else {
|
|
510
|
+
BurritoScreen(
|
|
511
|
+
burritoCount = currentUser?.burritoConsiderations ?: 0,
|
|
512
|
+
onConsiderBurrito = { viewModel.incrementBurritoCount() }
|
|
513
|
+
)
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
composable(Screen.Profile.route) {
|
|
518
|
+
if (!isAuthenticated) {
|
|
519
|
+
LaunchedEffect(Unit) {
|
|
520
|
+
navController.navigate(Screen.Home.route) {
|
|
521
|
+
popUpTo(Screen.Home.route) { inclusive = true }
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
} else {
|
|
525
|
+
ProfileScreen(
|
|
526
|
+
username = currentUser?.username ?: "",
|
|
527
|
+
burritoCount = currentUser?.burritoConsiderations ?: 0
|
|
528
|
+
)
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
## app/src/main/java/com/example/amplitude/ui/components/AppHeader.kt
|
|
539
|
+
|
|
540
|
+
```kt
|
|
541
|
+
package com.example.amplitude.ui.components
|
|
542
|
+
|
|
543
|
+
import androidx.compose.foundation.background
|
|
544
|
+
import androidx.compose.foundation.layout.Arrangement
|
|
545
|
+
import androidx.compose.foundation.layout.Box
|
|
546
|
+
import androidx.compose.foundation.layout.PaddingValues
|
|
547
|
+
import androidx.compose.foundation.layout.Row
|
|
548
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
549
|
+
import androidx.compose.foundation.layout.padding
|
|
550
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
551
|
+
import androidx.compose.material3.Button
|
|
552
|
+
import androidx.compose.material3.ButtonDefaults
|
|
553
|
+
import androidx.compose.material3.Text
|
|
554
|
+
import androidx.compose.runtime.Composable
|
|
555
|
+
import androidx.compose.ui.Alignment
|
|
556
|
+
import androidx.compose.ui.Modifier
|
|
557
|
+
import androidx.compose.ui.unit.dp
|
|
558
|
+
import androidx.compose.ui.unit.sp
|
|
559
|
+
import com.example.amplitude.ui.theme.DarkHeader
|
|
560
|
+
import com.example.amplitude.ui.theme.ErrorRed
|
|
561
|
+
import com.example.amplitude.ui.theme.White
|
|
562
|
+
|
|
563
|
+
@Composable
|
|
564
|
+
fun AppHeader(
|
|
565
|
+
isAuthenticated: Boolean,
|
|
566
|
+
username: String?,
|
|
567
|
+
currentRoute: String?,
|
|
568
|
+
onNavigate: (String) -> Unit,
|
|
569
|
+
onLogout: () -> Unit
|
|
570
|
+
) {
|
|
571
|
+
Box(
|
|
572
|
+
modifier = Modifier
|
|
573
|
+
.fillMaxWidth()
|
|
574
|
+
.background(DarkHeader)
|
|
575
|
+
.padding(horizontal = 16.dp, vertical = 12.dp)
|
|
576
|
+
) {
|
|
577
|
+
Row(
|
|
578
|
+
modifier = Modifier.fillMaxWidth(),
|
|
579
|
+
horizontalArrangement = Arrangement.SpaceBetween,
|
|
580
|
+
verticalAlignment = Alignment.CenterVertically
|
|
581
|
+
) {
|
|
582
|
+
// App title
|
|
583
|
+
Text(
|
|
584
|
+
text = "Burrito App",
|
|
585
|
+
color = White,
|
|
586
|
+
fontSize = 18.sp
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
// User section (right side)
|
|
590
|
+
if (isAuthenticated && username != null) {
|
|
591
|
+
Row(
|
|
592
|
+
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
|
593
|
+
verticalAlignment = Alignment.CenterVertically
|
|
594
|
+
) {
|
|
595
|
+
Text(
|
|
596
|
+
text = username,
|
|
597
|
+
color = White,
|
|
598
|
+
fontSize = 14.sp
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
Button(
|
|
602
|
+
onClick = onLogout,
|
|
603
|
+
colors = ButtonDefaults.buttonColors(
|
|
604
|
+
containerColor = ErrorRed
|
|
605
|
+
),
|
|
606
|
+
shape = RoundedCornerShape(4.dp),
|
|
607
|
+
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 6.dp)
|
|
608
|
+
) {
|
|
609
|
+
Text(
|
|
610
|
+
text = "Logout",
|
|
611
|
+
color = White,
|
|
612
|
+
fontSize = 14.sp
|
|
613
|
+
)
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
---
|
|
624
|
+
|
|
625
|
+
## app/src/main/java/com/example/amplitude/ui/components/BottomNavBar.kt
|
|
626
|
+
|
|
627
|
+
```kt
|
|
628
|
+
package com.example.amplitude.ui.components
|
|
629
|
+
|
|
630
|
+
import androidx.compose.foundation.layout.size
|
|
631
|
+
import androidx.compose.material.icons.Icons
|
|
632
|
+
import androidx.compose.material.icons.filled.Home
|
|
633
|
+
import androidx.compose.material.icons.filled.Person
|
|
634
|
+
import androidx.compose.material.icons.outlined.Home
|
|
635
|
+
import androidx.compose.material.icons.outlined.Person
|
|
636
|
+
import androidx.compose.material3.Icon
|
|
637
|
+
import androidx.compose.material3.NavigationBar
|
|
638
|
+
import androidx.compose.material3.NavigationBarItem
|
|
639
|
+
import androidx.compose.material3.NavigationBarItemDefaults
|
|
640
|
+
import androidx.compose.material3.Text
|
|
641
|
+
import androidx.compose.runtime.Composable
|
|
642
|
+
import androidx.compose.ui.Modifier
|
|
643
|
+
import androidx.compose.ui.graphics.vector.ImageVector
|
|
644
|
+
import androidx.compose.ui.unit.dp
|
|
645
|
+
import androidx.compose.ui.unit.sp
|
|
646
|
+
import com.example.amplitude.navigation.Screen
|
|
647
|
+
import com.example.amplitude.ui.theme.PrimaryBlue
|
|
648
|
+
import com.example.amplitude.ui.theme.TextGray
|
|
649
|
+
import com.example.amplitude.ui.theme.White
|
|
650
|
+
|
|
651
|
+
sealed class BottomNavItem(
|
|
652
|
+
val route: String,
|
|
653
|
+
val label: String,
|
|
654
|
+
val selectedIcon: ImageVector?,
|
|
655
|
+
val unselectedIcon: ImageVector?
|
|
656
|
+
) {
|
|
657
|
+
object Home : BottomNavItem(
|
|
658
|
+
route = Screen.Home.route,
|
|
659
|
+
label = "Home",
|
|
660
|
+
selectedIcon = Icons.Filled.Home,
|
|
661
|
+
unselectedIcon = Icons.Outlined.Home
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
object Burrito : BottomNavItem(
|
|
665
|
+
route = Screen.Burrito.route,
|
|
666
|
+
label = "Burrito",
|
|
667
|
+
selectedIcon = null, // We'll use a custom icon or emoji
|
|
668
|
+
unselectedIcon = null
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
object Profile : BottomNavItem(
|
|
672
|
+
route = Screen.Profile.route,
|
|
673
|
+
label = "Profile",
|
|
674
|
+
selectedIcon = Icons.Filled.Person,
|
|
675
|
+
unselectedIcon = Icons.Outlined.Person
|
|
676
|
+
)
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
@Composable
|
|
680
|
+
fun BottomNavBar(
|
|
681
|
+
isAuthenticated: Boolean,
|
|
682
|
+
currentRoute: String?,
|
|
683
|
+
onNavigate: (String) -> Unit
|
|
684
|
+
) {
|
|
685
|
+
val items = if (isAuthenticated) {
|
|
686
|
+
listOf(BottomNavItem.Home, BottomNavItem.Burrito, BottomNavItem.Profile)
|
|
687
|
+
} else {
|
|
688
|
+
listOf(BottomNavItem.Home)
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
NavigationBar(
|
|
692
|
+
containerColor = White
|
|
693
|
+
) {
|
|
694
|
+
items.forEach { item ->
|
|
695
|
+
val selected = currentRoute == item.route
|
|
696
|
+
|
|
697
|
+
NavigationBarItem(
|
|
698
|
+
selected = selected,
|
|
699
|
+
onClick = { onNavigate(item.route) },
|
|
700
|
+
icon = {
|
|
701
|
+
if (item.selectedIcon != null && item.unselectedIcon != null) {
|
|
702
|
+
Icon(
|
|
703
|
+
imageVector = if (selected) item.selectedIcon else item.unselectedIcon,
|
|
704
|
+
contentDescription = item.label,
|
|
705
|
+
modifier = Modifier.size(24.dp)
|
|
706
|
+
)
|
|
707
|
+
} else {
|
|
708
|
+
// For Burrito, use text emoji as icon
|
|
709
|
+
Text(
|
|
710
|
+
text = "🌯",
|
|
711
|
+
fontSize = 24.sp
|
|
712
|
+
)
|
|
713
|
+
}
|
|
714
|
+
},
|
|
715
|
+
label = {
|
|
716
|
+
Text(
|
|
717
|
+
text = item.label,
|
|
718
|
+
fontSize = 12.sp
|
|
719
|
+
)
|
|
720
|
+
},
|
|
721
|
+
colors = NavigationBarItemDefaults.colors(
|
|
722
|
+
selectedIconColor = PrimaryBlue,
|
|
723
|
+
selectedTextColor = PrimaryBlue,
|
|
724
|
+
unselectedIconColor = TextGray,
|
|
725
|
+
unselectedTextColor = TextGray,
|
|
726
|
+
indicatorColor = PrimaryBlue.copy(alpha = 0.1f)
|
|
727
|
+
)
|
|
728
|
+
)
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
---
|
|
736
|
+
|
|
737
|
+
## app/src/main/java/com/example/amplitude/ui/components/StatsCard.kt
|
|
738
|
+
|
|
739
|
+
```kt
|
|
740
|
+
package com.example.amplitude.ui.components
|
|
741
|
+
|
|
742
|
+
import androidx.compose.foundation.background
|
|
743
|
+
import androidx.compose.foundation.layout.Column
|
|
744
|
+
import androidx.compose.foundation.layout.Spacer
|
|
745
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
746
|
+
import androidx.compose.foundation.layout.height
|
|
747
|
+
import androidx.compose.foundation.layout.padding
|
|
748
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
749
|
+
import androidx.compose.material3.Text
|
|
750
|
+
import androidx.compose.runtime.Composable
|
|
751
|
+
import androidx.compose.ui.Modifier
|
|
752
|
+
import androidx.compose.ui.text.font.FontWeight
|
|
753
|
+
import androidx.compose.ui.unit.dp
|
|
754
|
+
import androidx.compose.ui.unit.sp
|
|
755
|
+
import com.example.amplitude.ui.theme.LightGray
|
|
756
|
+
import com.example.amplitude.ui.theme.TextDark
|
|
757
|
+
import com.example.amplitude.ui.theme.TextGray
|
|
758
|
+
|
|
759
|
+
@Composable
|
|
760
|
+
fun StatsCard(
|
|
761
|
+
title: String,
|
|
762
|
+
value: String,
|
|
763
|
+
modifier: Modifier = Modifier
|
|
764
|
+
) {
|
|
765
|
+
Column(
|
|
766
|
+
modifier = modifier
|
|
767
|
+
.fillMaxWidth()
|
|
768
|
+
.background(
|
|
769
|
+
color = LightGray,
|
|
770
|
+
shape = RoundedCornerShape(4.dp)
|
|
771
|
+
)
|
|
772
|
+
.padding(16.dp)
|
|
773
|
+
) {
|
|
774
|
+
Text(
|
|
775
|
+
text = title,
|
|
776
|
+
color = TextGray,
|
|
777
|
+
fontSize = 14.sp
|
|
778
|
+
)
|
|
779
|
+
Spacer(modifier = Modifier.height(4.dp))
|
|
780
|
+
Text(
|
|
781
|
+
text = value,
|
|
782
|
+
color = TextDark,
|
|
783
|
+
fontSize = 24.sp,
|
|
784
|
+
fontWeight = FontWeight.Bold
|
|
785
|
+
)
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
---
|
|
792
|
+
|
|
793
|
+
## app/src/main/java/com/example/amplitude/ui/screens/BurritoScreen.kt
|
|
794
|
+
|
|
795
|
+
```kt
|
|
796
|
+
package com.example.amplitude.ui.screens
|
|
797
|
+
|
|
798
|
+
import androidx.compose.foundation.background
|
|
799
|
+
import androidx.compose.foundation.layout.Box
|
|
800
|
+
import androidx.compose.foundation.layout.Column
|
|
801
|
+
import androidx.compose.foundation.layout.Spacer
|
|
802
|
+
import androidx.compose.foundation.layout.fillMaxSize
|
|
803
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
804
|
+
import androidx.compose.foundation.layout.height
|
|
805
|
+
import androidx.compose.foundation.layout.padding
|
|
806
|
+
import androidx.compose.foundation.layout.widthIn
|
|
807
|
+
import androidx.compose.foundation.rememberScrollState
|
|
808
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
809
|
+
import androidx.compose.foundation.verticalScroll
|
|
810
|
+
import androidx.compose.material3.Button
|
|
811
|
+
import androidx.compose.material3.ButtonDefaults
|
|
812
|
+
import androidx.compose.material3.Text
|
|
813
|
+
import androidx.compose.runtime.Composable
|
|
814
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
815
|
+
import androidx.compose.runtime.getValue
|
|
816
|
+
import androidx.compose.runtime.mutableStateOf
|
|
817
|
+
import androidx.compose.runtime.remember
|
|
818
|
+
import androidx.compose.runtime.setValue
|
|
819
|
+
import androidx.compose.ui.Alignment
|
|
820
|
+
import androidx.compose.ui.Modifier
|
|
821
|
+
import androidx.compose.ui.draw.shadow
|
|
822
|
+
import androidx.compose.ui.text.font.FontWeight
|
|
823
|
+
import androidx.compose.ui.text.style.TextAlign
|
|
824
|
+
import androidx.compose.ui.unit.dp
|
|
825
|
+
import androidx.compose.ui.unit.sp
|
|
826
|
+
import com.example.amplitude.ui.components.StatsCard
|
|
827
|
+
import com.example.amplitude.ui.theme.BackgroundGray
|
|
828
|
+
import com.example.amplitude.ui.theme.SuccessGreen
|
|
829
|
+
import com.example.amplitude.ui.theme.TextDark
|
|
830
|
+
import com.example.amplitude.ui.theme.TextGray
|
|
831
|
+
import com.example.amplitude.ui.theme.White
|
|
832
|
+
import kotlinx.coroutines.delay
|
|
833
|
+
|
|
834
|
+
@Composable
|
|
835
|
+
fun BurritoScreen(
|
|
836
|
+
burritoCount: Int,
|
|
837
|
+
onConsiderBurrito: () -> Unit
|
|
838
|
+
) {
|
|
839
|
+
var showSuccess by remember { mutableStateOf(false) }
|
|
840
|
+
|
|
841
|
+
LaunchedEffect(showSuccess) {
|
|
842
|
+
if (showSuccess) {
|
|
843
|
+
delay(2000)
|
|
844
|
+
showSuccess = false
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
Box(
|
|
849
|
+
modifier = Modifier
|
|
850
|
+
.fillMaxSize()
|
|
851
|
+
.background(BackgroundGray)
|
|
852
|
+
.padding(horizontal = 16.dp)
|
|
853
|
+
.verticalScroll(rememberScrollState()),
|
|
854
|
+
contentAlignment = Alignment.TopCenter
|
|
855
|
+
) {
|
|
856
|
+
Column(
|
|
857
|
+
modifier = Modifier
|
|
858
|
+
.widthIn(max = 600.dp)
|
|
859
|
+
.padding(vertical = 16.dp),
|
|
860
|
+
horizontalAlignment = Alignment.CenterHorizontally
|
|
861
|
+
) {
|
|
862
|
+
// Main content card
|
|
863
|
+
Column(
|
|
864
|
+
modifier = Modifier
|
|
865
|
+
.fillMaxWidth()
|
|
866
|
+
.shadow(
|
|
867
|
+
elevation = 4.dp,
|
|
868
|
+
shape = RoundedCornerShape(8.dp),
|
|
869
|
+
ambientColor = TextDark.copy(alpha = 0.1f),
|
|
870
|
+
spotColor = TextDark.copy(alpha = 0.1f)
|
|
871
|
+
)
|
|
872
|
+
.background(
|
|
873
|
+
color = White,
|
|
874
|
+
shape = RoundedCornerShape(8.dp)
|
|
875
|
+
)
|
|
876
|
+
.padding(24.dp),
|
|
877
|
+
horizontalAlignment = Alignment.CenterHorizontally
|
|
878
|
+
) {
|
|
879
|
+
Text(
|
|
880
|
+
text = "Burrito consideration zone",
|
|
881
|
+
fontSize = 24.sp,
|
|
882
|
+
fontWeight = FontWeight.SemiBold,
|
|
883
|
+
color = TextDark,
|
|
884
|
+
textAlign = TextAlign.Center
|
|
885
|
+
)
|
|
886
|
+
|
|
887
|
+
Spacer(modifier = Modifier.height(16.dp))
|
|
888
|
+
|
|
889
|
+
Text(
|
|
890
|
+
text = "Take a moment to truly consider the burrito.",
|
|
891
|
+
fontSize = 16.sp,
|
|
892
|
+
color = TextGray,
|
|
893
|
+
textAlign = TextAlign.Center,
|
|
894
|
+
lineHeight = 26.sp
|
|
895
|
+
)
|
|
896
|
+
|
|
897
|
+
Spacer(modifier = Modifier.height(24.dp))
|
|
898
|
+
|
|
899
|
+
Button(
|
|
900
|
+
onClick = {
|
|
901
|
+
onConsiderBurrito()
|
|
902
|
+
showSuccess = true
|
|
903
|
+
},
|
|
904
|
+
modifier = Modifier
|
|
905
|
+
.fillMaxWidth()
|
|
906
|
+
.height(56.dp),
|
|
907
|
+
colors = ButtonDefaults.buttonColors(
|
|
908
|
+
containerColor = SuccessGreen
|
|
909
|
+
),
|
|
910
|
+
shape = RoundedCornerShape(4.dp)
|
|
911
|
+
) {
|
|
912
|
+
Text(
|
|
913
|
+
text = "Consider the Burrito",
|
|
914
|
+
fontSize = 18.sp,
|
|
915
|
+
color = White
|
|
916
|
+
)
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
if (showSuccess) {
|
|
920
|
+
Spacer(modifier = Modifier.height(16.dp))
|
|
921
|
+
|
|
922
|
+
Text(
|
|
923
|
+
text = "You have considered the burrito. Well done!",
|
|
924
|
+
color = SuccessGreen,
|
|
925
|
+
fontSize = 16.sp,
|
|
926
|
+
textAlign = TextAlign.Center
|
|
927
|
+
)
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
Spacer(modifier = Modifier.height(24.dp))
|
|
931
|
+
|
|
932
|
+
Text(
|
|
933
|
+
text = "Consideration stats",
|
|
934
|
+
fontSize = 20.sp,
|
|
935
|
+
fontWeight = FontWeight.SemiBold,
|
|
936
|
+
color = TextDark,
|
|
937
|
+
modifier = Modifier.fillMaxWidth()
|
|
938
|
+
)
|
|
939
|
+
|
|
940
|
+
Spacer(modifier = Modifier.height(16.dp))
|
|
941
|
+
|
|
942
|
+
StatsCard(
|
|
943
|
+
title = "Total Burrito Considerations",
|
|
944
|
+
value = burritoCount.toString()
|
|
945
|
+
)
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
---
|
|
954
|
+
|
|
955
|
+
## app/src/main/java/com/example/amplitude/ui/screens/HomeScreen.kt
|
|
956
|
+
|
|
957
|
+
```kt
|
|
958
|
+
package com.example.amplitude.ui.screens
|
|
959
|
+
|
|
960
|
+
import androidx.compose.foundation.background
|
|
961
|
+
import androidx.compose.foundation.layout.Arrangement
|
|
962
|
+
import androidx.compose.foundation.layout.Box
|
|
963
|
+
import androidx.compose.foundation.layout.Column
|
|
964
|
+
import androidx.compose.foundation.layout.Spacer
|
|
965
|
+
import androidx.compose.foundation.layout.fillMaxSize
|
|
966
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
967
|
+
import androidx.compose.foundation.layout.height
|
|
968
|
+
import androidx.compose.foundation.layout.padding
|
|
969
|
+
import androidx.compose.foundation.layout.widthIn
|
|
970
|
+
import androidx.compose.foundation.rememberScrollState
|
|
971
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
972
|
+
import androidx.compose.foundation.text.KeyboardOptions
|
|
973
|
+
import androidx.compose.foundation.verticalScroll
|
|
974
|
+
import androidx.compose.material3.Button
|
|
975
|
+
import androidx.compose.material3.ButtonDefaults
|
|
976
|
+
import androidx.compose.material3.OutlinedTextField
|
|
977
|
+
import androidx.compose.material3.OutlinedTextFieldDefaults
|
|
978
|
+
import androidx.compose.material3.Text
|
|
979
|
+
import androidx.compose.runtime.Composable
|
|
980
|
+
import androidx.compose.runtime.getValue
|
|
981
|
+
import androidx.compose.runtime.mutableStateOf
|
|
982
|
+
import androidx.compose.runtime.remember
|
|
983
|
+
import androidx.compose.runtime.setValue
|
|
984
|
+
import androidx.compose.ui.Alignment
|
|
985
|
+
import androidx.compose.ui.Modifier
|
|
986
|
+
import androidx.compose.ui.draw.shadow
|
|
987
|
+
import androidx.compose.ui.text.font.FontWeight
|
|
988
|
+
import androidx.compose.ui.text.input.KeyboardType
|
|
989
|
+
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
|
990
|
+
import androidx.compose.ui.text.style.TextAlign
|
|
991
|
+
import androidx.compose.ui.unit.dp
|
|
992
|
+
import androidx.compose.ui.unit.sp
|
|
993
|
+
import com.example.amplitude.ui.theme.BackgroundGray
|
|
994
|
+
import com.example.amplitude.ui.theme.BorderGray
|
|
995
|
+
import com.example.amplitude.ui.theme.PrimaryBlue
|
|
996
|
+
import com.example.amplitude.ui.theme.TextDark
|
|
997
|
+
import com.example.amplitude.ui.theme.TextGray
|
|
998
|
+
import com.example.amplitude.ui.theme.White
|
|
999
|
+
|
|
1000
|
+
@Composable
|
|
1001
|
+
fun HomeScreen(
|
|
1002
|
+
isAuthenticated: Boolean,
|
|
1003
|
+
username: String?,
|
|
1004
|
+
onLogin: (String) -> Unit
|
|
1005
|
+
) {
|
|
1006
|
+
Box(
|
|
1007
|
+
modifier = Modifier
|
|
1008
|
+
.fillMaxSize()
|
|
1009
|
+
.background(BackgroundGray)
|
|
1010
|
+
.padding(horizontal = 16.dp)
|
|
1011
|
+
.verticalScroll(rememberScrollState()),
|
|
1012
|
+
contentAlignment = if (isAuthenticated) Alignment.TopCenter else Alignment.Center
|
|
1013
|
+
) {
|
|
1014
|
+
Column(
|
|
1015
|
+
modifier = Modifier
|
|
1016
|
+
.widthIn(max = 600.dp)
|
|
1017
|
+
.padding(vertical = 16.dp),
|
|
1018
|
+
horizontalAlignment = Alignment.CenterHorizontally
|
|
1019
|
+
) {
|
|
1020
|
+
if (isAuthenticated && username != null) {
|
|
1021
|
+
LoggedInContent(username = username)
|
|
1022
|
+
} else {
|
|
1023
|
+
LoginForm(onLogin = onLogin)
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
@Composable
|
|
1030
|
+
private fun LoggedInContent(username: String) {
|
|
1031
|
+
ContentCard {
|
|
1032
|
+
Text(
|
|
1033
|
+
text = "Welcome back, $username!",
|
|
1034
|
+
fontSize = 24.sp,
|
|
1035
|
+
fontWeight = FontWeight.SemiBold,
|
|
1036
|
+
color = TextDark
|
|
1037
|
+
)
|
|
1038
|
+
|
|
1039
|
+
Spacer(modifier = Modifier.height(16.dp))
|
|
1040
|
+
|
|
1041
|
+
Text(
|
|
1042
|
+
text = "Ready to consider some burritos?",
|
|
1043
|
+
fontSize = 16.sp,
|
|
1044
|
+
color = TextGray,
|
|
1045
|
+
lineHeight = 26.sp
|
|
1046
|
+
)
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
@Composable
|
|
1051
|
+
private fun LoginForm(onLogin: (String) -> Unit) {
|
|
1052
|
+
var username by remember { mutableStateOf("") }
|
|
1053
|
+
var password by remember { mutableStateOf("") }
|
|
1054
|
+
|
|
1055
|
+
ContentCard {
|
|
1056
|
+
Text(
|
|
1057
|
+
text = "Welcome to Burrito Consideration App",
|
|
1058
|
+
fontSize = 24.sp,
|
|
1059
|
+
fontWeight = FontWeight.SemiBold,
|
|
1060
|
+
color = TextDark,
|
|
1061
|
+
textAlign = TextAlign.Center
|
|
1062
|
+
)
|
|
1063
|
+
|
|
1064
|
+
Spacer(modifier = Modifier.height(24.dp))
|
|
1065
|
+
|
|
1066
|
+
// Username field
|
|
1067
|
+
Column(modifier = Modifier.fillMaxWidth()) {
|
|
1068
|
+
Text(
|
|
1069
|
+
text = "Username",
|
|
1070
|
+
fontSize = 16.sp,
|
|
1071
|
+
fontWeight = FontWeight.Medium,
|
|
1072
|
+
color = TextDark,
|
|
1073
|
+
modifier = Modifier.padding(bottom = 8.dp)
|
|
1074
|
+
)
|
|
1075
|
+
OutlinedTextField(
|
|
1076
|
+
value = username,
|
|
1077
|
+
onValueChange = { username = it },
|
|
1078
|
+
modifier = Modifier.fillMaxWidth(),
|
|
1079
|
+
singleLine = true,
|
|
1080
|
+
shape = RoundedCornerShape(4.dp),
|
|
1081
|
+
colors = OutlinedTextFieldDefaults.colors(
|
|
1082
|
+
unfocusedBorderColor = BorderGray,
|
|
1083
|
+
focusedBorderColor = PrimaryBlue,
|
|
1084
|
+
unfocusedContainerColor = White,
|
|
1085
|
+
focusedContainerColor = White
|
|
1086
|
+
)
|
|
1087
|
+
)
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
Spacer(modifier = Modifier.height(16.dp))
|
|
1091
|
+
|
|
1092
|
+
// Password field
|
|
1093
|
+
Column(modifier = Modifier.fillMaxWidth()) {
|
|
1094
|
+
Text(
|
|
1095
|
+
text = "Password",
|
|
1096
|
+
fontSize = 16.sp,
|
|
1097
|
+
fontWeight = FontWeight.Medium,
|
|
1098
|
+
color = TextDark,
|
|
1099
|
+
modifier = Modifier.padding(bottom = 8.dp)
|
|
1100
|
+
)
|
|
1101
|
+
OutlinedTextField(
|
|
1102
|
+
value = password,
|
|
1103
|
+
onValueChange = { password = it },
|
|
1104
|
+
modifier = Modifier.fillMaxWidth(),
|
|
1105
|
+
singleLine = true,
|
|
1106
|
+
visualTransformation = PasswordVisualTransformation(),
|
|
1107
|
+
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
|
|
1108
|
+
shape = RoundedCornerShape(4.dp),
|
|
1109
|
+
colors = OutlinedTextFieldDefaults.colors(
|
|
1110
|
+
unfocusedBorderColor = BorderGray,
|
|
1111
|
+
focusedBorderColor = PrimaryBlue,
|
|
1112
|
+
unfocusedContainerColor = White,
|
|
1113
|
+
focusedContainerColor = White
|
|
1114
|
+
)
|
|
1115
|
+
)
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
Spacer(modifier = Modifier.height(16.dp))
|
|
1119
|
+
|
|
1120
|
+
Button(
|
|
1121
|
+
onClick = {
|
|
1122
|
+
if (username.isNotBlank()) {
|
|
1123
|
+
onLogin(username)
|
|
1124
|
+
}
|
|
1125
|
+
},
|
|
1126
|
+
modifier = Modifier
|
|
1127
|
+
.fillMaxWidth()
|
|
1128
|
+
.height(48.dp),
|
|
1129
|
+
colors = ButtonDefaults.buttonColors(
|
|
1130
|
+
containerColor = PrimaryBlue
|
|
1131
|
+
),
|
|
1132
|
+
shape = RoundedCornerShape(4.dp)
|
|
1133
|
+
) {
|
|
1134
|
+
Text(
|
|
1135
|
+
text = "Sign In",
|
|
1136
|
+
fontSize = 16.sp,
|
|
1137
|
+
color = White
|
|
1138
|
+
)
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
Spacer(modifier = Modifier.height(24.dp))
|
|
1142
|
+
|
|
1143
|
+
Text(
|
|
1144
|
+
text = "Note: This is a demo app. Enter any username to sign in.",
|
|
1145
|
+
fontSize = 14.sp,
|
|
1146
|
+
color = TextGray,
|
|
1147
|
+
textAlign = TextAlign.Center,
|
|
1148
|
+
lineHeight = 21.sp
|
|
1149
|
+
)
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
@Composable
|
|
1154
|
+
private fun ContentCard(
|
|
1155
|
+
content: @Composable () -> Unit
|
|
1156
|
+
) {
|
|
1157
|
+
Column(
|
|
1158
|
+
modifier = Modifier
|
|
1159
|
+
.fillMaxWidth()
|
|
1160
|
+
.shadow(
|
|
1161
|
+
elevation = 4.dp,
|
|
1162
|
+
shape = RoundedCornerShape(8.dp),
|
|
1163
|
+
ambientColor = TextDark.copy(alpha = 0.1f),
|
|
1164
|
+
spotColor = TextDark.copy(alpha = 0.1f)
|
|
1165
|
+
)
|
|
1166
|
+
.background(
|
|
1167
|
+
color = White,
|
|
1168
|
+
shape = RoundedCornerShape(8.dp)
|
|
1169
|
+
)
|
|
1170
|
+
.padding(24.dp),
|
|
1171
|
+
horizontalAlignment = Alignment.CenterHorizontally
|
|
1172
|
+
) {
|
|
1173
|
+
content()
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
```
|
|
1178
|
+
|
|
1179
|
+
---
|
|
1180
|
+
|
|
1181
|
+
## app/src/main/java/com/example/amplitude/ui/screens/ProfileScreen.kt
|
|
1182
|
+
|
|
1183
|
+
```kt
|
|
1184
|
+
package com.example.amplitude.ui.screens
|
|
1185
|
+
|
|
1186
|
+
import androidx.compose.foundation.background
|
|
1187
|
+
import androidx.compose.foundation.layout.Box
|
|
1188
|
+
import androidx.compose.foundation.layout.Column
|
|
1189
|
+
import androidx.compose.foundation.layout.Spacer
|
|
1190
|
+
import androidx.compose.foundation.layout.fillMaxSize
|
|
1191
|
+
import androidx.compose.foundation.layout.fillMaxWidth
|
|
1192
|
+
import androidx.compose.foundation.layout.height
|
|
1193
|
+
import androidx.compose.foundation.layout.padding
|
|
1194
|
+
import androidx.compose.foundation.layout.widthIn
|
|
1195
|
+
import androidx.compose.foundation.rememberScrollState
|
|
1196
|
+
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
1197
|
+
import androidx.compose.foundation.verticalScroll
|
|
1198
|
+
import androidx.compose.material3.Text
|
|
1199
|
+
import androidx.compose.runtime.Composable
|
|
1200
|
+
import androidx.compose.ui.Alignment
|
|
1201
|
+
import androidx.compose.ui.Modifier
|
|
1202
|
+
import androidx.compose.ui.draw.shadow
|
|
1203
|
+
import androidx.compose.ui.text.font.FontWeight
|
|
1204
|
+
import androidx.compose.ui.text.style.TextAlign
|
|
1205
|
+
import androidx.compose.ui.unit.dp
|
|
1206
|
+
import androidx.compose.ui.unit.sp
|
|
1207
|
+
import com.example.amplitude.ui.components.StatsCard
|
|
1208
|
+
import com.example.amplitude.ui.theme.BackgroundGray
|
|
1209
|
+
import com.example.amplitude.ui.theme.TextDark
|
|
1210
|
+
import com.example.amplitude.ui.theme.TextGray
|
|
1211
|
+
import com.example.amplitude.ui.theme.White
|
|
1212
|
+
|
|
1213
|
+
@Composable
|
|
1214
|
+
fun ProfileScreen(
|
|
1215
|
+
username: String,
|
|
1216
|
+
burritoCount: Int
|
|
1217
|
+
) {
|
|
1218
|
+
Box(
|
|
1219
|
+
modifier = Modifier
|
|
1220
|
+
.fillMaxSize()
|
|
1221
|
+
.background(BackgroundGray)
|
|
1222
|
+
.padding(horizontal = 16.dp)
|
|
1223
|
+
.verticalScroll(rememberScrollState()),
|
|
1224
|
+
contentAlignment = Alignment.TopCenter
|
|
1225
|
+
) {
|
|
1226
|
+
Column(
|
|
1227
|
+
modifier = Modifier
|
|
1228
|
+
.widthIn(max = 600.dp)
|
|
1229
|
+
.padding(vertical = 16.dp),
|
|
1230
|
+
horizontalAlignment = Alignment.CenterHorizontally
|
|
1231
|
+
) {
|
|
1232
|
+
// Main content card
|
|
1233
|
+
Column(
|
|
1234
|
+
modifier = Modifier
|
|
1235
|
+
.fillMaxWidth()
|
|
1236
|
+
.shadow(
|
|
1237
|
+
elevation = 4.dp,
|
|
1238
|
+
shape = RoundedCornerShape(8.dp),
|
|
1239
|
+
ambientColor = TextDark.copy(alpha = 0.1f),
|
|
1240
|
+
spotColor = TextDark.copy(alpha = 0.1f)
|
|
1241
|
+
)
|
|
1242
|
+
.background(
|
|
1243
|
+
color = White,
|
|
1244
|
+
shape = RoundedCornerShape(8.dp)
|
|
1245
|
+
)
|
|
1246
|
+
.padding(24.dp),
|
|
1247
|
+
horizontalAlignment = Alignment.CenterHorizontally
|
|
1248
|
+
) {
|
|
1249
|
+
Text(
|
|
1250
|
+
text = "User Profile",
|
|
1251
|
+
fontSize = 24.sp,
|
|
1252
|
+
fontWeight = FontWeight.SemiBold,
|
|
1253
|
+
color = TextDark
|
|
1254
|
+
)
|
|
1255
|
+
|
|
1256
|
+
Spacer(modifier = Modifier.height(24.dp))
|
|
1257
|
+
|
|
1258
|
+
// Your Information section
|
|
1259
|
+
Text(
|
|
1260
|
+
text = "Your Information",
|
|
1261
|
+
fontSize = 24.sp,
|
|
1262
|
+
fontWeight = FontWeight.SemiBold,
|
|
1263
|
+
color = TextDark,
|
|
1264
|
+
modifier = Modifier.fillMaxWidth()
|
|
1265
|
+
)
|
|
1266
|
+
|
|
1267
|
+
Spacer(modifier = Modifier.height(16.dp))
|
|
1268
|
+
|
|
1269
|
+
// Username display
|
|
1270
|
+
Column(modifier = Modifier.fillMaxWidth()) {
|
|
1271
|
+
Text(
|
|
1272
|
+
text = "Username",
|
|
1273
|
+
fontSize = 14.sp,
|
|
1274
|
+
color = TextGray
|
|
1275
|
+
)
|
|
1276
|
+
Text(
|
|
1277
|
+
text = username,
|
|
1278
|
+
fontSize = 20.sp,
|
|
1279
|
+
fontWeight = FontWeight.SemiBold,
|
|
1280
|
+
color = TextDark
|
|
1281
|
+
)
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
Spacer(modifier = Modifier.height(24.dp))
|
|
1285
|
+
|
|
1286
|
+
// Stats card
|
|
1287
|
+
StatsCard(
|
|
1288
|
+
title = "Total Burrito Considerations",
|
|
1289
|
+
value = burritoCount.toString()
|
|
1290
|
+
)
|
|
1291
|
+
|
|
1292
|
+
Spacer(modifier = Modifier.height(24.dp))
|
|
1293
|
+
|
|
1294
|
+
// Your Burrito Journey section
|
|
1295
|
+
Text(
|
|
1296
|
+
text = "Your Burrito Journey",
|
|
1297
|
+
fontSize = 20.sp,
|
|
1298
|
+
fontWeight = FontWeight.SemiBold,
|
|
1299
|
+
color = TextDark,
|
|
1300
|
+
modifier = Modifier.fillMaxWidth()
|
|
1301
|
+
)
|
|
1302
|
+
|
|
1303
|
+
Spacer(modifier = Modifier.height(8.dp))
|
|
1304
|
+
|
|
1305
|
+
Text(
|
|
1306
|
+
text = getJourneyMessage(burritoCount),
|
|
1307
|
+
fontSize = 16.sp,
|
|
1308
|
+
color = TextGray,
|
|
1309
|
+
textAlign = TextAlign.Start,
|
|
1310
|
+
lineHeight = 26.sp,
|
|
1311
|
+
modifier = Modifier.fillMaxWidth()
|
|
1312
|
+
)
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
private fun getJourneyMessage(count: Int): String = when {
|
|
1319
|
+
count == 0 -> "You haven't considered any burritos yet. Start your journey!"
|
|
1320
|
+
count == 1 -> "You've considered the burrito potential once. The journey begins!"
|
|
1321
|
+
count in 2..4 -> "You're getting the hang of burrito consideration!"
|
|
1322
|
+
count in 5..9 -> "You're becoming a burrito consideration expert!"
|
|
1323
|
+
else -> "You are a true burrito consideration master!"
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
```
|
|
1327
|
+
|
|
1328
|
+
---
|
|
1329
|
+
|
|
1330
|
+
## app/src/main/java/com/example/amplitude/ui/theme/Color.kt
|
|
1331
|
+
|
|
1332
|
+
```kt
|
|
1333
|
+
package com.example.amplitude.ui.theme
|
|
1334
|
+
|
|
1335
|
+
import androidx.compose.ui.graphics.Color
|
|
1336
|
+
|
|
1337
|
+
val PrimaryBlue = Color(0xFF0070F3)
|
|
1338
|
+
val PrimaryBlueHover = Color(0xFF0051CC)
|
|
1339
|
+
val SuccessGreen = Color(0xFF28A745)
|
|
1340
|
+
val SuccessGreenHover = Color(0xFF218838)
|
|
1341
|
+
val ErrorRed = Color(0xFFDC3545)
|
|
1342
|
+
val ErrorRedHover = Color(0xFFC82333)
|
|
1343
|
+
val DarkHeader = Color(0xFF333333)
|
|
1344
|
+
val DarkHeaderHover = Color(0xFF555555)
|
|
1345
|
+
val LightGray = Color(0xFFF8F9FA)
|
|
1346
|
+
val BorderGray = Color(0xFFDDDDDD)
|
|
1347
|
+
val TextGray = Color(0xFF666666)
|
|
1348
|
+
val BackgroundGray = Color(0xFFF5F5F5)
|
|
1349
|
+
val TextDark = Color(0xFF333333)
|
|
1350
|
+
val White = Color(0xFFFFFFFF)
|
|
1351
|
+
|
|
1352
|
+
```
|
|
1353
|
+
|
|
1354
|
+
---
|
|
1355
|
+
|
|
1356
|
+
## app/src/main/java/com/example/amplitude/ui/theme/Theme.kt
|
|
1357
|
+
|
|
1358
|
+
```kt
|
|
1359
|
+
package com.example.amplitude.ui.theme
|
|
1360
|
+
|
|
1361
|
+
import androidx.compose.material3.MaterialTheme
|
|
1362
|
+
import androidx.compose.material3.lightColorScheme
|
|
1363
|
+
import androidx.compose.runtime.Composable
|
|
1364
|
+
|
|
1365
|
+
private val LightColorScheme = lightColorScheme(
|
|
1366
|
+
primary = PrimaryBlue,
|
|
1367
|
+
secondary = SuccessGreen,
|
|
1368
|
+
tertiary = DarkHeader,
|
|
1369
|
+
background = BackgroundGray,
|
|
1370
|
+
surface = White,
|
|
1371
|
+
onPrimary = White,
|
|
1372
|
+
onSecondary = White,
|
|
1373
|
+
onTertiary = White,
|
|
1374
|
+
onBackground = TextDark,
|
|
1375
|
+
onSurface = TextDark
|
|
1376
|
+
)
|
|
1377
|
+
|
|
1378
|
+
@Composable
|
|
1379
|
+
fun AmplitudeTheme(
|
|
1380
|
+
content: @Composable () -> Unit
|
|
1381
|
+
) {
|
|
1382
|
+
MaterialTheme(
|
|
1383
|
+
colorScheme = LightColorScheme,
|
|
1384
|
+
typography = Typography,
|
|
1385
|
+
content = content
|
|
1386
|
+
)
|
|
1387
|
+
}
|
|
1388
|
+
```
|
|
1389
|
+
|
|
1390
|
+
---
|
|
1391
|
+
|
|
1392
|
+
## app/src/main/java/com/example/amplitude/ui/theme/Type.kt
|
|
1393
|
+
|
|
1394
|
+
```kt
|
|
1395
|
+
package com.example.amplitude.ui.theme
|
|
1396
|
+
|
|
1397
|
+
import androidx.compose.material3.Typography
|
|
1398
|
+
import androidx.compose.ui.text.TextStyle
|
|
1399
|
+
import androidx.compose.ui.text.font.FontFamily
|
|
1400
|
+
import androidx.compose.ui.text.font.FontWeight
|
|
1401
|
+
import androidx.compose.ui.unit.sp
|
|
1402
|
+
|
|
1403
|
+
// Typography based on design specification
|
|
1404
|
+
// Uses system font stack (FontFamily.Default maps to Roboto on Android)
|
|
1405
|
+
val Typography = Typography(
|
|
1406
|
+
// H1 - Page titles (32sp)
|
|
1407
|
+
displayLarge = TextStyle(
|
|
1408
|
+
fontFamily = FontFamily.Default,
|
|
1409
|
+
fontWeight = FontWeight.SemiBold,
|
|
1410
|
+
fontSize = 32.sp,
|
|
1411
|
+
lineHeight = 40.sp,
|
|
1412
|
+
letterSpacing = 0.sp
|
|
1413
|
+
),
|
|
1414
|
+
// H2 - Section titles (24sp)
|
|
1415
|
+
displayMedium = TextStyle(
|
|
1416
|
+
fontFamily = FontFamily.Default,
|
|
1417
|
+
fontWeight = FontWeight.SemiBold,
|
|
1418
|
+
fontSize = 24.sp,
|
|
1419
|
+
lineHeight = 32.sp,
|
|
1420
|
+
letterSpacing = 0.sp
|
|
1421
|
+
),
|
|
1422
|
+
// H3 - Subsection titles (20sp)
|
|
1423
|
+
displaySmall = TextStyle(
|
|
1424
|
+
fontFamily = FontFamily.Default,
|
|
1425
|
+
fontWeight = FontWeight.SemiBold,
|
|
1426
|
+
fontSize = 20.sp,
|
|
1427
|
+
lineHeight = 26.sp,
|
|
1428
|
+
letterSpacing = 0.sp
|
|
1429
|
+
),
|
|
1430
|
+
// Body text (16sp with 1.6 line height = 25.6sp)
|
|
1431
|
+
bodyLarge = TextStyle(
|
|
1432
|
+
fontFamily = FontFamily.Default,
|
|
1433
|
+
fontWeight = FontWeight.Normal,
|
|
1434
|
+
fontSize = 16.sp,
|
|
1435
|
+
lineHeight = 26.sp,
|
|
1436
|
+
letterSpacing = 0.sp
|
|
1437
|
+
),
|
|
1438
|
+
// Small/Note text (14sp)
|
|
1439
|
+
bodySmall = TextStyle(
|
|
1440
|
+
fontFamily = FontFamily.Default,
|
|
1441
|
+
fontWeight = FontWeight.Normal,
|
|
1442
|
+
fontSize = 14.sp,
|
|
1443
|
+
lineHeight = 21.sp,
|
|
1444
|
+
letterSpacing = 0.sp
|
|
1445
|
+
),
|
|
1446
|
+
// Labels (16sp, medium weight)
|
|
1447
|
+
labelLarge = TextStyle(
|
|
1448
|
+
fontFamily = FontFamily.Default,
|
|
1449
|
+
fontWeight = FontWeight.Medium,
|
|
1450
|
+
fontSize = 16.sp,
|
|
1451
|
+
lineHeight = 24.sp,
|
|
1452
|
+
letterSpacing = 0.sp
|
|
1453
|
+
),
|
|
1454
|
+
// Button text - Burrito button (18sp)
|
|
1455
|
+
titleLarge = TextStyle(
|
|
1456
|
+
fontFamily = FontFamily.Default,
|
|
1457
|
+
fontWeight = FontWeight.Normal,
|
|
1458
|
+
fontSize = 18.sp,
|
|
1459
|
+
lineHeight = 24.sp,
|
|
1460
|
+
letterSpacing = 0.sp
|
|
1461
|
+
),
|
|
1462
|
+
// Button text - Primary/Logout (16sp/14sp)
|
|
1463
|
+
titleMedium = TextStyle(
|
|
1464
|
+
fontFamily = FontFamily.Default,
|
|
1465
|
+
fontWeight = FontWeight.Normal,
|
|
1466
|
+
fontSize = 16.sp,
|
|
1467
|
+
lineHeight = 24.sp,
|
|
1468
|
+
letterSpacing = 0.sp
|
|
1469
|
+
),
|
|
1470
|
+
titleSmall = TextStyle(
|
|
1471
|
+
fontFamily = FontFamily.Default,
|
|
1472
|
+
fontWeight = FontWeight.Normal,
|
|
1473
|
+
fontSize = 14.sp,
|
|
1474
|
+
lineHeight = 20.sp,
|
|
1475
|
+
letterSpacing = 0.sp
|
|
1476
|
+
)
|
|
1477
|
+
)
|
|
1478
|
+
```
|
|
1479
|
+
|
|
1480
|
+
---
|
|
1481
|
+
|
|
1482
|
+
## app/src/main/java/com/example/amplitude/viewmodel/AuthViewModel.kt
|
|
1483
|
+
|
|
1484
|
+
```kt
|
|
1485
|
+
package com.example.amplitude.viewmodel
|
|
1486
|
+
|
|
1487
|
+
import android.app.Application
|
|
1488
|
+
import androidx.lifecycle.AndroidViewModel
|
|
1489
|
+
import androidx.lifecycle.viewModelScope
|
|
1490
|
+
import com.example.amplitude.BurritoApplication
|
|
1491
|
+
import com.example.amplitude.data.User
|
|
1492
|
+
import com.example.amplitude.data.UserRepository
|
|
1493
|
+
import kotlinx.coroutines.flow.MutableStateFlow
|
|
1494
|
+
import kotlinx.coroutines.flow.StateFlow
|
|
1495
|
+
import kotlinx.coroutines.flow.asStateFlow
|
|
1496
|
+
import kotlinx.coroutines.launch
|
|
1497
|
+
|
|
1498
|
+
class AuthViewModel(application: Application) : AndroidViewModel(application) {
|
|
1499
|
+
|
|
1500
|
+
private val repository = UserRepository(application)
|
|
1501
|
+
private val amplitude = (application as BurritoApplication).amplitude
|
|
1502
|
+
|
|
1503
|
+
private val _currentUser = MutableStateFlow<User?>(null)
|
|
1504
|
+
val currentUser: StateFlow<User?> = _currentUser.asStateFlow()
|
|
1505
|
+
|
|
1506
|
+
private val _isAuthenticated = MutableStateFlow(false)
|
|
1507
|
+
val isAuthenticated: StateFlow<Boolean> = _isAuthenticated.asStateFlow()
|
|
1508
|
+
|
|
1509
|
+
init {
|
|
1510
|
+
loadCurrentUser()
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
private fun loadCurrentUser() {
|
|
1514
|
+
viewModelScope.launch {
|
|
1515
|
+
val user = repository.getCurrentUser()
|
|
1516
|
+
_currentUser.value = user
|
|
1517
|
+
_isAuthenticated.value = user != null
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
fun login(username: String) {
|
|
1522
|
+
viewModelScope.launch {
|
|
1523
|
+
val existingUser = repository.getUser(username)
|
|
1524
|
+
val user = existingUser ?: User(username = username, burritoConsiderations = 0)
|
|
1525
|
+
repository.saveUser(user)
|
|
1526
|
+
_currentUser.value = user
|
|
1527
|
+
_isAuthenticated.value = true
|
|
1528
|
+
|
|
1529
|
+
amplitude.setUserId(username)
|
|
1530
|
+
amplitude.track("User Logged In")
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
fun logout() {
|
|
1535
|
+
viewModelScope.launch {
|
|
1536
|
+
amplitude.track("User Logged Out")
|
|
1537
|
+
amplitude.reset()
|
|
1538
|
+
repository.clearCurrentUser()
|
|
1539
|
+
_currentUser.value = null
|
|
1540
|
+
_isAuthenticated.value = false
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
fun incrementBurritoCount() {
|
|
1545
|
+
viewModelScope.launch {
|
|
1546
|
+
val user = _currentUser.value ?: return@launch
|
|
1547
|
+
val updatedUser = user.copy(burritoConsiderations = user.burritoConsiderations + 1)
|
|
1548
|
+
repository.saveUser(updatedUser)
|
|
1549
|
+
_currentUser.value = updatedUser
|
|
1550
|
+
|
|
1551
|
+
val eventProperties = mapOf(
|
|
1552
|
+
"total_considerations" to updatedUser.burritoConsiderations,
|
|
1553
|
+
"username" to updatedUser.username
|
|
1554
|
+
)
|
|
1555
|
+
amplitude.track("Burrito Considered", eventProperties)
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
```
|
|
1561
|
+
|
|
1562
|
+
---
|
|
1563
|
+
|
|
1564
|
+
## app/src/main/res/drawable/ic_launcher_background.xml
|
|
1565
|
+
|
|
1566
|
+
```xml
|
|
1567
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
1568
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
1569
|
+
android:width="108dp"
|
|
1570
|
+
android:height="108dp"
|
|
1571
|
+
android:viewportWidth="108"
|
|
1572
|
+
android:viewportHeight="108">
|
|
1573
|
+
<path
|
|
1574
|
+
android:fillColor="#3DDC84"
|
|
1575
|
+
android:pathData="M0,0h108v108h-108z" />
|
|
1576
|
+
<path
|
|
1577
|
+
android:fillColor="#00000000"
|
|
1578
|
+
android:pathData="M9,0L9,108"
|
|
1579
|
+
android:strokeWidth="0.8"
|
|
1580
|
+
android:strokeColor="#33FFFFFF" />
|
|
1581
|
+
<path
|
|
1582
|
+
android:fillColor="#00000000"
|
|
1583
|
+
android:pathData="M19,0L19,108"
|
|
1584
|
+
android:strokeWidth="0.8"
|
|
1585
|
+
android:strokeColor="#33FFFFFF" />
|
|
1586
|
+
<path
|
|
1587
|
+
android:fillColor="#00000000"
|
|
1588
|
+
android:pathData="M29,0L29,108"
|
|
1589
|
+
android:strokeWidth="0.8"
|
|
1590
|
+
android:strokeColor="#33FFFFFF" />
|
|
1591
|
+
<path
|
|
1592
|
+
android:fillColor="#00000000"
|
|
1593
|
+
android:pathData="M39,0L39,108"
|
|
1594
|
+
android:strokeWidth="0.8"
|
|
1595
|
+
android:strokeColor="#33FFFFFF" />
|
|
1596
|
+
<path
|
|
1597
|
+
android:fillColor="#00000000"
|
|
1598
|
+
android:pathData="M49,0L49,108"
|
|
1599
|
+
android:strokeWidth="0.8"
|
|
1600
|
+
android:strokeColor="#33FFFFFF" />
|
|
1601
|
+
<path
|
|
1602
|
+
android:fillColor="#00000000"
|
|
1603
|
+
android:pathData="M59,0L59,108"
|
|
1604
|
+
android:strokeWidth="0.8"
|
|
1605
|
+
android:strokeColor="#33FFFFFF" />
|
|
1606
|
+
<path
|
|
1607
|
+
android:fillColor="#00000000"
|
|
1608
|
+
android:pathData="M69,0L69,108"
|
|
1609
|
+
android:strokeWidth="0.8"
|
|
1610
|
+
android:strokeColor="#33FFFFFF" />
|
|
1611
|
+
<path
|
|
1612
|
+
android:fillColor="#00000000"
|
|
1613
|
+
android:pathData="M79,0L79,108"
|
|
1614
|
+
android:strokeWidth="0.8"
|
|
1615
|
+
android:strokeColor="#33FFFFFF" />
|
|
1616
|
+
<path
|
|
1617
|
+
android:fillColor="#00000000"
|
|
1618
|
+
android:pathData="M89,0L89,108"
|
|
1619
|
+
android:strokeWidth="0.8"
|
|
1620
|
+
android:strokeColor="#33FFFFFF" />
|
|
1621
|
+
<path
|
|
1622
|
+
android:fillColor="#00000000"
|
|
1623
|
+
android:pathData="M99,0L99,108"
|
|
1624
|
+
android:strokeWidth="0.8"
|
|
1625
|
+
android:strokeColor="#33FFFFFF" />
|
|
1626
|
+
<path
|
|
1627
|
+
android:fillColor="#00000000"
|
|
1628
|
+
android:pathData="M0,9L108,9"
|
|
1629
|
+
android:strokeWidth="0.8"
|
|
1630
|
+
android:strokeColor="#33FFFFFF" />
|
|
1631
|
+
<path
|
|
1632
|
+
android:fillColor="#00000000"
|
|
1633
|
+
android:pathData="M0,19L108,19"
|
|
1634
|
+
android:strokeWidth="0.8"
|
|
1635
|
+
android:strokeColor="#33FFFFFF" />
|
|
1636
|
+
<path
|
|
1637
|
+
android:fillColor="#00000000"
|
|
1638
|
+
android:pathData="M0,29L108,29"
|
|
1639
|
+
android:strokeWidth="0.8"
|
|
1640
|
+
android:strokeColor="#33FFFFFF" />
|
|
1641
|
+
<path
|
|
1642
|
+
android:fillColor="#00000000"
|
|
1643
|
+
android:pathData="M0,39L108,39"
|
|
1644
|
+
android:strokeWidth="0.8"
|
|
1645
|
+
android:strokeColor="#33FFFFFF" />
|
|
1646
|
+
<path
|
|
1647
|
+
android:fillColor="#00000000"
|
|
1648
|
+
android:pathData="M0,49L108,49"
|
|
1649
|
+
android:strokeWidth="0.8"
|
|
1650
|
+
android:strokeColor="#33FFFFFF" />
|
|
1651
|
+
<path
|
|
1652
|
+
android:fillColor="#00000000"
|
|
1653
|
+
android:pathData="M0,59L108,59"
|
|
1654
|
+
android:strokeWidth="0.8"
|
|
1655
|
+
android:strokeColor="#33FFFFFF" />
|
|
1656
|
+
<path
|
|
1657
|
+
android:fillColor="#00000000"
|
|
1658
|
+
android:pathData="M0,69L108,69"
|
|
1659
|
+
android:strokeWidth="0.8"
|
|
1660
|
+
android:strokeColor="#33FFFFFF" />
|
|
1661
|
+
<path
|
|
1662
|
+
android:fillColor="#00000000"
|
|
1663
|
+
android:pathData="M0,79L108,79"
|
|
1664
|
+
android:strokeWidth="0.8"
|
|
1665
|
+
android:strokeColor="#33FFFFFF" />
|
|
1666
|
+
<path
|
|
1667
|
+
android:fillColor="#00000000"
|
|
1668
|
+
android:pathData="M0,89L108,89"
|
|
1669
|
+
android:strokeWidth="0.8"
|
|
1670
|
+
android:strokeColor="#33FFFFFF" />
|
|
1671
|
+
<path
|
|
1672
|
+
android:fillColor="#00000000"
|
|
1673
|
+
android:pathData="M0,99L108,99"
|
|
1674
|
+
android:strokeWidth="0.8"
|
|
1675
|
+
android:strokeColor="#33FFFFFF" />
|
|
1676
|
+
<path
|
|
1677
|
+
android:fillColor="#00000000"
|
|
1678
|
+
android:pathData="M19,29L89,29"
|
|
1679
|
+
android:strokeWidth="0.8"
|
|
1680
|
+
android:strokeColor="#33FFFFFF" />
|
|
1681
|
+
<path
|
|
1682
|
+
android:fillColor="#00000000"
|
|
1683
|
+
android:pathData="M19,39L89,39"
|
|
1684
|
+
android:strokeWidth="0.8"
|
|
1685
|
+
android:strokeColor="#33FFFFFF" />
|
|
1686
|
+
<path
|
|
1687
|
+
android:fillColor="#00000000"
|
|
1688
|
+
android:pathData="M19,49L89,49"
|
|
1689
|
+
android:strokeWidth="0.8"
|
|
1690
|
+
android:strokeColor="#33FFFFFF" />
|
|
1691
|
+
<path
|
|
1692
|
+
android:fillColor="#00000000"
|
|
1693
|
+
android:pathData="M19,59L89,59"
|
|
1694
|
+
android:strokeWidth="0.8"
|
|
1695
|
+
android:strokeColor="#33FFFFFF" />
|
|
1696
|
+
<path
|
|
1697
|
+
android:fillColor="#00000000"
|
|
1698
|
+
android:pathData="M19,69L89,69"
|
|
1699
|
+
android:strokeWidth="0.8"
|
|
1700
|
+
android:strokeColor="#33FFFFFF" />
|
|
1701
|
+
<path
|
|
1702
|
+
android:fillColor="#00000000"
|
|
1703
|
+
android:pathData="M19,79L89,79"
|
|
1704
|
+
android:strokeWidth="0.8"
|
|
1705
|
+
android:strokeColor="#33FFFFFF" />
|
|
1706
|
+
<path
|
|
1707
|
+
android:fillColor="#00000000"
|
|
1708
|
+
android:pathData="M29,19L29,89"
|
|
1709
|
+
android:strokeWidth="0.8"
|
|
1710
|
+
android:strokeColor="#33FFFFFF" />
|
|
1711
|
+
<path
|
|
1712
|
+
android:fillColor="#00000000"
|
|
1713
|
+
android:pathData="M39,19L39,89"
|
|
1714
|
+
android:strokeWidth="0.8"
|
|
1715
|
+
android:strokeColor="#33FFFFFF" />
|
|
1716
|
+
<path
|
|
1717
|
+
android:fillColor="#00000000"
|
|
1718
|
+
android:pathData="M49,19L49,89"
|
|
1719
|
+
android:strokeWidth="0.8"
|
|
1720
|
+
android:strokeColor="#33FFFFFF" />
|
|
1721
|
+
<path
|
|
1722
|
+
android:fillColor="#00000000"
|
|
1723
|
+
android:pathData="M59,19L59,89"
|
|
1724
|
+
android:strokeWidth="0.8"
|
|
1725
|
+
android:strokeColor="#33FFFFFF" />
|
|
1726
|
+
<path
|
|
1727
|
+
android:fillColor="#00000000"
|
|
1728
|
+
android:pathData="M69,19L69,89"
|
|
1729
|
+
android:strokeWidth="0.8"
|
|
1730
|
+
android:strokeColor="#33FFFFFF" />
|
|
1731
|
+
<path
|
|
1732
|
+
android:fillColor="#00000000"
|
|
1733
|
+
android:pathData="M79,19L79,89"
|
|
1734
|
+
android:strokeWidth="0.8"
|
|
1735
|
+
android:strokeColor="#33FFFFFF" />
|
|
1736
|
+
</vector>
|
|
1737
|
+
|
|
1738
|
+
```
|
|
1739
|
+
|
|
1740
|
+
---
|
|
1741
|
+
|
|
1742
|
+
## app/src/main/res/drawable/ic_launcher_foreground.xml
|
|
1743
|
+
|
|
1744
|
+
```xml
|
|
1745
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
1746
|
+
xmlns:aapt="http://schemas.android.com/aapt"
|
|
1747
|
+
android:width="108dp"
|
|
1748
|
+
android:height="108dp"
|
|
1749
|
+
android:viewportWidth="108"
|
|
1750
|
+
android:viewportHeight="108">
|
|
1751
|
+
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
|
1752
|
+
<aapt:attr name="android:fillColor">
|
|
1753
|
+
<gradient
|
|
1754
|
+
android:endX="85.84757"
|
|
1755
|
+
android:endY="92.4963"
|
|
1756
|
+
android:startX="42.9492"
|
|
1757
|
+
android:startY="49.59793"
|
|
1758
|
+
android:type="linear">
|
|
1759
|
+
<item
|
|
1760
|
+
android:color="#44000000"
|
|
1761
|
+
android:offset="0.0" />
|
|
1762
|
+
<item
|
|
1763
|
+
android:color="#00000000"
|
|
1764
|
+
android:offset="1.0" />
|
|
1765
|
+
</gradient>
|
|
1766
|
+
</aapt:attr>
|
|
1767
|
+
</path>
|
|
1768
|
+
<path
|
|
1769
|
+
android:fillColor="#FFFFFF"
|
|
1770
|
+
android:fillType="nonZero"
|
|
1771
|
+
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
|
1772
|
+
android:strokeWidth="1"
|
|
1773
|
+
android:strokeColor="#00000000" />
|
|
1774
|
+
</vector>
|
|
1775
|
+
```
|
|
1776
|
+
|
|
1777
|
+
---
|
|
1778
|
+
|
|
1779
|
+
## app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
|
|
1780
|
+
|
|
1781
|
+
```xml
|
|
1782
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
1783
|
+
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
1784
|
+
<background android:drawable="@drawable/ic_launcher_background" />
|
|
1785
|
+
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
1786
|
+
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
|
1787
|
+
</adaptive-icon>
|
|
1788
|
+
```
|
|
1789
|
+
|
|
1790
|
+
---
|
|
1791
|
+
|
|
1792
|
+
## app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
|
|
1793
|
+
|
|
1794
|
+
```xml
|
|
1795
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
1796
|
+
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
1797
|
+
<background android:drawable="@drawable/ic_launcher_background" />
|
|
1798
|
+
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
1799
|
+
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
|
1800
|
+
</adaptive-icon>
|
|
1801
|
+
```
|
|
1802
|
+
|
|
1803
|
+
---
|
|
1804
|
+
|
|
1805
|
+
## app/src/main/res/values/colors.xml
|
|
1806
|
+
|
|
1807
|
+
```xml
|
|
1808
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
1809
|
+
<resources>
|
|
1810
|
+
<color name="purple_200">#FFBB86FC</color>
|
|
1811
|
+
<color name="purple_500">#FF6200EE</color>
|
|
1812
|
+
<color name="purple_700">#FF3700B3</color>
|
|
1813
|
+
<color name="teal_200">#FF03DAC5</color>
|
|
1814
|
+
<color name="teal_700">#FF018786</color>
|
|
1815
|
+
<color name="black">#FF000000</color>
|
|
1816
|
+
<color name="white">#FFFFFFFF</color>
|
|
1817
|
+
</resources>
|
|
1818
|
+
```
|
|
1819
|
+
|
|
1820
|
+
---
|
|
1821
|
+
|
|
1822
|
+
## app/src/main/res/values/strings.xml
|
|
1823
|
+
|
|
1824
|
+
```xml
|
|
1825
|
+
<resources>
|
|
1826
|
+
<string name="app_name">Amplitude</string>
|
|
1827
|
+
</resources>
|
|
1828
|
+
```
|
|
1829
|
+
|
|
1830
|
+
---
|
|
1831
|
+
|
|
1832
|
+
## app/src/main/res/values/themes.xml
|
|
1833
|
+
|
|
1834
|
+
```xml
|
|
1835
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
1836
|
+
<resources>
|
|
1837
|
+
|
|
1838
|
+
<style name="Theme.Amplitude" parent="android:Theme.Material.Light.NoActionBar" />
|
|
1839
|
+
</resources>
|
|
1840
|
+
```
|
|
1841
|
+
|
|
1842
|
+
---
|
|
1843
|
+
|
|
1844
|
+
## app/src/main/res/xml/backup_rules.xml
|
|
1845
|
+
|
|
1846
|
+
```xml
|
|
1847
|
+
<?xml version="1.0" encoding="utf-8"?><!--
|
|
1848
|
+
Sample backup rules file; uncomment and customize as necessary.
|
|
1849
|
+
See https://developer.android.com/guide/topics/data/autobackup
|
|
1850
|
+
for details.
|
|
1851
|
+
Note: This file is ignored for devices older than API 31
|
|
1852
|
+
See https://developer.android.com/about/versions/12/backup-restore
|
|
1853
|
+
-->
|
|
1854
|
+
<full-backup-content>
|
|
1855
|
+
<!--
|
|
1856
|
+
<include domain="sharedpref" path="."/>
|
|
1857
|
+
<exclude domain="sharedpref" path="device.xml"/>
|
|
1858
|
+
-->
|
|
1859
|
+
</full-backup-content>
|
|
1860
|
+
```
|
|
1861
|
+
|
|
1862
|
+
---
|
|
1863
|
+
|
|
1864
|
+
## app/src/main/res/xml/data_extraction_rules.xml
|
|
1865
|
+
|
|
1866
|
+
```xml
|
|
1867
|
+
<?xml version="1.0" encoding="utf-8"?><!--
|
|
1868
|
+
Sample data extraction rules file; uncomment and customize as necessary.
|
|
1869
|
+
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
|
1870
|
+
for details.
|
|
1871
|
+
-->
|
|
1872
|
+
<data-extraction-rules>
|
|
1873
|
+
<cloud-backup>
|
|
1874
|
+
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
|
1875
|
+
<include .../>
|
|
1876
|
+
<exclude .../>
|
|
1877
|
+
-->
|
|
1878
|
+
</cloud-backup>
|
|
1879
|
+
<!--
|
|
1880
|
+
<device-transfer>
|
|
1881
|
+
<include .../>
|
|
1882
|
+
<exclude .../>
|
|
1883
|
+
</device-transfer>
|
|
1884
|
+
-->
|
|
1885
|
+
</data-extraction-rules>
|
|
1886
|
+
```
|
|
1887
|
+
|
|
1888
|
+
---
|
|
1889
|
+
|
|
1890
|
+
## gradle.properties
|
|
1891
|
+
|
|
1892
|
+
```properties
|
|
1893
|
+
# Project-wide Gradle settings.
|
|
1894
|
+
# IDE (e.g. Android Studio) users:
|
|
1895
|
+
# Gradle settings configured through the IDE *will override*
|
|
1896
|
+
# any settings specified in this file.
|
|
1897
|
+
# For more details on how to configure your build environment visit
|
|
1898
|
+
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
|
1899
|
+
# Specifies the JVM arguments used for the daemon process.
|
|
1900
|
+
# The setting is particularly useful for tweaking memory settings.
|
|
1901
|
+
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
|
1902
|
+
# When configured, Gradle will run in incubating parallel mode.
|
|
1903
|
+
# This option should only be used with decoupled projects. For more details, visit
|
|
1904
|
+
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
|
1905
|
+
# org.gradle.parallel=true
|
|
1906
|
+
# AndroidX package structure to make it clearer which packages are bundled with the
|
|
1907
|
+
# Android operating system, and which are packaged with your app's APK
|
|
1908
|
+
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
|
1909
|
+
android.useAndroidX=true
|
|
1910
|
+
# Kotlin code style for this project: "official" or "obsolete":
|
|
1911
|
+
kotlin.code.style=official
|
|
1912
|
+
# Enables namespacing of each library's R class so that its R class includes only the
|
|
1913
|
+
# resources declared in the library itself and none from the library's dependencies,
|
|
1914
|
+
# thereby reducing the size of the R class for that library
|
|
1915
|
+
android.nonTransitiveRClass=true
|
|
1916
|
+
```
|
|
1917
|
+
|
|
1918
|
+
---
|
|
1919
|
+
|
|
1920
|
+
## gradle/libs.versions.toml
|
|
1921
|
+
|
|
1922
|
+
```toml
|
|
1923
|
+
[versions]
|
|
1924
|
+
agp = "9.0.0"
|
|
1925
|
+
coreKtx = "1.10.1"
|
|
1926
|
+
junit = "4.13.2"
|
|
1927
|
+
junitVersion = "1.1.5"
|
|
1928
|
+
espressoCore = "3.5.1"
|
|
1929
|
+
lifecycleRuntimeKtx = "2.6.1"
|
|
1930
|
+
activityCompose = "1.8.0"
|
|
1931
|
+
kotlin = "2.0.21"
|
|
1932
|
+
composeBom = "2024.09.00"
|
|
1933
|
+
navigationCompose = "2.7.5"
|
|
1934
|
+
amplitude = "1.+"
|
|
1935
|
+
|
|
1936
|
+
[libraries]
|
|
1937
|
+
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
|
1938
|
+
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
|
1939
|
+
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
|
1940
|
+
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
|
1941
|
+
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
|
1942
|
+
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
|
1943
|
+
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
|
1944
|
+
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
|
|
1945
|
+
androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
|
1946
|
+
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
|
1947
|
+
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
|
1948
|
+
androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
|
1949
|
+
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
|
1950
|
+
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
|
1951
|
+
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
|
|
1952
|
+
amplitude-android = { group = "com.amplitude", name = "analytics-android", version.ref = "amplitude" }
|
|
1953
|
+
|
|
1954
|
+
[plugins]
|
|
1955
|
+
android-application = { id = "com.android.application", version.ref = "agp" }
|
|
1956
|
+
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
|
1957
|
+
|
|
1958
|
+
|
|
1959
|
+
```
|
|
1960
|
+
|
|
1961
|
+
---
|
|
1962
|
+
|
|
1963
|
+
## local.properties.example
|
|
1964
|
+
|
|
1965
|
+
```example
|
|
1966
|
+
# Copy this file to local.properties and fill in your values
|
|
1967
|
+
# local.properties is gitignored and will not be committed
|
|
1968
|
+
|
|
1969
|
+
sdk.dir=/path/to/your/android/sdk
|
|
1970
|
+
|
|
1971
|
+
# Amplitude configuration
|
|
1972
|
+
amplitude.apiKey=your_amplitude_api_key_here
|
|
1973
|
+
|
|
1974
|
+
```
|
|
1975
|
+
|
|
1976
|
+
---
|
|
1977
|
+
|