Skip to content

Conversation

@nimi0112
Copy link

@nimi0112 nimi0112 commented Jan 18, 2026

Continue Compose migration: LoginScreen and ProxySettings (Part 2 of ConfigureScreen work)
1st PR: #2991

Checklist

  • If applicable, unit tests
  • If applicable, create follow-up issues for purchases-ios and hybrids

Motivation

Migrates LoginScreen and ProxySettings from Fragment-based UI to Jetpack Compose

Description

LoginScreen Migration:

  • New LoginScreen.kt with Material3 components
  • LoginScreenViewModel using StateFlow and coroutines
  • Support for user ID login and anonymous user flow
  • Unit tests covering all state transitions

ProxySettings Migration:

  • New ProxySettingsSheet.kt as Material3 ModalBottomSheet
  • ProxySettingsViewModel with StateFlow and injectable dispatcher
  • Shows as overlay from ConfigureScreen and LoginScreen
  • Enhanced error messages with exception class names
  • Unit tests for all proxy state scenarios

Implementation Notes:

  • Old Fragment implementations remain untouched for OverviewFragment
  • Clean separation: Compose code in ui/screens/, Fragments in root packages
  • Both implementations share ProxySettingsState and ProxyMode
  • Fragment code will be removed when OverviewScreen migrates to Compose
output.mp4

I could not fully test the proxy settings because I did not had a valid proxy. The implementation was verified with the available resources since the ViewModel and methods are unchanged. Any resources for end-to-end proxy testing would be appreciated.

@nimi0112 nimi0112 requested a review from a team as a code owner January 18, 2026 08:35
…odule

Migrate ConfigureScreen to Jetpack Compose

- Add Compose theme system (Color, Type, Theme)
- Create ConfigureActivity as Compose entry point
- Add Compose dependencies to build config
- Add test cases for ConfigureScreenViewModel
Refactor ConfigureScreenViewModelImpl to use a reactive flow-based approach:
- Remove init block and loadData() method to eliminate side effects
- Transform _state from MutableStateFlow to derived StateFlow using combine()
- Add userEdits flow to track user modifications before persistence
- Use SharingStarted.WhileSubscribed() for lazy, lifecycle-aware data loading
- Extract subscription timeout as SUBSCRIPTION_TIMEOUT_MILLIS constant

Benefits:
- No side effects in constructor/init
- Data loads only when UI observes the state
- Better testability and lifecycle awareness
- Automatic cleanup when no observers
- Follows modern Android ViewModel best practices

All existing tests pass without modifications.
…ecture

Migrate LoginFragment to a fully Compose-based LoginScreen with type-safe
navigation architecture, following the patterns established in ConfigureScreen.

## LoginScreen Implementation

Created new Compose-based login screen with:
- LoginScreenState: Sealed class for state management with Loading and Data states
- LoginScreenViewModel: Reactive ViewModel using StateFlow with user edits pattern
- LoginScreen: Composable UI matching original fragment_login.xml layout exactly
- LoginScreenViewModelTest: Comprehensive unit tests (7 tests, all passing)

Key features:
- User ID input with automatic trimming
- Login with user ID using Purchases.logInWith()
- Continue as random user (handles both anonymous and logged-in states)
- Reset SDK functionality with Purchases.close()
- Type-safe event system for navigation and error handling
- String resources for all UI text (localization-ready)

## Navigation Architecture

Created type-safe Compose navigation:
- PurchaseTesterScreen: Sealed class defining all navigation routes
- PurchaseTesterNavHost: NavHost with hybrid Compose + Fragment navigation
- Smooth transitions between Configure and Login screens
- Proper back stack management with popUpTo configurations

Navigation flows:
- Configure → Login: Pure Compose navigation within ConfigureActivity
- Login → Overview: Intent to MainActivity (Fragment-based, until migrated)
- Login → Configure: Compose navigation with full stack clear
- Logs/Proxy: Fallback to Fragment navigation (until migrated)

## Cleanup and Updates

Deleted obsolete files:
- LoginFragment.kt (2,768 bytes)
- fragment_login.xml (4,217 bytes)

Updated existing files:
- ConfigureActivity: Now uses PurchaseTesterApp with NavHost
- MainActivity: Removed LOGIN navigation case (handled by Compose)
- OverviewFragment: Logout navigates to ConfigureActivity instead of LoginFragment
- nav_graph.xml: Removed all LoginFragment references, changed startDestination
- Constants.kt: Removed unused LOGIN constant

## Architecture Benefits

- Type-safe navigation with sealed classes (compile-time safety)
- Hybrid navigation allowing gradual migration of remaining screens
- Pattern consistency across all Compose screens
- Better testability with interface-based ViewModels
- Modern reactive state management
- Clean separation between Compose and Fragment screens

## Testing

- All unit tests passing (7/7)
- Build successful
- Feature parity with original LoginFragment verified
- Navigation flows tested and working correctly

This migration establishes the navigation architecture pattern for remaining
screen migrations and demonstrates best practices for Compose + Fragment hybrid
navigation during incremental migration.
    Implement new Compose-native ProxySettings bottom sheet to replace Fragment-based implementation for ConfigureScreen and
    LoginScreen.

    Changes:
    - Add ProxySettingsViewModel with StateFlow and coroutines
    - Add ProxySettingsSheet using Material3 ModalBottomSheet
    - Update PurchaseTesterNavHost to show sheet as modal overlay
    - Add comprehensive unit tests for ProxySettingsViewModel
    - Improve error messages with exception class name for debugging

    The old Fragment-based implementation remains untouched and continues to be used by OverviewFragment. It will be removed when
    OverviewScreen is migrated to Compose in a future PR.

    Technical details:
    - Uses StateFlow instead of LiveData for reactive state management
    - Coroutines with injectable dispatcher for testability
    - No init block - explicit loadCurrentState() method
    - Inline updateState function matching ConfigureScreenViewModel pattern
    - Unit tests covering all state transitions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant