@@ -43,12 +43,12 @@ SMS operations:
4343Tag operations:
4444- addTag(key: String, value: String)
4545- removeTag(key: String)
46- - removeTags(keys: Collection<String>)
4746- getTags(): Map<String, String>
4847
4948Trigger operations:
5049- addTrigger(key: String, value: String)
5150- removeTrigger(key: String)
51+ - clearTriggers(keys: Collection<String>)
5252
5353Outcome operations:
5454- sendOutcome(name: String)
@@ -125,17 +125,17 @@ In MainViewModel.kt, implement observers:
125125
126126### Section Order (top to bottom) - FINAL
127127
128- 1 . ** App Section** (App ID, Guidance Banner, Consent Toggle, Login/Logout)
129- 2 . ** Push Section** (Push ID, Enabled Toggle, Prompt Push button )
128+ 1 . ** App Section** (App ID, Guidance Banner, Consent Toggle, Logged-in-as display, Login/Logout)
129+ 2 . ** Push Section** (Push ID, Enabled Toggle, Auto-prompts permission on load )
1301303 . ** Send Push Notification Section** (Simple, With Image, Custom buttons)
1311314 . ** In-App Messaging Section** (Pause toggle)
1321325 . ** Send In-App Message Section** (Top Banner, Bottom Banner, Center Modal, Full Screen)
1331336 . ** Aliases Section** (RecyclerView with Add/Remove All)
1341347 . ** Emails Section** (RecyclerView with Add, collapsible >5 items)
1351358 . ** SMS Section** (RecyclerView with Add, collapsible >5 items)
136- 9 . ** Tags Section** (RecyclerView with Add/Remove All )
136+ 9 . ** Tags Section** (RecyclerView with Add, individual remove only )
13713710 . ** Outcome Events Section** (Send Outcome dropdown)
138- 11 . ** Triggers Section** (RecyclerView with Add - IN MEMORY ONLY)
138+ 11 . ** Triggers Section** (RecyclerView with Add/Clear Triggers - IN MEMORY ONLY)
13913912 . ** Track Event Section** (Track Event button)
14014013 . ** Location Section** (Location Shared toggle, Prompt Location button)
14114114 . ** Next Activity Button**
@@ -158,14 +158,17 @@ App Section layout:
158158 - SwitchCompat control
159159 - NOT a blocking overlay - user can interact with app regardless of state
160160
161- 4. LOGIN USER button:
161+ 4. "Logged in as" display (ABOVE the buttons, only visible when logged in):
162+ - Prominent green CardView background (#E8F5E9)
163+ - "Logged in as:" label (16sp)
164+ - External User ID displayed large and centered (22sp bold, green #2E7D32)
165+ - Positioned ABOVE the Login/Switch User button
166+
167+ 5. LOGIN USER button:
162168 - Shows "LOGIN USER" when no user is logged in
163169 - Shows "SWITCH USER" when a user is logged in
164170 - Opens dialog with empty "External User Id" field
165171
166- 5. External ID display (only visible when logged in):
167- - Shows current external user ID below login button
168-
1691726. LOGOUT USER button
170173```
171174
@@ -274,12 +277,10 @@ SMS Section:
274277Tags Section:
275278- Section title: "Tags" with info icon for tooltip
276279- RecyclerView showing key-value pairs
277- - Each item shows: Key | Value with long-press to delete
280+ - Each item shows: Key | Value with X button to delete individually
278281- "No Tags Added" text when empty
279282- ADD TAG button → dialog with empty Key and Value fields
280- - REMOVE ALL TAGS button:
281- - Only visible when at least one tag exists
282- - Red background color
283+ - NO "Remove All" button - tags are removed individually only
283284```
284285
285286### Prompt 2.10 - Outcome Events Section
@@ -299,9 +300,13 @@ Outcome Events Section:
299300Triggers Section:
300301- Section title: "Triggers" with info icon for tooltip
301302- RecyclerView showing key-value pairs
302- - Each item shows: Key | Value with long-press to delete
303+ - Each item shows: Key | Value with X button to delete individually
303304- "No Triggers Added" text when empty
304305- ADD TRIGGER button → dialog with empty Key and Value fields
306+ - CLEAR TRIGGERS button:
307+ - Only visible when at least 1 trigger exists
308+ - Red background color
309+ - Clears all triggers at once
305310
306311IMPORTANT: Triggers are stored IN MEMORY ONLY during the app session.
307312- triggersList is a mutableListOf<Pair<String, String>>() in MainViewModel
@@ -343,10 +348,13 @@ Loading indicator overlay:
343348- Add ProgressBar overlay to activity_main.xml (covers entire screen with semi-transparent background)
344349- Add isLoading LiveData to MainViewModel
345350- Show/hide based on isLoading state
351+ - IMPORTANT: Add 100ms delay after populating data before dismissing loading indicator
352+ - This ensures UI (RecyclerViews, adapters) has time to render
353+ - Use kotlinx.coroutines.delay(100) after setting all LiveData values
346354
347355On cold start:
348356- Check if OneSignal.User.onesignalId is not null
349- - If exists: show loading → call fetchUserDataFromApi() → populate UI → hide loading
357+ - If exists: show loading → call fetchUserDataFromApi() → populate UI → delay 100ms → hide loading
350358- If null: just show empty state (no loading indicator)
351359
352360On login (LOGIN USER / SWITCH USER):
@@ -355,7 +363,7 @@ On login (LOGIN USER / SWITCH USER):
355363- Clear old user data (aliases, emails, sms, triggers)
356364- Wait for onUserStateChange callback
357365- onUserStateChange calls fetchUserDataFromApi()
358- - fetchUserDataFromApi() populates UI and hides loading
366+ - fetchUserDataFromApi() populates UI, delays 100ms, then hides loading
359367
360368On logout:
361369- Show loading indicator
@@ -461,10 +469,21 @@ Create TooltipHelper.kt:
461469
462470object TooltipHelper {
463471 private var tooltips: Map<String, TooltipData> = emptyMap()
472+ private var initialized = false
464473
465474 fun init(context: Context) {
466- // Load tooltip_content.json from assets
467- // Parse JSON into tooltips map
475+ if (initialized) return
476+
477+ // IMPORTANT: Load on background thread to avoid blocking app startup
478+ CoroutineScope(Dispatchers.IO).launch {
479+ // Load tooltip_content.json from assets
480+ // Parse JSON into tooltips map
481+
482+ withContext(Dispatchers.Main) {
483+ // Update tooltips map on main thread
484+ initialized = true
485+ }
486+ }
468487 }
469488
470489 fun getTooltip(key: String): TooltipData?
@@ -527,7 +546,12 @@ MainViewModel holds in memory:
527546 - Cleared on app restart
528547 - Used for testing IAM trigger conditions
529548
530- - aliasesList, emailsList, smsNumbersList:
549+ - aliasesList:
550+ - Populated from REST API on each session start
551+ - When user adds alias locally, added to list immediately (SDK syncs async)
552+ - Fetched fresh via fetchUserDataFromApi() on login/app start
553+
554+ - emailsList, smsNumbersList:
531555 - Populated from REST API on each session
532556 - Not cached locally
533557 - Fetched fresh via fetchUserDataFromApi()
@@ -644,14 +668,41 @@ If you change the package name, you must also update these files with your own F
644668
645669---
646670
671+ ## Phase 7: Important Implementation Details
672+
673+ ### Alias Management
674+
675+ ```
676+ Aliases are managed with a hybrid approach:
677+
678+ 1. On app start/login: Fetched from REST API via fetchUserDataFromApi()
679+ 2. When user adds alias locally:
680+ - Call OneSignal.User.addAlias(label, id) - syncs to server async
681+ - Immediately add to local aliasesList (don't wait for API)
682+ - This ensures instant UI feedback while SDK syncs in background
683+ 3. On next app launch: Fresh data from API includes the synced alias
684+ ```
685+
686+ ### Notification Permission
687+
688+ ```
689+ Notification permission is automatically requested when MainActivity loads:
690+ - Call viewModel.promptPush() at end of onCreate()
691+ - This ensures prompt appears after user sees the app UI
692+ - PROMPT PUSH button remains as fallback if user initially denied
693+ - Button hidden once permission is granted
694+ ```
695+
696+ ---
697+
647698## Summary
648699
649700This app demonstrates all OneSignal Android SDK features:
650701- User management (login/logout, aliases)
651- - Push notifications (subscription, sending)
702+ - Push notifications (subscription, sending, auto-permission prompt )
652703- Email and SMS subscriptions
653- - Tags for segmentation
654- - Triggers for in-app message targeting (in-memory only)
704+ - Tags for segmentation (individual remove only, no bulk remove)
705+ - Triggers for in-app message targeting (in-memory only, with Clear Triggers )
655706- Outcomes for conversion tracking
656707- Event tracking
657708- In-app messages (display and testing)
@@ -664,3 +715,5 @@ The app is designed to be:
6647153 . ** Clean** - MVVM architecture with centralized OneSignal code
6657164 . ** Cross-platform ready** - Tooltip content in JSON for sharing across wrappers
6667175 . ** Session-based triggers** - Triggers stored in memory only, cleared on restart
718+ 6 . ** Responsive UI** - Loading indicator with delay to ensure UI populates before dismissing
719+ 7 . ** Performant** - Tooltip JSON loaded on background thread
0 commit comments