├── .editorconfig
├── .github
├── dependabot.yml.bkp
├── release-drafter.yml
├── renovate.json
└── workflows
│ ├── build.yml
│ ├── draft.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .run
├── Generate Baseline Profile.run.xml
├── Generate Compose Metrics.run.xml
├── Kover Html Debug Report.run.xml
└── Run all tests.run.xml
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ ├── dev
│ └── res
│ │ ├── drawable
│ │ └── ic_launcher_foreground.xml
│ │ ├── mipmap-anydpi
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ └── values
│ │ ├── ic_launcher_background.xml
│ │ └── strings.xml
│ ├── devRelease
│ └── generated
│ │ └── baselineProfiles
│ │ ├── baseline-prof.txt
│ │ └── startup-prof.txt
│ └── main
│ ├── AndroidManifest.xml
│ ├── baseline-prof.txt
│ ├── ic_launcher-playstore.png
│ ├── kotlin
│ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ ├── FocusLauncherApp.kt
│ │ ├── LauncherActivity.kt
│ │ └── di
│ │ └── CircuitModule.kt
│ └── res
│ ├── drawable
│ └── ic_launcher_foreground.xml
│ ├── mipmap-anydpi
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ └── values
│ ├── ic_launcher_background.xml
│ └── themes.xml
├── baselineprofile
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── kotlin
│ └── baselineprofile
│ ├── BaselineProfileGenerator.kt
│ ├── StartupBenchmarks.kt
│ └── extensions
│ ├── LauncherScreenExtensions.kt
│ └── UiDeviceExtensions.kt
├── build-logic
├── convention
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── kotlin
│ │ ├── AndroidApplicationComposeConventionPlugin.kt
│ │ ├── AndroidApplicationConventionPlugin.kt
│ │ ├── AndroidFeatureConventionPlugin.kt
│ │ ├── AndroidHiltConventionPlugin.kt
│ │ ├── AndroidLibraryComposeConventionPlugin.kt
│ │ ├── AndroidLibraryComposeTestingConventionPlugin.kt
│ │ ├── AndroidLibraryConventionPlugin.kt
│ │ ├── AndroidRoomConventionPlugin.kt
│ │ ├── KotlinLibraryConventionPlugin.kt
│ │ ├── LintConventionPlugin.kt
│ │ ├── NewScreenConventionPlugin.kt
│ │ ├── SentryPlugin.kt
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ ├── AndroidCompose.kt
│ │ └── KotlinAndroid.kt
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
└── settings.gradle.kts
├── build.gradle.kts
├── buildScripts
└── install-git-hooks.gradle.kts
├── config
└── detekt
│ └── detekt.yml
├── core
├── circuitoverlay
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── core
│ │ └── circuitoverlay
│ │ ├── OverlayResultScreen.kt
│ │ └── bottomsheet
│ │ ├── BottomSheetOverlay.kt
│ │ └── BottomSheetOverlayExtensions.kt
├── common
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── mslalith
│ │ │ └── focuslauncher
│ │ │ └── core
│ │ │ └── common
│ │ │ ├── appcoroutinedispatcher
│ │ │ ├── AppCoroutineDispatcher.kt
│ │ │ ├── impl
│ │ │ │ └── AppCoroutineDispatcherImpl.kt
│ │ │ └── test
│ │ │ │ └── TestAppCoroutineDispatcher.kt
│ │ │ ├── di
│ │ │ ├── CommonModule.kt
│ │ │ └── test
│ │ │ │ └── TestCommonModule.kt
│ │ │ ├── extensions
│ │ │ ├── ContextExtensions.kt
│ │ │ ├── DateTimeExtensions.kt
│ │ │ ├── ImmutableExtensions.kt
│ │ │ └── UtilityExtensions.kt
│ │ │ ├── model
│ │ │ ├── LoadingState.kt
│ │ │ └── State.kt
│ │ │ ├── network
│ │ │ ├── NetworkMonitor.kt
│ │ │ ├── impl
│ │ │ │ └── ConnectivityManagerNetworkMonitorImpl.kt
│ │ │ └── test
│ │ │ │ └── FakeNetworkMonitor.kt
│ │ │ └── providers
│ │ │ ├── clock
│ │ │ ├── ClockProvider.kt
│ │ │ ├── impl
│ │ │ │ └── ClockProviderImpl.kt
│ │ │ └── test
│ │ │ │ └── TestClockProvider.kt
│ │ │ └── randomnumber
│ │ │ ├── RandomNumberProvider.kt
│ │ │ ├── impl
│ │ │ └── RandomNumberProviderImpl.kt
│ │ │ └── test
│ │ │ └── TestRandomNumberProvider.kt
│ │ └── test
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── core
│ │ └── common
│ │ └── extensions
│ │ ├── FormatNumberTest.kt
│ │ └── ImmutableExtensionsKtTest.kt
├── data-test
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── core
│ │ └── data
│ │ └── test
│ │ └── repository
│ │ ├── FakeAppDrawerRepo.kt
│ │ ├── FakeClockRepo.kt
│ │ ├── FakeFavoritesRepo.kt
│ │ ├── FakeHiddenAppsRepo.kt
│ │ ├── FakeLunarPhaseDetailsRepo.kt
│ │ ├── FakePlacesRepo.kt
│ │ ├── FakeQuotesRepo.kt
│ │ ├── FakeThemeRepo.kt
│ │ └── settings
│ │ ├── FakeAppDrawerSettingsRepo.kt
│ │ ├── FakeClockSettingsRepo.kt
│ │ ├── FakeGeneralSettingsRepo.kt
│ │ ├── FakeLunarPhaseSettingsRepo.kt
│ │ └── FakeQuotesSettingsRepo.kt
├── data
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── schemas
│ │ └── dev.mslalith.focuslauncher.core.data.database.AppDatabase
│ │ │ ├── 1.json
│ │ │ ├── 2.json
│ │ │ └── 3.json
│ └── src
│ │ ├── main
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── mslalith
│ │ │ └── focuslauncher
│ │ │ └── core
│ │ │ └── data
│ │ │ ├── database
│ │ │ ├── AppDatabase.kt
│ │ │ ├── dao
│ │ │ │ ├── AppsDao.kt
│ │ │ │ ├── FavoriteAppsDao.kt
│ │ │ │ ├── HiddenAppsDao.kt
│ │ │ │ ├── PlacesDao.kt
│ │ │ │ └── QuotesDao.kt
│ │ │ ├── entities
│ │ │ │ ├── AppRoom.kt
│ │ │ │ ├── FavoriteAppRoom.kt
│ │ │ │ ├── HiddenAppRoom.kt
│ │ │ │ ├── PlaceRoom.kt
│ │ │ │ └── QuoteRoom.kt
│ │ │ ├── typeconverter
│ │ │ │ └── Converters.kt
│ │ │ └── usecase
│ │ │ │ ├── datastore
│ │ │ │ ├── ClearDataStoreUseCase.kt
│ │ │ │ └── ClearThemeDataStoreUseCase.kt
│ │ │ │ └── room
│ │ │ │ └── CloseDatabaseUseCase.kt
│ │ │ ├── di
│ │ │ └── modules
│ │ │ │ ├── DataStoreModule.kt
│ │ │ │ ├── NetworkModule.kt
│ │ │ │ ├── RepositoryModule.kt
│ │ │ │ ├── RoomModule.kt
│ │ │ │ └── test
│ │ │ │ ├── TestNetworkModule.kt
│ │ │ │ ├── TestRepositoryModule.kt
│ │ │ │ └── TestRoomModule.kt
│ │ │ ├── dto
│ │ │ ├── AddressDto.kt
│ │ │ ├── AppDto.kt
│ │ │ ├── PlaceDto.kt
│ │ │ └── QuoteDto.kt
│ │ │ ├── network
│ │ │ ├── api
│ │ │ │ ├── PlacesApi.kt
│ │ │ │ ├── QuotesApi.kt
│ │ │ │ ├── fakes
│ │ │ │ │ ├── FakePlacesApi.kt
│ │ │ │ │ └── FakeQuotesApi.kt
│ │ │ │ └── impl
│ │ │ │ │ ├── PlacesApiImpl.kt
│ │ │ │ │ └── QuotesApiImpl.kt
│ │ │ └── entities
│ │ │ │ ├── PlaceResponse.kt
│ │ │ │ └── QuoteResponse.kt
│ │ │ ├── repository
│ │ │ ├── AppDrawerRepo.kt
│ │ │ ├── ClockRepo.kt
│ │ │ ├── FavoritesRepo.kt
│ │ │ ├── HiddenAppsRepo.kt
│ │ │ ├── LunarPhaseDetailsRepo.kt
│ │ │ ├── PlacesRepo.kt
│ │ │ ├── QuotesRepo.kt
│ │ │ ├── ThemeRepo.kt
│ │ │ ├── impl
│ │ │ │ ├── AppDrawerRepoImpl.kt
│ │ │ │ ├── ClockRepoImpl.kt
│ │ │ │ ├── FavoritesRepoImpl.kt
│ │ │ │ ├── HiddenAppsRepoImpl.kt
│ │ │ │ ├── LunarPhaseDetailsRepoImpl.kt
│ │ │ │ ├── PlacesRepoImpl.kt
│ │ │ │ ├── QuotesRepoImpl.kt
│ │ │ │ ├── ThemeRepoImpl.kt
│ │ │ │ └── settings
│ │ │ │ │ ├── AppDrawerSettingsRepoImpl.kt
│ │ │ │ │ ├── ClockSettingsRepoImpl.kt
│ │ │ │ │ ├── GeneralSettingsRepoImpl.kt
│ │ │ │ │ ├── LunarPhaseSettingsRepoImpl.kt
│ │ │ │ │ └── QuotesSettingsRepoImpl.kt
│ │ │ └── settings
│ │ │ │ ├── AppDrawerSettingsRepo.kt
│ │ │ │ ├── ClockSettingsRepo.kt
│ │ │ │ ├── GeneralSettingsRepo.kt
│ │ │ │ ├── LunarPhaseSettingsRepo.kt
│ │ │ │ └── QuotesSettingsRepo.kt
│ │ │ └── utils
│ │ │ ├── PlacesRepoHelpers.kt
│ │ │ └── QuotesRepoHelpers.kt
│ │ └── test
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── core
│ │ └── data
│ │ ├── database
│ │ ├── MigrationTest.kt
│ │ └── usecase
│ │ │ └── room
│ │ │ └── CloseDatabaseUseCaseTest.kt
│ │ ├── helpers
│ │ └── SettingsRepoHelpers.kt
│ │ ├── network
│ │ └── api
│ │ │ └── impl
│ │ │ ├── PlacesApiImplTest.kt
│ │ │ └── QuotesApiImplTest.kt
│ │ └── repository
│ │ ├── AppDrawerRepoTest.kt
│ │ ├── ClockRepoTest.kt
│ │ ├── FavoritesRepoTest.kt
│ │ ├── HiddenAppsRepoTest.kt
│ │ ├── LunarPhaseDetailsRepoTest.kt
│ │ ├── PlacesRepoTest.kt
│ │ ├── QuotesRepoTest.kt
│ │ ├── ThemeRepoTest.kt
│ │ └── settings
│ │ ├── AppDrawerSettingsRepoTest.kt
│ │ ├── ClockSettingsRepoTest.kt
│ │ ├── GeneralSettingsRepoTest.kt
│ │ ├── LunarPhaseSettingsRepoTest.kt
│ │ └── QuotesSettingsRepoTest.kt
├── domain
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── mslalith
│ │ │ └── focuslauncher
│ │ │ └── core
│ │ │ └── domain
│ │ │ ├── PackageActionUseCase.kt
│ │ │ ├── appdrawer
│ │ │ └── GetAppDrawerAppsUseCase.kt
│ │ │ ├── apps
│ │ │ ├── GetAllAppsOnIconPackChangeUseCase.kt
│ │ │ ├── GetAppDrawerIconicAppsUseCase.kt
│ │ │ ├── GetFavoriteColoredAppsUseCase.kt
│ │ │ ├── GetIconPackIconicAppsUseCase.kt
│ │ │ └── core
│ │ │ │ ├── GetAppsIconPackAwareUseCase.kt
│ │ │ │ └── GetAppsUseCase.kt
│ │ │ ├── extensions
│ │ │ └── AppExtensions.kt
│ │ │ ├── iconpack
│ │ │ ├── FetchIconPacksUseCase.kt
│ │ │ ├── GetIconPackAppsUseCase.kt
│ │ │ ├── LoadIconPackUseCase.kt
│ │ │ ├── ReloadIconPackAfterFirstLoadUseCase.kt
│ │ │ └── ReloadIconPackUseCase.kt
│ │ │ ├── launcherapps
│ │ │ ├── GetDefaultFavoriteAppsUseCase.kt
│ │ │ └── LoadAllAppsUseCase.kt
│ │ │ ├── settings
│ │ │ └── UpdateReportCrashesSettingUseCase.kt
│ │ │ └── theme
│ │ │ ├── ChangeThemeUseCase.kt
│ │ │ └── GetThemeUseCase.kt
│ │ └── test
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── core
│ │ └── domain
│ │ ├── apps
│ │ ├── GetAllAppsOnIconPackChangeUseCaseTest.kt
│ │ ├── GetIconPackIconicAppsUseCaseTest.kt
│ │ └── PackageActionUseCaseTest.kt
│ │ ├── iconpack
│ │ ├── FetchIconPacksUseCaseTest.kt
│ │ ├── GetIconPackAppsUseCaseTest.kt
│ │ ├── LoadIconPackUseCaseTest.kt
│ │ ├── ReloadIconPackAfterFirstLoadUseCaseTest.kt
│ │ └── ReloadIconPackUseCaseTest.kt
│ │ ├── launcherapps
│ │ └── LoadAllAppsUseCaseTest.kt
│ │ ├── theme
│ │ ├── ChangeThemeUseCaseTest.kt
│ │ └── GetThemeUseCaseTest.kt
│ │ └── utils
│ │ └── Extensions.kt
├── launcherapps
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── mslalith
│ │ │ └── focuslauncher
│ │ │ └── core
│ │ │ └── launcherapps
│ │ │ ├── di
│ │ │ ├── LauncherAppsModule.kt
│ │ │ └── test
│ │ │ │ └── TestLauncherAppsModule.kt
│ │ │ ├── manager
│ │ │ ├── iconcache
│ │ │ │ ├── IconCacheManager.kt
│ │ │ │ └── IconCacheManagerImpl.kt
│ │ │ ├── iconpack
│ │ │ │ ├── IconPackManager.kt
│ │ │ │ ├── impl
│ │ │ │ │ └── IconPackManagerImpl.kt
│ │ │ │ └── test
│ │ │ │ │ └── TestIconPackManager.kt
│ │ │ └── launcherapps
│ │ │ │ ├── LauncherAppsManager.kt
│ │ │ │ ├── impl
│ │ │ │ └── LauncherAppsManagerImpl.kt
│ │ │ │ └── test
│ │ │ │ └── TestLauncherAppsManager.kt
│ │ │ ├── model
│ │ │ ├── DrawableInfo.kt
│ │ │ └── IconPack.kt
│ │ │ ├── parser
│ │ │ └── IconPackXmlParser.kt
│ │ │ ├── providers
│ │ │ └── icons
│ │ │ │ ├── IconProvider.kt
│ │ │ │ ├── impl
│ │ │ │ └── IconProviderImpl.kt
│ │ │ │ └── test
│ │ │ │ └── TestIconProvider.kt
│ │ │ └── utils
│ │ │ └── GoogleCalendarIcon.kt
│ │ └── test
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── core
│ │ └── launcherapps
│ │ └── providers
│ │ └── icons
│ │ └── impl
│ │ └── IconProviderImplTest.kt
├── lint
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── core
│ │ └── lint
│ │ ├── detekt
│ │ ├── IgnoreCyclomaticComplexMethod.kt
│ │ ├── IgnoreLongMethod.kt
│ │ └── IgnoreNestedBlockDepth.kt
│ │ └── kover
│ │ └── IgnoreInKoverReport.kt
├── model
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── core
│ │ └── model
│ │ ├── AppDrawerViewType.kt
│ │ ├── BuildFlavor.kt
│ │ ├── ClockAlignment.kt
│ │ ├── ConfirmSelectableItemType.kt
│ │ ├── Constants.kt
│ │ ├── CurrentPlace.kt
│ │ ├── IconPackLoadEvent.kt
│ │ ├── IconPackType.kt
│ │ ├── PackageAction.kt
│ │ ├── Quote.kt
│ │ ├── Theme.kt
│ │ ├── UiText.kt
│ │ ├── WidgetType.kt
│ │ ├── app
│ │ ├── App.kt
│ │ ├── AppWithColor.kt
│ │ ├── AppWithComponent.kt
│ │ ├── AppWithIcon.kt
│ │ ├── SelectedApp.kt
│ │ └── SelectedHiddenApp.kt
│ │ ├── appdrawer
│ │ ├── AppDrawerIconViewType.kt
│ │ └── AppDrawerItem.kt
│ │ ├── extensions
│ │ └── UtilityExtensions.kt
│ │ ├── location
│ │ └── LatLng.kt
│ │ ├── lunarphase
│ │ ├── LunarPhase.kt
│ │ ├── LunarPhaseDetails.kt
│ │ ├── LunarPhaseDirection.kt
│ │ ├── NextPhaseDetails.kt
│ │ ├── RiseAndSetDetails.kt
│ │ └── UpcomingLunarPhase.kt
│ │ └── place
│ │ ├── Address.kt
│ │ └── Place.kt
├── resources
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── res
│ │ ├── drawable
│ │ ├── ic_align_horizontal_center.xml
│ │ ├── ic_align_horizontal_left.xml
│ │ ├── ic_align_horizontal_right.xml
│ │ ├── ic_app_drawer_colored.xml
│ │ ├── ic_app_drawer_icons.xml
│ │ ├── ic_app_drawer_text.xml
│ │ ├── ic_arrow_left.xml
│ │ ├── ic_broom.xml
│ │ ├── ic_check.xml
│ │ ├── ic_close.xml
│ │ ├── ic_delete.xml
│ │ ├── ic_device_mobile.xml
│ │ ├── ic_drag_indicator.xml
│ │ ├── ic_edit.xml
│ │ ├── ic_grid.xml
│ │ ├── ic_hand_swipe_left.xml
│ │ ├── ic_heart.xml
│ │ ├── ic_house.xml
│ │ ├── ic_info.xml
│ │ ├── ic_launcher.xml
│ │ ├── ic_launcher_foreground.xml
│ │ ├── ic_list.xml
│ │ ├── ic_logo_github.xml
│ │ ├── ic_logo_phosphor.xml
│ │ ├── ic_logo_twitter.xml
│ │ ├── ic_map_pin.xml
│ │ ├── ic_map_pin_line.xml
│ │ ├── ic_moon_stars.xml
│ │ ├── ic_quote.xml
│ │ ├── ic_search.xml
│ │ ├── ic_star.xml
│ │ ├── ic_star_outline.xml
│ │ ├── ic_sun_dim.xml
│ │ └── ic_visibility_off.xml
│ │ └── values
│ │ ├── ic_launcher_background.xml
│ │ └── strings.xml
├── screens
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── core
│ │ └── screens
│ │ ├── BottomSheetScreens.kt
│ │ ├── Screens.kt
│ │ └── UiComponentScreens.kt
├── settings
│ └── sentry
│ │ ├── build.gradle.kts
│ │ └── src
│ │ └── main
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── core
│ │ └── settings
│ │ └── sentry
│ │ ├── FakeSentrySettings.kt
│ │ ├── SentrySettings.kt
│ │ ├── SentrySettingsImpl.kt
│ │ └── di
│ │ ├── SentryModule.kt
│ │ └── TestSentryModule.kt
├── testing-circuit
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── core
│ │ └── testing
│ │ └── circuit
│ │ └── PresenterTest.kt
├── testing-compose
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── core
│ │ └── testing
│ │ └── compose
│ │ ├── TestSemanticsProperties.kt
│ │ ├── assertion
│ │ ├── AssertBiasAlignment.kt
│ │ ├── AssertPrimitiveTypes.kt
│ │ ├── AssertSelectedApp.kt
│ │ ├── AssertSelectedHiddenApp.kt
│ │ ├── AssertWidgetType.kt
│ │ └── WaitAndAssertIsDisplayed.kt
│ │ ├── extensions
│ │ └── GeneralExtensions.kt
│ │ ├── matcher
│ │ ├── GeneralMatchers.kt
│ │ ├── MatcherPrimitiveTypes.kt
│ │ ├── MatcherSelectedApp.kt
│ │ ├── MatcherSelectedHiddenApp.kt
│ │ └── MatcherWidgetType.kt
│ │ ├── modifier
│ │ └── testsemantics
│ │ │ ├── TestSemanticsModifier.kt
│ │ │ ├── TestSemanticsScope.kt
│ │ │ └── impl
│ │ │ └── TestSemanticsScopeImpl.kt
│ │ └── waiter
│ │ ├── GeneralWaiters.kt
│ │ ├── WaitPrimitiveTypes.kt
│ │ └── WaitSelectedApps.kt
├── testing
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── core
│ │ └── testing
│ │ ├── AppRobolectricTestRunner.kt
│ │ ├── CoroutineTest.kt
│ │ ├── HiltTestRunner.kt
│ │ ├── KtorApiTest.kt
│ │ ├── TestApps.kt
│ │ ├── extensions
│ │ ├── DateTimeExtensions.kt
│ │ ├── LocaleExtensions.kt
│ │ ├── TurbineAssertExtensions.kt
│ │ └── TurbineExtensions.kt
│ │ └── rules
│ │ └── TestCoroutineRule.kt
└── ui
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ └── main
│ └── kotlin
│ └── dev
│ └── mslalith
│ └── focuslauncher
│ └── core
│ └── ui
│ ├── ActionButton.kt
│ ├── AppBarWithBackIcon.kt
│ ├── Blob.kt
│ ├── ChooserGroup.kt
│ ├── ConfirmSelectableItem.kt
│ ├── DotWaveLoader.kt
│ ├── RoundIcon.kt
│ ├── SearchField.kt
│ ├── SelectableCheckboxItem.kt
│ ├── SelectableIconItem.kt
│ ├── Spacer.kt
│ ├── TextButton.kt
│ ├── TextIconButton.kt
│ ├── controller
│ └── SystemUiController.kt
│ ├── effects
│ ├── OnDayChangeListener.kt
│ ├── OnLifecycleEventChange.kt
│ ├── PackageActionListener.kt
│ └── SystemBroadcastReceiver.kt
│ ├── extensions
│ ├── ColorExtensions.kt
│ ├── ModifierExtensions.kt
│ ├── ScaffoldExtensions.kt
│ └── UiTextExtensions.kt
│ ├── modifiers
│ ├── HorizontalFadeOutEdge.kt
│ └── VerticalFadeOutEdge.kt
│ ├── providers
│ ├── ProvideLauncherPagerState.kt
│ └── ProvideSystemUiController.kt
│ ├── remember
│ └── RememberAppColor.kt
│ └── settings
│ ├── SettingsLoadableItem.kt
│ ├── SettingsSelectableBottomContentItem.kt
│ ├── SettingsSelectableChooserItem.kt
│ ├── SettingsSelectableItem.kt
│ ├── SettingsSelectableSliderItem.kt
│ └── SettingsSelectableSwitchItem.kt
├── feature
├── appdrawerpage
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── mslalith
│ │ │ └── focuslauncher
│ │ │ └── feature
│ │ │ └── appdrawerpage
│ │ │ ├── AppDrawerPage.kt
│ │ │ ├── AppDrawerPageContract.kt
│ │ │ ├── AppDrawerPagePresenter.kt
│ │ │ ├── apps
│ │ │ ├── grid
│ │ │ │ ├── AppDrawerGridItem.kt
│ │ │ │ ├── AppsGrid.kt
│ │ │ │ └── PreviewAppsGrid.kt
│ │ │ └── list
│ │ │ │ ├── AppDrawerListItem.kt
│ │ │ │ ├── AppsList.kt
│ │ │ │ ├── CharacterHeader.kt
│ │ │ │ └── GroupedAppsList.kt
│ │ │ ├── bottomsheet
│ │ │ ├── moreoptions
│ │ │ │ ├── AppMoreOptionsBottomSheet.kt
│ │ │ │ ├── AppMoreOptionsBottomSheetContract.kt
│ │ │ │ └── AppMoreOptionsBottomSheetPresenter.kt
│ │ │ └── updateappdisplayname
│ │ │ │ ├── UpdateAppDisplayNameBottomSheet.kt
│ │ │ │ ├── UpdateAppDisplayNameBottomSheetContract.kt
│ │ │ │ └── UpdateAppDisplayNameBottomSheetPresenter.kt
│ │ │ └── utils
│ │ │ └── Constants.kt
│ │ └── test
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── feature
│ │ └── appdrawerpage
│ │ ├── AppDrawerPagePresenterTest.kt
│ │ └── bottomsheet
│ │ ├── moreoptions
│ │ └── AppMoreOptionsBottomSheetPresenterTest.kt
│ │ └── updateappdisplayname
│ │ └── UpdateAppDisplayNameBottomSheetPresenterTest.kt
├── clock24
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── mslalith
│ │ │ └── focuslauncher
│ │ │ └── feature
│ │ │ └── clock24
│ │ │ ├── bottomsheet
│ │ │ └── clockwidgetsettings
│ │ │ │ ├── ClockWidgetSettingsBottomSheet.kt
│ │ │ │ ├── ClockWidgetSettingsBottomSheetContract.kt
│ │ │ │ └── ClockWidgetSettingsBottomSheetPresenter.kt
│ │ │ ├── model
│ │ │ ├── AnalogClockHandlePhase.kt
│ │ │ ├── AnalogClockPhase.kt
│ │ │ └── Digit.kt
│ │ │ ├── settings
│ │ │ └── PreviewClock.kt
│ │ │ ├── utils
│ │ │ └── TestTags.kt
│ │ │ └── widget
│ │ │ ├── ClockWidgetUiComponent.kt
│ │ │ ├── ClockWidgetUiComponentContract.kt
│ │ │ ├── ClockWidgetUiComponentPresenter.kt
│ │ │ └── ui
│ │ │ ├── Clock24.kt
│ │ │ └── CurrentTime.kt
│ │ └── test
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── feature
│ │ └── clock24
│ │ ├── ClockWidgetKtTest.kt
│ │ ├── CurrentTimeKtTest.kt
│ │ ├── bottomsheet
│ │ └── clockwidgetsettings
│ │ │ └── ClockWidgetSettingsBottomSheetPresenterTest.kt
│ │ └── widget
│ │ └── ClockWidgetUiComponentPresenterTest.kt
├── favorites
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── mslalith
│ │ │ └── focuslauncher
│ │ │ └── feature
│ │ │ └── favorites
│ │ │ ├── FavoritesListUiComponent.kt
│ │ │ ├── FavoritesListUiComponentContract.kt
│ │ │ ├── FavoritesListUiComponentPresenter.kt
│ │ │ ├── bottomsheet
│ │ │ ├── DismissibleFavoriteItem.kt
│ │ │ ├── FavoritesBottomSheet.kt
│ │ │ ├── FavoritesBottomSheetContract.kt
│ │ │ └── FavoritesBottomSheetPresenter.kt
│ │ │ └── ui
│ │ │ └── StaggeredFlowRow.kt
│ │ └── test
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── feature
│ │ └── favorites
│ │ ├── FavoritesListUiComponentPresenterTest.kt
│ │ └── bottomsheet
│ │ └── FavoritesBottomSheetPresenterTest.kt
├── homepage
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── feature
│ │ └── homepage
│ │ ├── DecoratedLunarCalendar.kt
│ │ ├── DecoratedQuote.kt
│ │ ├── HomePage.kt
│ │ ├── HomePageContract.kt
│ │ ├── HomePagePresenter.kt
│ │ └── model
│ │ └── HomePadding.kt
├── lunarcalendar
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── mslalith
│ │ │ └── focuslauncher
│ │ │ └── feature
│ │ │ └── lunarcalendar
│ │ │ ├── bottomsheet
│ │ │ ├── lunarphasedetails
│ │ │ │ ├── LunarPhaseDetailsBottomSheet.kt
│ │ │ │ ├── LunarPhaseDetailsBottomSheetContract.kt
│ │ │ │ ├── LunarPhaseDetailsBottomSheetPresenter.kt
│ │ │ │ └── ui
│ │ │ │ │ ├── LunarRiseAndSetDetails.kt
│ │ │ │ │ ├── NextMajorPhaseDetails.kt
│ │ │ │ │ ├── NextSingleMajorPhaseDetails.kt
│ │ │ │ │ ├── RiseAndSetHeaders.kt
│ │ │ │ │ ├── RiseAndSetIndicator.kt
│ │ │ │ │ ├── RiseAndSetTime.kt
│ │ │ │ │ ├── RiseTimeDetails.kt
│ │ │ │ │ ├── SetTimeDetails.kt
│ │ │ │ │ ├── TodayLunarMoonIconAndPhase.kt
│ │ │ │ │ ├── TodayLunarMoonPhaseDetails.kt
│ │ │ │ │ └── TodayLunarPhase.kt
│ │ │ └── lunarphasewidgetsettings
│ │ │ │ ├── LunarPhaseWidgetSettingsBottomSheet.kt
│ │ │ │ ├── LunarPhaseWidgetSettingsBottomSheetContract.kt
│ │ │ │ └── LunarPhaseWidgetSettingsBottomSheetPresenter.kt
│ │ │ ├── settings
│ │ │ └── PreviewLunarCalendar.kt
│ │ │ ├── shared
│ │ │ └── LunarPhaseMoonIcon.kt
│ │ │ └── widget
│ │ │ ├── LunarCalendarUiComponent.kt
│ │ │ ├── LunarCalendarUiComponentContract.kt
│ │ │ ├── LunarCalendarUiComponentPresenter.kt
│ │ │ └── ui
│ │ │ ├── LunarCalendarContent.kt
│ │ │ ├── LunarPhaseName.kt
│ │ │ └── UpcomingLunarPhaseDetails.kt
│ │ └── test
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── feature
│ │ └── lunarcalendar
│ │ ├── bottomsheet
│ │ ├── lunarphasedetails
│ │ │ └── LunarPhaseDetailsBottomSheetPresenterTest.kt
│ │ └── lunarphasewidgetsettings
│ │ │ └── LunarPhaseWidgetSettingsBottomSheetPresenterTest.kt
│ │ └── widget
│ │ └── LunarCalendarUiComponentPresenterTest.kt
├── quoteforyou
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── mslalith
│ │ │ └── focuslauncher
│ │ │ └── feature
│ │ │ └── quoteforyou
│ │ │ ├── bottomsheet
│ │ │ └── quotewidgetsettings
│ │ │ │ ├── QuoteWidgetSettingsBottomSheet.kt
│ │ │ │ ├── QuoteWidgetSettingsBottomSheetContract.kt
│ │ │ │ └── QuoteWidgetSettingsBottomSheetPresenter.kt
│ │ │ ├── settings
│ │ │ └── PreviewQuotes.kt
│ │ │ └── widget
│ │ │ ├── QuoteForYouUiComponent.kt
│ │ │ ├── QuoteForYouUiComponentContract.kt
│ │ │ ├── QuoteForYouUiComponentPresenter.kt
│ │ │ └── ui
│ │ │ └── QuoteForYouContent.kt
│ │ └── test
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── feature
│ │ └── quoteforyou
│ │ ├── bottomsheet
│ │ └── quotewidgetsettings
│ │ │ └── QuoteWidgetSettingsBottomSheetPresenterTest.kt
│ │ └── widget
│ │ └── QuoteForYouUiComponentPresenterTest.kt
├── settingspage
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── mslalith
│ │ │ └── focuslauncher
│ │ │ └── feature
│ │ │ └── settingspage
│ │ │ ├── SettingsPage.kt
│ │ │ ├── SettingsPageContract.kt
│ │ │ ├── SettingsPagePresenter.kt
│ │ │ ├── bottomsheet
│ │ │ ├── appdrawersettings
│ │ │ │ ├── AppDrawerSettingsBottomSheet.kt
│ │ │ │ ├── AppDrawerSettingsBottomSheetContract.kt
│ │ │ │ └── AppDrawerSettingsBottomSheetPresenter.kt
│ │ │ └── privacy
│ │ │ │ ├── PrivacySettingsBottomSheet.kt
│ │ │ │ ├── PrivacySettingsBottomSheetContract.kt
│ │ │ │ └── PrivacySettingsBottomSheetPresenter.kt
│ │ │ ├── settingsitems
│ │ │ ├── About.kt
│ │ │ ├── AppDrawer.kt
│ │ │ ├── ChangeTheme.kt
│ │ │ ├── Developer.kt
│ │ │ ├── EditFavorites.kt
│ │ │ ├── HideApps.kt
│ │ │ ├── IconPack.kt
│ │ │ ├── Privacy.kt
│ │ │ ├── PullDownNotifications.kt
│ │ │ ├── SetAsDefaultLauncher.kt
│ │ │ ├── SettingsHeader.kt
│ │ │ ├── ToggleStatusBar.kt
│ │ │ └── Widgets.kt
│ │ │ ├── shared
│ │ │ ├── SettingsExpandableItem.kt
│ │ │ ├── SettingsGridContent.kt
│ │ │ ├── SettingsGridItem.kt
│ │ │ └── SettingsItem.kt
│ │ │ └── utils
│ │ │ ├── Extensions.kt
│ │ │ └── TestTags.kt
│ │ └── test
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── feature
│ │ └── settingspage
│ │ ├── AppDrawerSettingsSheetKtTest.kt
│ │ ├── SettingsPageKtTest.kt
│ │ ├── SettingsPagePresenterTest.kt
│ │ └── bottomsheet
│ │ ├── appdrawersettings
│ │ └── AppDrawerSettingsBottomSheetPresenterTest.kt
│ │ └── privacy
│ │ └── PrivacySettingsBottomSheetPresenterTest.kt
└── theme
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ ├── main
│ ├── kotlin
│ │ └── dev
│ │ │ └── mslalith
│ │ │ └── focuslauncher
│ │ │ └── feature
│ │ │ └── theme
│ │ │ ├── LauncherTheme.kt
│ │ │ ├── LauncherThemeContract.kt
│ │ │ ├── LauncherThemePresenter.kt
│ │ │ ├── bottomsheet
│ │ │ └── themeselection
│ │ │ │ ├── ThemeSelectionBottomSheet.kt
│ │ │ │ ├── ThemeSelectionBottomSheetContract.kt
│ │ │ │ └── ThemeSelectionBottomSheetPresenter.kt
│ │ │ ├── data
│ │ │ ├── Color.kt
│ │ │ ├── ColorScheme.kt
│ │ │ └── Type.kt
│ │ │ └── model
│ │ │ └── ThemeWithIcon.kt
│ └── res
│ │ └── font
│ │ └── nunitosans_regular.ttf
│ └── test
│ └── kotlin
│ └── dev
│ └── mslalith
│ └── focuslauncher
│ └── feature
│ └── theme
│ ├── LauncherThemePresenterTest.kt
│ └── bottomsheet
│ └── themeselection
│ └── ThemeSelectionBottomSheetPresenterTest.kt
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
├── animated_clock.mp4
├── change_theme.mp4
├── edit_favorites.mp4
├── hide_apps.mp4
├── icon_pack.mp4
├── lunar_phase_info.mp4
└── what_is.png
├── privacy-policy.html
├── screens
├── about
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── mslalith
│ │ │ └── focuslauncher
│ │ │ └── screens
│ │ │ └── about
│ │ │ ├── About.kt
│ │ │ ├── AboutContract.kt
│ │ │ ├── AboutPresenter.kt
│ │ │ └── ui
│ │ │ ├── AppInfo.kt
│ │ │ ├── Credits.kt
│ │ │ ├── MadeWithLove.kt
│ │ │ └── SocialAccounts.kt
│ │ └── test
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── screens
│ │ └── about
│ │ └── AboutPresenterTest.kt
├── currentplace
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── mslalith
│ │ │ └── focuslauncher
│ │ │ └── screens
│ │ │ └── currentplace
│ │ │ ├── CurrentPlace.kt
│ │ │ ├── CurrentPlaceContract.kt
│ │ │ ├── CurrentPlacePresenter.kt
│ │ │ └── ui
│ │ │ ├── CurrentPlaceInfo.kt
│ │ │ ├── MapView.kt
│ │ │ └── interop
│ │ │ └── AndroidMapView.kt
│ │ └── test
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── screens
│ │ └── currentplace
│ │ └── CurrentPlacePresenterTest.kt
├── developer
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── screens
│ │ └── developer
│ │ ├── Developer.kt
│ │ ├── DeveloperContract.kt
│ │ ├── DeveloperPresenter.kt
│ │ └── file
│ │ ├── CacheFileInteractor.kt
│ │ └── FavoritesCacheFileInteractor.kt
├── editfavorites
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── mslalith
│ │ │ └── focuslauncher
│ │ │ └── screens
│ │ │ └── editfavorites
│ │ │ ├── EditFavorites.kt
│ │ │ ├── EditFavoritesContract.kt
│ │ │ ├── EditFavoritesPresenter.kt
│ │ │ ├── ui
│ │ │ ├── FavoriteListItem.kt
│ │ │ ├── FavoritesList.kt
│ │ │ └── HiddenAppActionText.kt
│ │ │ └── utils
│ │ │ └── TestTags.kt
│ │ └── test
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── screens
│ │ └── editfavorites
│ │ ├── EditFavoritesPresenterTest.kt
│ │ └── EditFavoritesScreenKtTest.kt
├── hideapps
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── mslalith
│ │ │ └── focuslauncher
│ │ │ └── screens
│ │ │ └── hideapps
│ │ │ ├── HideApps.kt
│ │ │ ├── HideAppsContract.kt
│ │ │ ├── HideAppsPresenter.kt
│ │ │ ├── ui
│ │ │ ├── HiddenAppListItem.kt
│ │ │ └── HiddenAppsList.kt
│ │ │ └── utils
│ │ │ └── TestTags.kt
│ │ └── test
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── screens
│ │ └── hideapps
│ │ ├── HideAppsPresenterTest.kt
│ │ └── HideAppsScreenKtTest.kt
├── iconpack
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ └── kotlin
│ │ │ └── dev
│ │ │ └── mslalith
│ │ │ └── focuslauncher
│ │ │ └── screens
│ │ │ └── iconpack
│ │ │ ├── IconPack.kt
│ │ │ ├── IconPackContract.kt
│ │ │ └── IconPackPresenter.kt
│ │ └── test
│ │ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── screens
│ │ └── iconpack
│ │ └── IconPackPresenterTest.kt
└── launcher
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ ├── main
│ └── kotlin
│ │ └── dev
│ │ └── mslalith
│ │ └── focuslauncher
│ │ └── screens
│ │ └── launcher
│ │ ├── Launcher.kt
│ │ ├── LauncherContract.kt
│ │ └── LauncherPresenter.kt
│ └── test
│ └── kotlin
│ └── dev
│ └── mslalith
│ └── focuslauncher
│ └── screens
│ └── launcher
│ └── LauncherPresenterTest.kt
├── scripts
└── pre-push
└── settings.gradle.kts
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*.{kt,kts}]
4 | charset = utf-8
5 | max_line_length = 200
6 |
--------------------------------------------------------------------------------
/.github/dependabot.yml.bkp:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "gradle"
4 | directory: "/"
5 | schedule:
6 | interval: "daily"
7 | target-branch: "develop"
8 | reviewers:
9 | - "mslalith"
10 |
--------------------------------------------------------------------------------
/.github/release-drafter.yml:
--------------------------------------------------------------------------------
1 | name-template: 'v$RESOLVED_VERSION'
2 | tag-template: 'v$RESOLVED_VERSION'
3 | template: |
4 | # What's Changed
5 |
6 | $CHANGES
7 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:base"
5 | ],
6 | "baseBranches": [
7 | "develop"
8 | ],
9 | "timezone": "Asia/Kolkata",
10 | "assignees": [
11 | "mslalith"
12 | ],
13 | "packageRules": [
14 | {
15 | "groupName": "Kotlin",
16 | "groupSlug": "kotlin",
17 | "matchPackagePrefixes": [
18 | "com.google.devtools.ksp",
19 | "androidx.compose.compiler"
20 | ],
21 | "matchPackagePatterns": [
22 | "org.jetbrains.kotlin.*"
23 | ]
24 | }
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/.github/workflows/draft.yml:
--------------------------------------------------------------------------------
1 | name: Release Drafter
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches:
7 | - main
8 | pull_request_target:
9 | types: [ opened, reopened, synchronize ]
10 |
11 | jobs:
12 | update_release_draft:
13 | permissions:
14 | contents: write
15 | pull-requests: read
16 | runs-on: ubuntu-latest
17 | steps:
18 | - uses: release-drafter/release-drafter@v6.0.0
19 | env:
20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | assets/
2 | build-cache
3 |
4 | # Android Studio provided
5 | .DS_Store
6 | /app/release
7 |
8 | # Created by https://www.toptal.com/developers/gitignore/api/android
9 | # Edit at https://www.toptal.com/developers/gitignore?templates=android
10 |
11 | ### Android ###
12 | # Gradle files
13 | .gradle/
14 | build/
15 |
16 | # Local configuration file (sdk path, etc)
17 | local.properties
18 |
19 | # Log/OS Files
20 | *.log
21 |
22 | # Android Studio generated files and folders
23 | captures/
24 | .externalNativeBuild/
25 | .cxx/
26 | *.apk
27 | output.json
28 |
29 | # IntelliJ
30 | *.iml
31 | .idea/
32 |
33 | # Keystore files
34 | *.jks
35 | *.keystore
36 |
37 | # Google Services (e.g. APIs or Firebase)
38 | app/google-services.json
39 |
40 | # Android Profiling
41 | *.hprof
42 |
43 | ### Android Patch ###
44 | gen-external-apklibs
45 |
46 | # Replacement of .externalNativeBuild directories introduced
47 | # with Android Studio 3.5.
48 |
49 | # End of https://www.toptal.com/developers/gitignore/api/android
50 |
51 | # Sentry Config File
52 | sentry.properties
53 |
--------------------------------------------------------------------------------
/.run/Generate Compose Metrics.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
16 |
17 |
18 | true
19 | true
20 | false
21 |
22 |
23 |
--------------------------------------------------------------------------------
/.run/Kover Html Debug Report.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | true
20 | true
21 | false
22 | false
23 |
24 |
25 |
--------------------------------------------------------------------------------
/.run/Run all tests.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | true
21 | true
22 | false
23 | false
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | -keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
23 | # Firebase
24 | #-keep class com.google.firebase.** { *; }
25 |
26 | -dontwarn edu.umd.cs.findbugs.annotations.Nullable
27 | -dontwarn org.slf4j.impl.StaticLoggerBinder
28 |
29 |
--------------------------------------------------------------------------------
/app/src/dev/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/dev/res/mipmap-anydpi/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/dev/res/mipmap-anydpi/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/dev/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #313131
4 |
--------------------------------------------------------------------------------
/app/src/dev/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Focus Launcher (Dev)
4 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/kotlin/dev/mslalith/focuslauncher/FocusLauncherApp.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher
2 |
3 | import android.app.Application
4 | import androidx.lifecycle.ProcessLifecycleOwner
5 | import androidx.lifecycle.lifecycleScope
6 | import dagger.hilt.android.HiltAndroidApp
7 | import dev.mslalith.focuslauncher.core.lint.kover.IgnoreInKoverReport
8 | import dev.mslalith.focuslauncher.core.settings.sentry.SentrySettings
9 | import kotlinx.coroutines.flow.first
10 | import kotlinx.coroutines.launch
11 | import javax.inject.Inject
12 |
13 | @HiltAndroidApp
14 | @IgnoreInKoverReport
15 | class FocusLauncherApp : Application() {
16 |
17 | @Inject
18 | lateinit var sentrySettings: SentrySettings
19 |
20 | override fun onCreate() {
21 | super.onCreate()
22 |
23 | setupSentry()
24 | }
25 |
26 | private fun setupSentry() = with(sentrySettings) {
27 | ProcessLifecycleOwner.get().lifecycleScope.launch {
28 | if (isEnabled.first()) enableSentry() else disableSentry()
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/dev/mslalith/focuslauncher/di/CircuitModule.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.di
2 |
3 | import com.slack.circuit.foundation.Circuit
4 | import com.slack.circuit.runtime.presenter.Presenter
5 | import com.slack.circuit.runtime.ui.Ui
6 | import dagger.Module
7 | import dagger.Provides
8 | import dagger.hilt.InstallIn
9 | import dagger.hilt.components.SingletonComponent
10 | import javax.inject.Singleton
11 |
12 | @Module
13 | @InstallIn(SingletonComponent::class)
14 | object CircuitModule {
15 |
16 | @Provides
17 | @Singleton
18 | fun provideCircuit(
19 | presenterFactories: @JvmSuppressWildcards Set,
20 | uiFactories: @JvmSuppressWildcards Set
21 | ): Circuit = Circuit.Builder()
22 | .addPresenterFactories(presenterFactories)
23 | .addUiFactories(uiFactories)
24 | .build()
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #313131
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/baselineprofile/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/baselineprofile/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/baselineprofile/src/main/kotlin/baselineprofile/extensions/LauncherScreenExtensions.kt:
--------------------------------------------------------------------------------
1 | package baselineprofile.extensions
2 |
3 | import androidx.benchmark.macro.MacrobenchmarkScope
4 | import androidx.test.uiautomator.By
5 | import androidx.test.uiautomator.Direction
6 | import androidx.test.uiautomator.Until
7 |
8 | internal fun MacrobenchmarkScope.gotoSettings() {
9 | device.rootObject.fling(Direction.LEFT)
10 | device.wait(Until.hasObject(By.text("Change Theme")), 5_000)
11 | }
12 |
13 | internal fun MacrobenchmarkScope.gotoHomeFromSettings() {
14 | device.rootObject.fling(Direction.RIGHT)
15 | device.wait(Until.hasObject(By.text("Full Moon")), 5_000)
16 | }
17 |
18 | internal fun MacrobenchmarkScope.gotoHomeFromAppDrawer() {
19 | device.rootObject.fling(Direction.LEFT)
20 | device.wait(Until.hasObject(By.text("Full Moon")), 5_000)
21 | }
22 |
23 | internal fun MacrobenchmarkScope.gotoAppDrawer() {
24 | device.rootObject.fling(Direction.RIGHT)
25 | device.wait(Until.hasObject(By.text("Chrome")), 5_000)
26 | }
27 |
--------------------------------------------------------------------------------
/baselineprofile/src/main/kotlin/baselineprofile/extensions/UiDeviceExtensions.kt:
--------------------------------------------------------------------------------
1 | package baselineprofile.extensions
2 |
3 | import androidx.test.uiautomator.By
4 | import androidx.test.uiautomator.UiDevice
5 | import androidx.test.uiautomator.UiObject2
6 |
7 | internal val UiDevice.rootObject: UiObject2
8 | get() = findObject(By.scrollable(true))
9 |
--------------------------------------------------------------------------------
/build-logic/convention/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt:
--------------------------------------------------------------------------------
1 | import com.android.build.api.dsl.ApplicationExtension
2 | import dev.mslalith.focuslauncher.configureAndroidCompose
3 | import org.gradle.api.Plugin
4 | import org.gradle.api.Project
5 | import org.gradle.kotlin.dsl.getByType
6 |
7 | class AndroidApplicationComposeConventionPlugin : Plugin {
8 | override fun apply(target: Project) = with(target) {
9 | pluginManager.apply("com.android.application")
10 | val extension = extensions.getByType()
11 | configureAndroidCompose(commonExtension = extension)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt:
--------------------------------------------------------------------------------
1 | import com.android.build.api.dsl.ApplicationExtension
2 | import dev.mslalith.focuslauncher.configureKotlinAndroid
3 | import org.gradle.api.Plugin
4 | import org.gradle.api.Project
5 | import org.gradle.api.artifacts.VersionCatalogsExtension
6 | import org.gradle.kotlin.dsl.configure
7 | import org.gradle.kotlin.dsl.getByType
8 |
9 | class AndroidApplicationConventionPlugin : Plugin {
10 | override fun apply(target: Project) = with(target) {
11 | val libs = extensions.getByType().named("libs")
12 |
13 | with(pluginManager) {
14 | apply("com.android.application")
15 | apply("org.jetbrains.kotlin.android")
16 | apply("focuslauncher.lint")
17 | }
18 |
19 | extensions.configure {
20 | configureKotlinAndroid(commonExtension = this)
21 | defaultConfig.targetSdk = libs.findVersion("androidTargetSdk").get().requiredVersion.toInt()
22 | buildFeatures.buildConfig = true
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt:
--------------------------------------------------------------------------------
1 | import com.android.build.gradle.LibraryExtension
2 | import dev.mslalith.focuslauncher.configureAndroidCompose
3 | import org.gradle.api.Plugin
4 | import org.gradle.api.Project
5 | import org.gradle.kotlin.dsl.getByType
6 |
7 | class AndroidLibraryComposeConventionPlugin : Plugin {
8 | override fun apply(target: Project) {
9 | with(target) {
10 | pluginManager.apply("com.android.library")
11 | val extension = extensions.getByType()
12 | configureAndroidCompose(commonExtension = extension)
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt:
--------------------------------------------------------------------------------
1 | import com.android.build.gradle.LibraryExtension
2 | import dev.mslalith.focuslauncher.configureKotlinAndroid
3 | import org.gradle.api.Plugin
4 | import org.gradle.api.Project
5 | import org.gradle.api.artifacts.VersionCatalogsExtension
6 | import org.gradle.kotlin.dsl.configure
7 | import org.gradle.kotlin.dsl.getByType
8 |
9 | class AndroidLibraryConventionPlugin : Plugin {
10 | override fun apply(target: Project) = with(target) {
11 | val libs = extensions.getByType().named("libs")
12 |
13 | with(pluginManager) {
14 | apply("com.android.library")
15 | apply("focuslauncher.lint")
16 | apply("org.jetbrains.kotlin.android")
17 | apply(libs.findPlugin("kotlinx-kover").get().get().pluginId)
18 | }
19 |
20 | extensions.configure {
21 | configureKotlinAndroid(commonExtension = this)
22 | defaultConfig.targetSdk = libs.findVersion("androidTargetSdk").get().requiredVersion.toInt()
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/build-logic/convention/src/main/kotlin/KotlinLibraryConventionPlugin.kt:
--------------------------------------------------------------------------------
1 | import org.gradle.api.Plugin
2 | import org.gradle.api.Project
3 |
4 | class KotlinLibraryConventionPlugin : Plugin {
5 | override fun apply(target: Project) = with(target) {
6 | pluginManager.apply("kotlin")
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/build-logic/convention/src/main/kotlin/LintConventionPlugin.kt:
--------------------------------------------------------------------------------
1 | import org.gradle.api.Plugin
2 | import org.gradle.api.Project
3 | import org.gradle.api.artifacts.VersionCatalogsExtension
4 | import org.gradle.kotlin.dsl.dependencies
5 | import org.gradle.kotlin.dsl.getByType
6 | import org.gradle.kotlin.dsl.project
7 |
8 | class LintConventionPlugin : Plugin {
9 | override fun apply(target: Project) = with(target) {
10 | pluginManager.apply("io.gitlab.arturbosch.detekt")
11 |
12 | val libs = extensions.getByType().named("libs")
13 | dependencies {
14 | add(configurationName = "implementation", project(":core:lint"))
15 | add(configurationName = "detektPlugins", libs.findLibrary("detekt.formatting").get())
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/build-logic/gradle.properties:
--------------------------------------------------------------------------------
1 | # Gradle properties are not passed to included builds https://github.com/gradle/gradle/issues/2534
2 | org.gradle.parallel=true
3 | org.gradle.caching=true
4 | org.gradle.configureondemand=true
5 |
--------------------------------------------------------------------------------
/build-logic/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/build-logic/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/build-logic/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/build-logic/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | dependencyResolutionManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | versionCatalogs {
7 | create("libs") {
8 | from(files("../gradle/libs.versions.toml"))
9 | }
10 | }
11 | }
12 |
13 | rootProject.name = "build-logic"
14 | include(":convention")
15 |
--------------------------------------------------------------------------------
/buildScripts/install-git-hooks.gradle.kts:
--------------------------------------------------------------------------------
1 | import java.nio.file.Files
2 | import java.nio.file.Paths
3 | import java.nio.file.attribute.PosixFilePermission
4 |
5 | // Create a task to copy Git hooks from /scripts to .git/hooks path
6 | val installGitHooks by tasks.creating(Copy::class) {
7 | from(layout.projectDirectory.file("scripts/pre-push"))
8 | val toDir = layout.projectDirectory.dir(".git/hooks")
9 | val toFile = toDir.file("pre-push").asFile
10 | val toFilePath = Paths.get(toFile.absolutePath)
11 | into(toDir)
12 | duplicatesStrategy = DuplicatesStrategy.EXCLUDE
13 |
14 | doLast {
15 | val perms = Files.getPosixFilePermissions(toFilePath)
16 | perms.add(PosixFilePermission.OWNER_EXECUTE)
17 | Files.setPosixFilePermissions(toFilePath, perms)
18 | }
19 | }
20 |
21 | // Register the Git task to run at beginning
22 | tasks.getByPath(":app:preBuild").dependsOn(installGitHooks)
23 |
--------------------------------------------------------------------------------
/core/circuitoverlay/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.library")
3 | id("focuslauncher.android.library.compose")
4 | alias(libs.plugins.kotlin.parcelize)
5 | }
6 |
7 | android {
8 | namespace = "dev.mslalith.focuslauncher.core.circuitoverlay"
9 | }
10 |
11 | dependencies {
12 | implementation(projects.core.screens)
13 |
14 | implementation(platform(libs.androidx.compose.bom))
15 | implementation(libs.androidx.compose.material3)
16 | implementation(libs.androidx.compose.ui)
17 | implementation(libs.androidx.compose.ui.tooling)
18 |
19 | implementation(libs.circuit.foundation)
20 | implementation(libs.circuit.runtime)
21 | implementation(libs.circuit.overlay)
22 | }
23 |
--------------------------------------------------------------------------------
/core/circuitoverlay/src/main/kotlin/dev/mslalith/focuslauncher/core/circuitoverlay/OverlayResultScreen.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.circuitoverlay
2 |
3 | import com.slack.circuit.runtime.screen.Screen
4 | import kotlinx.parcelize.Parcelize
5 | import kotlinx.parcelize.RawValue
6 |
7 | @Parcelize
8 | data class OverlayResultScreen(
9 | val result: @RawValue T? = null
10 | ) : Screen
11 |
--------------------------------------------------------------------------------
/core/common/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/common/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.library")
3 | id("focuslauncher.android.hilt")
4 | }
5 |
6 | android {
7 | namespace = "dev.mslalith.focuslauncher.core.common"
8 | }
9 |
10 | dependencies {
11 | implementation(projects.core.model)
12 |
13 | implementation(platform(libs.androidx.compose.bom))
14 | implementation(libs.androidx.compose.runtime)
15 |
16 | implementation(libs.kotlinx.datetime)
17 | implementation(libs.kotlinx.coroutines.core)
18 | implementation(libs.kotlinx.collections.immutable)
19 |
20 | testImplementation(libs.truth)
21 | testImplementation(projects.core.testing)
22 | }
23 |
--------------------------------------------------------------------------------
/core/common/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/core/common/src/main/kotlin/dev/mslalith/focuslauncher/core/common/appcoroutinedispatcher/AppCoroutineDispatcher.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.common.appcoroutinedispatcher
2 |
3 | import kotlin.coroutines.CoroutineContext
4 |
5 | interface AppCoroutineDispatcher {
6 | val main: CoroutineContext
7 | val io: CoroutineContext
8 | }
9 |
--------------------------------------------------------------------------------
/core/common/src/main/kotlin/dev/mslalith/focuslauncher/core/common/appcoroutinedispatcher/impl/AppCoroutineDispatcherImpl.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.common.appcoroutinedispatcher.impl
2 |
3 | import dev.mslalith.focuslauncher.core.common.appcoroutinedispatcher.AppCoroutineDispatcher
4 | import dev.mslalith.focuslauncher.core.lint.kover.IgnoreInKoverReport
5 | import kotlin.coroutines.CoroutineContext
6 | import kotlinx.coroutines.Dispatchers
7 |
8 | @IgnoreInKoverReport
9 | internal class AppCoroutineDispatcherImpl : AppCoroutineDispatcher {
10 | override val main: CoroutineContext = Dispatchers.Main
11 | override val io: CoroutineContext = Dispatchers.IO
12 | }
13 |
--------------------------------------------------------------------------------
/core/common/src/main/kotlin/dev/mslalith/focuslauncher/core/common/appcoroutinedispatcher/test/TestAppCoroutineDispatcher.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.common.appcoroutinedispatcher.test
2 |
3 | import dev.mslalith.focuslauncher.core.common.appcoroutinedispatcher.AppCoroutineDispatcher
4 | import kotlinx.coroutines.Dispatchers
5 | import kotlin.coroutines.CoroutineContext
6 |
7 | class TestAppCoroutineDispatcher(
8 | coroutineContext: CoroutineContext = Dispatchers.Main // this is swapped with TestDispatcher in tests
9 | ) : AppCoroutineDispatcher {
10 | override val main: CoroutineContext = coroutineContext
11 | override val io: CoroutineContext = coroutineContext
12 | }
13 |
--------------------------------------------------------------------------------
/core/common/src/main/kotlin/dev/mslalith/focuslauncher/core/common/extensions/ImmutableExtensions.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.common.extensions
2 |
3 | import kotlinx.collections.immutable.ImmutableList
4 | import kotlinx.collections.immutable.ImmutableMap
5 | import kotlinx.collections.immutable.persistentMapOf
6 | import kotlinx.collections.immutable.toImmutableList
7 |
8 | inline fun Iterable.groupByImmutable(
9 | keySelector: (T) -> K
10 | ): ImmutableMap> {
11 | var persistentMap = persistentMapOf>()
12 | groupBy(keySelector = keySelector).forEach { (key, value) ->
13 | persistentMap = persistentMap.put(key, value.toImmutableList())
14 | }
15 | return persistentMap
16 | }
17 |
--------------------------------------------------------------------------------
/core/common/src/main/kotlin/dev/mslalith/focuslauncher/core/common/extensions/UtilityExtensions.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.common.extensions
2 |
3 | import java.text.DecimalFormat
4 | import java.text.DecimalFormatSymbols
5 | import java.util.Locale
6 |
7 |
8 | fun Int.to2Digit() = when {
9 | this < 10 -> "0$this"
10 | else -> this
11 | }
12 |
13 | fun Double.asPercent(maxFractions: Int = 2) = limitDecimals(maxFractions = maxFractions) + "%"
14 |
15 | fun Double.limitDecimals(
16 | maxFractions: Int = 2,
17 | minFractions: Int = 2
18 | ): String {
19 | val fractionsPlaceholder = "#".repeat(n = maxFractions)
20 | val decimalFormat = DecimalFormat("#.$fractionsPlaceholder", DecimalFormatSymbols(Locale.US)).apply {
21 | minimumFractionDigits = minFractions
22 | maximumFractionDigits = maxFractions
23 | }
24 | return decimalFormat.format(this) ?: this.toString()
25 | }
26 |
27 | fun Char.isAlphabet() = when (code) {
28 | in 65..90 -> true
29 | in 97..122 -> true
30 | else -> false
31 | }
32 |
--------------------------------------------------------------------------------
/core/common/src/main/kotlin/dev/mslalith/focuslauncher/core/common/model/LoadingState.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.common.model
2 |
3 | import androidx.compose.runtime.Immutable
4 |
5 | @Immutable
6 | sealed interface LoadingState {
7 | data object Loading : LoadingState
8 | data class Loaded(val value: R) : LoadingState
9 | }
10 |
11 | fun LoadingState.getOrNull(): T? = when (this) {
12 | is LoadingState.Loaded -> this.value
13 | else -> null
14 | }
15 |
--------------------------------------------------------------------------------
/core/common/src/main/kotlin/dev/mslalith/focuslauncher/core/common/model/State.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.common.model
2 |
3 | import androidx.compose.runtime.Immutable
4 |
5 | @Immutable
6 | sealed class State {
7 | data object Initial : State()
8 | data class Success(val value: R) : State()
9 | }
10 |
11 | fun State.getOrNull(): T? = when (this) {
12 | is State.Success -> this.value
13 | else -> null
14 | }
15 |
--------------------------------------------------------------------------------
/core/common/src/main/kotlin/dev/mslalith/focuslauncher/core/common/network/NetworkMonitor.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.common.network
2 |
3 | import kotlinx.coroutines.flow.Flow
4 |
5 | interface NetworkMonitor {
6 | val isOnline: Flow
7 | fun isCurrentlyConnected(): Boolean
8 | }
9 |
--------------------------------------------------------------------------------
/core/common/src/main/kotlin/dev/mslalith/focuslauncher/core/common/network/test/FakeNetworkMonitor.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.common.network.test
2 |
3 | import dev.mslalith.focuslauncher.core.common.network.NetworkMonitor
4 | import kotlinx.coroutines.flow.Flow
5 | import kotlinx.coroutines.flow.MutableStateFlow
6 |
7 | class FakeNetworkMonitor : NetworkMonitor {
8 |
9 | private val _isOnline = MutableStateFlow(value = true)
10 | override val isOnline: Flow = _isOnline
11 |
12 | override fun isCurrentlyConnected(): Boolean = _isOnline.value
13 |
14 | fun goOnline() {
15 | _isOnline.value = true
16 | }
17 |
18 | fun goOffline() {
19 | _isOnline.value = false
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/core/common/src/main/kotlin/dev/mslalith/focuslauncher/core/common/providers/clock/ClockProvider.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.common.providers.clock
2 |
3 | import kotlinx.datetime.Instant
4 |
5 | interface ClockProvider {
6 | fun now(): Instant
7 | }
8 |
--------------------------------------------------------------------------------
/core/common/src/main/kotlin/dev/mslalith/focuslauncher/core/common/providers/clock/impl/ClockProviderImpl.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.common.providers.clock.impl
2 |
3 | import dev.mslalith.focuslauncher.core.common.providers.clock.ClockProvider
4 | import dev.mslalith.focuslauncher.core.lint.kover.IgnoreInKoverReport
5 | import javax.inject.Inject
6 | import kotlinx.datetime.Clock
7 | import kotlinx.datetime.Instant
8 |
9 | @IgnoreInKoverReport
10 | internal class ClockProviderImpl @Inject constructor() : ClockProvider {
11 | override fun now(): Instant = Clock.System.now()
12 | }
13 |
--------------------------------------------------------------------------------
/core/common/src/main/kotlin/dev/mslalith/focuslauncher/core/common/providers/clock/test/TestClockProvider.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.common.providers.clock.test
2 |
3 | import dev.mslalith.focuslauncher.core.common.providers.clock.ClockProvider
4 | import javax.inject.Inject
5 | import kotlinx.datetime.Clock
6 | import kotlinx.datetime.Instant
7 |
8 | class TestClockProvider @Inject constructor() : ClockProvider {
9 |
10 | private var instant = Clock.System.now()
11 |
12 | override fun now(): Instant = instant
13 |
14 | fun setInstant(instant: Instant) {
15 | this.instant = instant
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/common/src/main/kotlin/dev/mslalith/focuslauncher/core/common/providers/randomnumber/RandomNumberProvider.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.common.providers.randomnumber
2 |
3 | interface RandomNumberProvider {
4 | fun random(till: Int): Int
5 | }
6 |
--------------------------------------------------------------------------------
/core/common/src/main/kotlin/dev/mslalith/focuslauncher/core/common/providers/randomnumber/impl/RandomNumberProviderImpl.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.common.providers.randomnumber.impl
2 |
3 | import dev.mslalith.focuslauncher.core.common.providers.randomnumber.RandomNumberProvider
4 | import dev.mslalith.focuslauncher.core.lint.kover.IgnoreInKoverReport
5 | import java.util.Random
6 | import javax.inject.Inject
7 |
8 | @IgnoreInKoverReport
9 | internal class RandomNumberProviderImpl @Inject constructor() : RandomNumberProvider {
10 | override fun random(till: Int): Int = Random().nextInt(till)
11 | }
12 |
--------------------------------------------------------------------------------
/core/common/src/main/kotlin/dev/mslalith/focuslauncher/core/common/providers/randomnumber/test/TestRandomNumberProvider.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.common.providers.randomnumber.test
2 |
3 | import dev.mslalith.focuslauncher.core.common.providers.randomnumber.RandomNumberProvider
4 | import javax.inject.Inject
5 |
6 | class TestRandomNumberProvider @Inject constructor() : RandomNumberProvider {
7 | private var randomNumber = 0
8 |
9 | override fun random(till: Int): Int = randomNumber
10 |
11 | fun setRandomNumber(randomNumber: Int) {
12 | this.randomNumber = randomNumber
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/core/common/src/test/kotlin/dev/mslalith/focuslauncher/core/common/extensions/FormatNumberTest.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.common.extensions
2 |
3 | import com.google.common.truth.Truth.assertThat
4 | import dev.mslalith.focuslauncher.core.testing.extensions.withLocale
5 | import org.junit.Test
6 | import java.util.Locale
7 |
8 | class FormatNumberTest {
9 |
10 | private fun limitDecimalTests() {
11 | assertThat(0.0.limitDecimals(maxFractions = 4)).isEqualTo("0.00")
12 | assertThat(23.0.limitDecimals(maxFractions = 4)).isEqualTo("23.00")
13 |
14 | assertThat(1.23.limitDecimals(maxFractions = 4)).isEqualTo("1.23")
15 | assertThat(1.2345678.limitDecimals(maxFractions = 4)).isEqualTo("1.2346")
16 |
17 | assertThat(753.23.limitDecimals(maxFractions = 4)).isEqualTo("753.23")
18 | assertThat(463.2345678.limitDecimals(maxFractions = 4)).isEqualTo("463.2346")
19 | }
20 |
21 | @Test
22 | fun `limit decimal in US locale`() = withLocale(Locale.US) { limitDecimalTests() }
23 |
24 | @Test
25 | fun `limit decimal in German locale`() = withLocale(Locale.GERMANY) { limitDecimalTests() }
26 | }
27 |
--------------------------------------------------------------------------------
/core/common/src/test/kotlin/dev/mslalith/focuslauncher/core/common/extensions/ImmutableExtensionsKtTest.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.common.extensions
2 |
3 | import com.google.common.truth.Truth.assertThat
4 | import kotlinx.collections.immutable.persistentListOf
5 | import kotlinx.collections.immutable.persistentMapOf
6 | import org.junit.Test
7 |
8 | class ImmutableExtensionsKtTest {
9 |
10 | @Test
11 | fun `01 - group by first character`() {
12 | val list = listOf("abc", "acc", "bcc", "bcd", "cdc")
13 | val expected = persistentMapOf(
14 | 'a' to persistentListOf("abc", "acc"),
15 | 'b' to persistentListOf("bcc", "bcd"),
16 | 'c' to persistentListOf("cdc")
17 | )
18 | val actual = list.groupByImmutable { it.first() }
19 | assertThat(actual).isEqualTo(expected)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/core/data-test/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.library")
3 | }
4 |
5 | android {
6 | namespace = "dev.mslalith.focuslauncher.core.data.test"
7 |
8 | packaging {
9 | resources.excludes.add("META-INF/*")
10 | }
11 | }
12 |
13 | dependencies {
14 | implementation(projects.core.model)
15 | implementation(projects.core.data)
16 | implementation(projects.core.common)
17 | implementation(projects.core.testing)
18 |
19 | implementation(libs.kotlinx.coroutines.core)
20 | implementation(libs.kotlinx.datetime)
21 | }
22 |
--------------------------------------------------------------------------------
/core/data-test/src/main/kotlin/dev/mslalith/focuslauncher/core/data/test/repository/FakeClockRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.test.repository
2 |
3 | import dev.mslalith.focuslauncher.core.data.repository.ClockRepo
4 | import kotlinx.coroutines.flow.MutableStateFlow
5 | import kotlinx.coroutines.flow.StateFlow
6 | import kotlinx.coroutines.flow.update
7 | import kotlinx.datetime.Clock
8 | import kotlinx.datetime.Instant
9 |
10 | class FakeClockRepo : ClockRepo {
11 |
12 | private val _currentInstantStateFlow = MutableStateFlow(value = Clock.System.now())
13 | override val currentInstantStateFlow: StateFlow = _currentInstantStateFlow
14 |
15 | override fun refreshTime() = Unit
16 |
17 | fun setInstant(instant: Instant) {
18 | _currentInstantStateFlow.update { instant }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/core/data-test/src/main/kotlin/dev/mslalith/focuslauncher/core/data/test/repository/FakePlacesRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.test.repository
2 |
3 | import dev.mslalith.focuslauncher.core.data.repository.PlacesRepo
4 | import dev.mslalith.focuslauncher.core.model.location.LatLng
5 | import dev.mslalith.focuslauncher.core.model.place.Place
6 |
7 | class FakePlacesRepo : PlacesRepo {
8 |
9 | private var place: Place? = null
10 |
11 | override suspend fun fetchPlaceLocal(latLng: LatLng): Place? = place
12 |
13 | override suspend fun fetchPlace(latLng: LatLng): Place? = place
14 |
15 | fun setPlace(place: Place?) {
16 | this.place = place
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/core/data-test/src/main/kotlin/dev/mslalith/focuslauncher/core/data/test/repository/FakeThemeRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.test.repository
2 |
3 | import dev.mslalith.focuslauncher.core.data.repository.ThemeRepo
4 | import dev.mslalith.focuslauncher.core.model.Constants.Defaults.Settings.General.DEFAULT_THEME
5 | import dev.mslalith.focuslauncher.core.model.Theme
6 | import kotlinx.coroutines.flow.Flow
7 | import kotlinx.coroutines.flow.MutableStateFlow
8 | import kotlinx.coroutines.flow.update
9 |
10 | class FakeThemeRepo : ThemeRepo {
11 |
12 | private val currentThemeStateFlow = MutableStateFlow(value = DEFAULT_THEME)
13 | override val currentThemeFlow: Flow = currentThemeStateFlow
14 |
15 | override suspend fun changeTheme(theme: Theme) = currentThemeStateFlow.update { theme }
16 | }
17 |
--------------------------------------------------------------------------------
/core/data-test/src/main/kotlin/dev/mslalith/focuslauncher/core/data/test/repository/settings/FakeQuotesSettingsRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.test.repository.settings
2 |
3 | import dev.mslalith.focuslauncher.core.data.repository.settings.QuotesSettingsRepo
4 | import dev.mslalith.focuslauncher.core.model.Constants.Defaults.Settings.Quotes.DEFAULT_SHOW_QUOTES
5 | import kotlinx.coroutines.flow.Flow
6 | import kotlinx.coroutines.flow.MutableStateFlow
7 | import kotlinx.coroutines.flow.update
8 |
9 | class FakeQuotesSettingsRepo : QuotesSettingsRepo {
10 |
11 | private val showQuotesStateFlow = MutableStateFlow(value = DEFAULT_SHOW_QUOTES)
12 | override val showQuotesFlow: Flow = showQuotesStateFlow
13 |
14 | override suspend fun toggleShowQuotes() {
15 | showQuotesStateFlow.update { !it }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/data/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/database/dao/PlacesDao.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.database.dao
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.OnConflictStrategy
6 | import androidx.room.Query
7 | import dev.mslalith.focuslauncher.core.data.database.entities.PlaceRoom
8 | import dev.mslalith.focuslauncher.core.model.Constants.Database.PLACES_TABLE_NAME
9 |
10 | @Dao
11 | internal interface PlacesDao {
12 |
13 | @Query(value = "SELECT * FROM $PLACES_TABLE_NAME WHERE latitude = :latitude AND longitude = :longitude")
14 | suspend fun fetchPlace(latitude: String, longitude: String): PlaceRoom?
15 |
16 | @Insert(onConflict = OnConflictStrategy.REPLACE)
17 | suspend fun insertPlace(place: PlaceRoom)
18 | }
19 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/database/entities/AppRoom.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.database.entities
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 | import dev.mslalith.focuslauncher.core.model.Constants.Database.APPS_TABLE_NAME
7 |
8 | @Entity(tableName = APPS_TABLE_NAME)
9 | internal data class AppRoom(
10 | val name: String,
11 | @ColumnInfo(defaultValue = "'name'")
12 | val displayName: String,
13 | @PrimaryKey(autoGenerate = false)
14 | val packageName: String,
15 | val isSystem: Boolean
16 | )
17 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/database/entities/FavoriteAppRoom.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.database.entities
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 | import dev.mslalith.focuslauncher.core.model.Constants.Database.FAVORITE_APPS_TABLE_NAME
6 |
7 | @Entity(tableName = FAVORITE_APPS_TABLE_NAME)
8 | internal data class FavoriteAppRoom(
9 | @PrimaryKey(autoGenerate = false)
10 | val packageName: String
11 | )
12 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/database/entities/HiddenAppRoom.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.database.entities
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 | import dev.mslalith.focuslauncher.core.model.Constants.Database.HIDDEN_APPS_TABLE_NAME
6 |
7 | @Entity(tableName = HIDDEN_APPS_TABLE_NAME)
8 | internal data class HiddenAppRoom(
9 | @PrimaryKey(autoGenerate = false)
10 | val packageName: String
11 | )
12 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/database/entities/QuoteRoom.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.database.entities
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 | import dev.mslalith.focuslauncher.core.model.Constants.Database.QUOTES_TABLE_NAME
6 |
7 | @Entity(tableName = QUOTES_TABLE_NAME)
8 | internal data class QuoteRoom(
9 | @PrimaryKey(autoGenerate = false)
10 | val id: String,
11 | val quote: String,
12 | val author: String,
13 | val authorSlug: String,
14 | val length: Int,
15 | val tags: List
16 | )
17 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/database/typeconverter/Converters.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.database.typeconverter
2 |
3 | import androidx.room.TypeConverter
4 | import kotlinx.serialization.builtins.ListSerializer
5 | import kotlinx.serialization.builtins.serializer
6 | import kotlinx.serialization.json.Json
7 |
8 | internal class Converters {
9 |
10 | @TypeConverter
11 | fun jsonToStringList(json: String): List {
12 | return Json.decodeFromString(
13 | deserializer = ListSerializer(elementSerializer = String.serializer()),
14 | string = json
15 | )
16 | }
17 |
18 | @TypeConverter
19 | fun stringListToJson(list: List): String {
20 | return Json.encodeToString(
21 | serializer = ListSerializer(elementSerializer = String.serializer()),
22 | value = list
23 | )
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/database/usecase/datastore/ClearDataStoreUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.database.usecase.datastore
2 |
3 | import androidx.datastore.core.DataStore
4 | import androidx.datastore.preferences.core.Preferences
5 | import androidx.datastore.preferences.core.edit
6 | import javax.inject.Inject
7 |
8 | internal class ClearDataStoreUseCase @Inject constructor() {
9 | suspend operator fun invoke(dataStore: DataStore) {
10 | dataStore.edit { it.clear() }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/database/usecase/datastore/ClearThemeDataStoreUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.database.usecase.datastore
2 |
3 | import androidx.datastore.core.DataStore
4 | import androidx.datastore.preferences.core.Preferences
5 | import dev.mslalith.focuslauncher.core.data.di.modules.ThemeProvider
6 | import javax.inject.Inject
7 |
8 | class ClearThemeDataStoreUseCase @Inject internal constructor(
9 | private val clearDataStoreUseCase: ClearDataStoreUseCase,
10 | @ThemeProvider private val themeDataStore: DataStore
11 | ) {
12 | suspend operator fun invoke() = clearDataStoreUseCase(dataStore = themeDataStore)
13 | }
14 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/database/usecase/room/CloseDatabaseUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.database.usecase.room
2 |
3 | import dev.mslalith.focuslauncher.core.data.database.AppDatabase
4 | import javax.inject.Inject
5 |
6 | class CloseDatabaseUseCase @Inject internal constructor(
7 | private val appDatabase: AppDatabase
8 | ) {
9 | operator fun invoke() = appDatabase.close()
10 | }
11 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/dto/AddressDto.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.dto
2 |
3 | import dev.mslalith.focuslauncher.core.data.database.entities.AddressRoom
4 | import dev.mslalith.focuslauncher.core.data.network.entities.AddressResponse
5 | import dev.mslalith.focuslauncher.core.model.place.Address
6 |
7 | internal fun AddressResponse.toAddress(): Address = Address(
8 | state = state,
9 | country = country
10 | )
11 |
12 | internal fun AddressRoom.toAddress(): Address = Address(
13 | state = state,
14 | country = country
15 | )
16 |
17 | internal fun AddressResponse.toAddressRoom(): AddressRoom = AddressRoom(
18 | houseNumber = houseNumber,
19 | road = road,
20 | village = village,
21 | suburb = suburb,
22 | postCode = postCode,
23 | county = county,
24 | stateDistrict = stateDistrict,
25 | state = state,
26 | iso3166 = iso3166,
27 | country = country,
28 | countryCode = countryCode
29 | )
30 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/dto/QuoteDto.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.dto
2 |
3 | import dev.mslalith.focuslauncher.core.data.database.entities.QuoteRoom
4 | import dev.mslalith.focuslauncher.core.data.network.entities.QuoteResponse
5 | import dev.mslalith.focuslauncher.core.model.Quote
6 |
7 | internal fun QuoteResponse.toQuoteRoom(): QuoteRoom = QuoteRoom(
8 | id = id,
9 | quote = quote,
10 | author = author,
11 | authorSlug = authorSlug,
12 | length = length,
13 | tags = tags
14 | )
15 |
16 | internal fun QuoteRoom.toQuote(): Quote = Quote(
17 | id = id,
18 | quote = quote,
19 | author = author
20 | )
21 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/network/api/PlacesApi.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.network.api
2 |
3 | import dev.mslalith.focuslauncher.core.data.network.entities.PlaceResponse
4 | import dev.mslalith.focuslauncher.core.model.location.LatLng
5 |
6 | internal interface PlacesApi {
7 | suspend fun getPlace(latLng: LatLng): Result
8 | }
9 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/network/api/QuotesApi.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.network.api
2 |
3 | import dev.mslalith.focuslauncher.core.data.network.entities.QuotesApiResponse
4 | import dev.mslalith.focuslauncher.core.model.Constants.Defaults.QUOTES_LIMIT_PER_PAGE
5 |
6 | internal interface QuotesApi {
7 | suspend fun getQuotes(
8 | page: Int = 1,
9 | limit: Int = QUOTES_LIMIT_PER_PAGE
10 | ): Result
11 | }
12 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/network/api/fakes/FakePlacesApi.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.network.api.fakes
2 |
3 | import dev.mslalith.focuslauncher.core.data.network.api.PlacesApi
4 | import dev.mslalith.focuslauncher.core.data.network.entities.PlaceResponse
5 | import dev.mslalith.focuslauncher.core.data.utils.dummyPlaceResponseFor
6 | import dev.mslalith.focuslauncher.core.model.location.LatLng
7 |
8 | internal class FakePlacesApi : PlacesApi {
9 | override suspend fun getPlace(latLng: LatLng): Result = Result.success(
10 | value = dummyPlaceResponseFor(latLng = latLng)
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/network/api/fakes/FakeQuotesApi.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.network.api.fakes
2 |
3 | import dev.mslalith.focuslauncher.core.data.network.api.QuotesApi
4 | import dev.mslalith.focuslauncher.core.data.network.entities.QuotesApiResponse
5 | import dev.mslalith.focuslauncher.core.data.utils.dummyQuoteResponseFor
6 |
7 | internal class FakeQuotesApi : QuotesApi {
8 | override suspend fun getQuotes(page: Int, limit: Int): Result {
9 | val pageOffset = (page - 1) * limit
10 | return Result.success(
11 | value = QuotesApiResponse(
12 | count = limit,
13 | totalCount = 100,
14 | page = page,
15 | totalPages = 20,
16 | lastItemIndex = 9,
17 | results = List(size = limit) { dummyQuoteResponseFor(index = pageOffset + it) }
18 | )
19 | )
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/network/api/impl/PlacesApiImpl.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.network.api.impl
2 |
3 | import dev.mslalith.focuslauncher.core.data.network.api.PlacesApi
4 | import dev.mslalith.focuslauncher.core.data.network.entities.PlaceResponse
5 | import dev.mslalith.focuslauncher.core.model.location.LatLng
6 | import io.ktor.client.HttpClient
7 | import io.ktor.client.call.body
8 | import io.ktor.client.request.get
9 | import io.ktor.client.request.parameter
10 | import javax.inject.Inject
11 |
12 | internal class PlacesApiImpl @Inject constructor(
13 | private val httpClient: HttpClient
14 | ) : PlacesApi {
15 |
16 | override suspend fun getPlace(latLng: LatLng): Result = runCatching {
17 | httpClient.get(urlString = "https://nominatim.openstreetmap.org/reverse") {
18 | parameter(key = "format", value = "json")
19 | parameter(key = "lat", value = latLng.latitude)
20 | parameter(key = "lon", value = latLng.longitude)
21 | }.body()
22 | }.recover { PlaceResponse.default() }
23 | }
24 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/repository/AppDrawerRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.repository
2 |
3 | import dev.mslalith.focuslauncher.core.model.app.App
4 | import kotlinx.coroutines.flow.Flow
5 |
6 | interface AppDrawerRepo {
7 | val allAppsFlow: Flow>
8 |
9 | suspend fun getAppBy(packageName: String): App?
10 | suspend fun addApps(apps: List)
11 | suspend fun addApp(app: App)
12 | suspend fun removeApp(app: App)
13 | suspend fun clearApps()
14 | suspend fun updateDisplayName(app: App, displayName: String)
15 | suspend fun areAppsEmptyInDatabase(): Boolean
16 | }
17 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/repository/ClockRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.repository
2 |
3 | import kotlinx.coroutines.flow.StateFlow
4 | import kotlinx.datetime.Instant
5 |
6 | interface ClockRepo {
7 | val currentInstantStateFlow: StateFlow
8 | fun refreshTime()
9 | }
10 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/repository/FavoritesRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.repository
2 |
3 | import dev.mslalith.focuslauncher.core.model.app.App
4 | import kotlinx.coroutines.flow.Flow
5 |
6 | interface FavoritesRepo {
7 | val onlyFavoritesFlow: Flow>
8 |
9 | suspend fun addToFavorites(app: App)
10 | suspend fun addToFavorites(apps: List)
11 | suspend fun reorderFavorite(app: App, withApp: App)
12 | suspend fun removeFromFavorites(packageName: String)
13 | suspend fun clearFavorites()
14 | suspend fun isFavorite(packageName: String): Boolean
15 | }
16 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/repository/HiddenAppsRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.repository
2 |
3 | import dev.mslalith.focuslauncher.core.model.app.App
4 | import kotlinx.coroutines.flow.Flow
5 |
6 | interface HiddenAppsRepo {
7 | val onlyHiddenAppsFlow: Flow>
8 |
9 | suspend fun addToHiddenApps(app: App)
10 | suspend fun addToHiddenApps(apps: List)
11 | suspend fun removeFromHiddenApps(packageName: String)
12 | suspend fun clearHiddenApps()
13 | suspend fun isHidden(packageName: String): Boolean
14 | }
15 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/repository/LunarPhaseDetailsRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.repository
2 |
3 | import dev.mslalith.focuslauncher.core.common.model.State
4 | import dev.mslalith.focuslauncher.core.model.location.LatLng
5 | import dev.mslalith.focuslauncher.core.model.lunarphase.LunarPhaseDetails
6 | import dev.mslalith.focuslauncher.core.model.lunarphase.UpcomingLunarPhase
7 | import kotlinx.coroutines.flow.StateFlow
8 | import kotlinx.datetime.Instant
9 |
10 | interface LunarPhaseDetailsRepo {
11 | val lunarPhaseDetailsStateFlow: StateFlow>
12 | val upcomingLunarPhaseStateFlow: StateFlow>
13 |
14 | suspend fun refreshLunarPhaseDetails(instant: Instant, latLng: LatLng)
15 | }
16 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/repository/PlacesRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.repository
2 |
3 | import dev.mslalith.focuslauncher.core.model.location.LatLng
4 | import dev.mslalith.focuslauncher.core.model.place.Place
5 |
6 | interface PlacesRepo {
7 | suspend fun fetchPlaceLocal(latLng: LatLng): Place?
8 | suspend fun fetchPlace(latLng: LatLng): Place?
9 | }
10 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/repository/QuotesRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.repository
2 |
3 | import dev.mslalith.focuslauncher.core.common.model.State
4 | import dev.mslalith.focuslauncher.core.model.Quote
5 | import kotlinx.coroutines.flow.StateFlow
6 |
7 | interface QuotesRepo {
8 | val currentQuoteStateFlow: StateFlow>
9 | val isFetchingQuotesStateFlow: StateFlow
10 |
11 | suspend fun nextRandomQuote()
12 | suspend fun fetchQuotes(maxPages: Int)
13 | suspend fun addInitialQuotesIfNeeded()
14 | suspend fun hasQuotesReachedLimit(): Boolean
15 | suspend fun quotesSize(): Int
16 | }
17 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/repository/ThemeRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.repository
2 |
3 | import dev.mslalith.focuslauncher.core.model.Theme
4 | import kotlinx.coroutines.flow.Flow
5 |
6 | interface ThemeRepo {
7 | val currentThemeFlow: Flow
8 |
9 | suspend fun changeTheme(theme: Theme)
10 | }
11 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/repository/impl/ClockRepoImpl.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.repository.impl
2 |
3 | import dev.mslalith.focuslauncher.core.common.providers.clock.ClockProvider
4 | import dev.mslalith.focuslauncher.core.data.repository.ClockRepo
5 | import javax.inject.Inject
6 | import kotlinx.coroutines.flow.MutableStateFlow
7 | import kotlinx.coroutines.flow.StateFlow
8 | import kotlinx.datetime.Instant
9 |
10 | internal class ClockRepoImpl @Inject constructor(
11 | private val clockProvider: ClockProvider
12 | ) : ClockRepo {
13 |
14 | private val _currentInstantStateFlow = MutableStateFlow(value = clockProvider.now())
15 | override val currentInstantStateFlow: StateFlow
16 | get() = _currentInstantStateFlow
17 |
18 | init { refreshTime() }
19 |
20 | override fun refreshTime() {
21 | _currentInstantStateFlow.value = clockProvider.now()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/repository/settings/AppDrawerSettingsRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.repository.settings
2 |
3 | import dev.mslalith.focuslauncher.core.model.AppDrawerViewType
4 | import dev.mslalith.focuslauncher.core.model.appdrawer.AppDrawerIconViewType
5 | import kotlinx.coroutines.flow.Flow
6 |
7 | interface AppDrawerSettingsRepo {
8 | val appDrawerViewTypeFlow: Flow
9 | val appIconsVisibilityFlow: Flow
10 | val appDrawerIconViewTypeFlow: Flow
11 | val searchBarVisibilityFlow: Flow
12 | val appGroupHeaderVisibilityFlow: Flow
13 |
14 | suspend fun updateAppDrawerViewType(appDrawerViewType: AppDrawerViewType)
15 | suspend fun updateAppDrawerIconViewType(appDrawerIconViewType: AppDrawerIconViewType)
16 | suspend fun toggleAppIconsVisibility()
17 | suspend fun toggleSearchBarVisibility()
18 | suspend fun toggleAppGroupHeaderVisibility()
19 | }
20 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/repository/settings/ClockSettingsRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.repository.settings
2 |
3 | import dev.mslalith.focuslauncher.core.model.ClockAlignment
4 | import kotlinx.coroutines.flow.Flow
5 |
6 | interface ClockSettingsRepo {
7 | val showClock24Flow: Flow
8 | val use24HourFlow: Flow
9 | val clockAlignmentFlow: Flow
10 | val clock24AnimationDurationFlow: Flow
11 |
12 | suspend fun toggleClock24()
13 | suspend fun toggleUse24Hour()
14 | suspend fun updateClockAlignment(clockAlignment: ClockAlignment)
15 | suspend fun updateClock24AnimationDuration(duration: Int)
16 | }
17 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/repository/settings/GeneralSettingsRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.repository.settings
2 |
3 | import dev.mslalith.focuslauncher.core.model.IconPackType
4 | import kotlinx.coroutines.flow.Flow
5 |
6 | interface GeneralSettingsRepo {
7 | val firstRunFlow: Flow
8 | val statusBarVisibilityFlow: Flow
9 | val notificationShadeFlow: Flow
10 | val isDefaultLauncher: Flow
11 | val iconPackTypeFlow: Flow
12 | val reportCrashesFlow: Flow
13 |
14 | suspend fun overrideFirstRun()
15 | suspend fun toggleStatusBarVisibility()
16 | suspend fun toggleNotificationShade()
17 | suspend fun setIsDefaultLauncher(isDefault: Boolean)
18 | suspend fun updateIconPackType(iconPackType: IconPackType)
19 | suspend fun updateReportCrashes(enabled: Boolean)
20 | }
21 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/repository/settings/LunarPhaseSettingsRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.repository.settings
2 |
3 | import dev.mslalith.focuslauncher.core.model.CurrentPlace
4 | import kotlinx.coroutines.flow.Flow
5 |
6 | interface LunarPhaseSettingsRepo {
7 | val showLunarPhaseFlow: Flow
8 | val showIlluminationPercentFlow: Flow
9 | val showUpcomingPhaseDetailsFlow: Flow
10 | val currentPlaceFlow: Flow
11 |
12 | suspend fun toggleShowLunarPhase()
13 | suspend fun toggleShowIlluminationPercent()
14 | suspend fun toggleShowUpcomingPhaseDetails()
15 | suspend fun updateCurrentPlace(currentPlace: CurrentPlace)
16 | }
17 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/repository/settings/QuotesSettingsRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.repository.settings
2 |
3 | import kotlinx.coroutines.flow.Flow
4 |
5 | interface QuotesSettingsRepo {
6 | val showQuotesFlow: Flow
7 |
8 | suspend fun toggleShowQuotes()
9 | }
10 |
--------------------------------------------------------------------------------
/core/data/src/main/kotlin/dev/mslalith/focuslauncher/core/data/utils/QuotesRepoHelpers.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.utils
2 |
3 | import dev.mslalith.focuslauncher.core.data.network.entities.QuoteResponse
4 | import dev.mslalith.focuslauncher.core.lint.kover.IgnoreInKoverReport
5 | import dev.mslalith.focuslauncher.core.model.Quote
6 |
7 | @IgnoreInKoverReport
8 | internal fun dummyQuoteResponseFor(index: Int) = QuoteResponse(
9 | id = "ID #$index",
10 | quote = "Quote #$index",
11 | author = "Author #$index",
12 | authorSlug = "Author Slug #$index",
13 | tags = listOf(),
14 | length = 20
15 | )
16 |
17 | @IgnoreInKoverReport
18 | fun dummyQuoteFor(index: Int) = Quote(
19 | id = "ID #$index",
20 | quote = "Quote #$index",
21 | author = "Author #$index"
22 | )
23 |
--------------------------------------------------------------------------------
/core/data/src/test/kotlin/dev/mslalith/focuslauncher/core/data/helpers/SettingsRepoHelpers.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.data.helpers
2 |
3 | import app.cash.turbine.test
4 | import com.google.common.truth.Truth.assertThat
5 | import kotlinx.coroutines.flow.Flow
6 |
7 | suspend fun verifyChange(
8 | flow: Flow,
9 | initialItem: T,
10 | mutate: suspend () -> T
11 | ) = flow.test {
12 | assertThat(awaitItem()).isEqualTo(initialItem)
13 | val newItem = mutate()
14 | assertThat(awaitItem()).isEqualTo(newItem)
15 | }
16 |
--------------------------------------------------------------------------------
/core/domain/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/domain/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.library")
3 | id("focuslauncher.android.hilt")
4 | }
5 |
6 | android {
7 | namespace = "dev.mslalith.focuslauncher.core.domain"
8 | }
9 |
10 | dependencies {
11 | implementation(projects.core.common)
12 | implementation(projects.core.data)
13 | implementation(projects.core.model)
14 | implementation(projects.core.launcherapps)
15 | implementation(projects.core.settings.sentry)
16 |
17 | implementation(libs.androidx.palette.ktx)
18 |
19 | testImplementation(projects.core.testing)
20 | }
21 |
--------------------------------------------------------------------------------
/core/domain/src/main/kotlin/dev/mslalith/focuslauncher/core/domain/apps/GetFavoriteColoredAppsUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.domain.apps
2 |
3 | import dev.mslalith.focuslauncher.core.common.appcoroutinedispatcher.AppCoroutineDispatcher
4 | import dev.mslalith.focuslauncher.core.data.repository.FavoritesRepo
5 | import dev.mslalith.focuslauncher.core.domain.apps.core.GetAppsIconPackAwareUseCase
6 | import dev.mslalith.focuslauncher.core.model.app.AppWithColor
7 | import kotlinx.coroutines.flow.Flow
8 | import kotlinx.coroutines.flow.flowOn
9 | import javax.inject.Inject
10 |
11 | class GetFavoriteColoredAppsUseCase @Inject internal constructor(
12 | private val getAppsIconPackAwareUseCase: GetAppsIconPackAwareUseCase,
13 | private val favoritesRepo: FavoritesRepo,
14 | private val appCoroutineDispatcher: AppCoroutineDispatcher
15 | ) {
16 | operator fun invoke(): Flow> = getAppsIconPackAwareUseCase.appsWithColor(
17 | appsFlow = favoritesRepo.onlyFavoritesFlow
18 | ).flowOn(context = appCoroutineDispatcher.io)
19 | }
20 |
--------------------------------------------------------------------------------
/core/domain/src/main/kotlin/dev/mslalith/focuslauncher/core/domain/apps/GetIconPackIconicAppsUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.domain.apps
2 |
3 | import dev.mslalith.focuslauncher.core.common.appcoroutinedispatcher.AppCoroutineDispatcher
4 | import dev.mslalith.focuslauncher.core.domain.apps.core.GetAppsIconPackAwareUseCase
5 | import dev.mslalith.focuslauncher.core.domain.iconpack.GetIconPackAppsUseCase
6 | import dev.mslalith.focuslauncher.core.model.app.AppWithIcon
7 | import kotlinx.coroutines.flow.Flow
8 | import kotlinx.coroutines.flow.flowOn
9 | import javax.inject.Inject
10 |
11 | class GetIconPackIconicAppsUseCase @Inject internal constructor(
12 | private val getAppsIconPackAwareUseCase: GetAppsIconPackAwareUseCase,
13 | private val getIconPackAppsUseCase: GetIconPackAppsUseCase,
14 | private val appCoroutineDispatcher: AppCoroutineDispatcher
15 | ) {
16 | operator fun invoke(): Flow> = getAppsIconPackAwareUseCase.appsWithIcons(
17 | appsFlow = getIconPackAppsUseCase()
18 | ).flowOn(context = appCoroutineDispatcher.io)
19 | }
20 |
--------------------------------------------------------------------------------
/core/domain/src/main/kotlin/dev/mslalith/focuslauncher/core/domain/iconpack/FetchIconPacksUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.domain.iconpack
2 |
3 | import dev.mslalith.focuslauncher.core.launcherapps.manager.iconpack.IconPackManager
4 | import javax.inject.Inject
5 |
6 | class FetchIconPacksUseCase @Inject constructor(
7 | private val iconPackManager: IconPackManager
8 | ) {
9 | operator fun invoke() = iconPackManager.fetchInstalledIconPacks()
10 | }
11 |
--------------------------------------------------------------------------------
/core/domain/src/main/kotlin/dev/mslalith/focuslauncher/core/domain/iconpack/GetIconPackAppsUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.domain.iconpack
2 |
3 | import dev.mslalith.focuslauncher.core.data.repository.AppDrawerRepo
4 | import dev.mslalith.focuslauncher.core.launcherapps.manager.iconpack.IconPackManager
5 | import dev.mslalith.focuslauncher.core.model.app.App
6 | import kotlinx.coroutines.flow.Flow
7 | import kotlinx.coroutines.flow.combine
8 | import javax.inject.Inject
9 |
10 | class GetIconPackAppsUseCase @Inject constructor(
11 | private val iconPackManager: IconPackManager,
12 | private val appDrawerRepo: AppDrawerRepo
13 | ) {
14 | operator fun invoke(): Flow> = appDrawerRepo.allAppsFlow
15 | .combine(flow = iconPackManager.iconPacksFlow) { allApps, iconPacks ->
16 | allApps.filter { app ->
17 | iconPacks.any { it.packageName == app.packageName }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/core/domain/src/main/kotlin/dev/mslalith/focuslauncher/core/domain/iconpack/LoadIconPackUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.domain.iconpack
2 |
3 | import dev.mslalith.focuslauncher.core.common.appcoroutinedispatcher.AppCoroutineDispatcher
4 | import dev.mslalith.focuslauncher.core.launcherapps.manager.iconpack.IconPackManager
5 | import dev.mslalith.focuslauncher.core.model.IconPackType
6 | import kotlinx.coroutines.withContext
7 | import javax.inject.Inject
8 |
9 | class LoadIconPackUseCase @Inject constructor(
10 | private val iconPackManager: IconPackManager,
11 | private val appCoroutineDispatcher: AppCoroutineDispatcher
12 | ) {
13 | suspend operator fun invoke(iconPackType: IconPackType) = withContext(appCoroutineDispatcher.io) {
14 | iconPackManager.loadIconPack(
15 | iconPackType = iconPackType
16 | )
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/core/domain/src/main/kotlin/dev/mslalith/focuslauncher/core/domain/iconpack/ReloadIconPackUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.domain.iconpack
2 |
3 | import dev.mslalith.focuslauncher.core.common.appcoroutinedispatcher.AppCoroutineDispatcher
4 | import dev.mslalith.focuslauncher.core.launcherapps.manager.iconpack.IconPackManager
5 | import kotlinx.coroutines.withContext
6 | import javax.inject.Inject
7 |
8 | class ReloadIconPackUseCase @Inject constructor(
9 | private val iconPackManager: IconPackManager,
10 | private val appCoroutineDispatcher: AppCoroutineDispatcher
11 | ) {
12 | suspend operator fun invoke() = withContext(appCoroutineDispatcher.io) {
13 | iconPackManager.reloadIconPack()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/core/domain/src/main/kotlin/dev/mslalith/focuslauncher/core/domain/launcherapps/GetDefaultFavoriteAppsUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.domain.launcherapps
2 |
3 | import dev.mslalith.focuslauncher.core.launcherapps.manager.launcherapps.LauncherAppsManager
4 | import dev.mslalith.focuslauncher.core.model.app.App
5 | import javax.inject.Inject
6 |
7 | class GetDefaultFavoriteAppsUseCase @Inject constructor(
8 | private val launcherAppsManager: LauncherAppsManager
9 | ) {
10 | suspend operator fun invoke(): List = launcherAppsManager.defaultFavoriteApps().map { it.app }
11 | }
12 |
--------------------------------------------------------------------------------
/core/domain/src/main/kotlin/dev/mslalith/focuslauncher/core/domain/launcherapps/LoadAllAppsUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.domain.launcherapps
2 |
3 | import dev.mslalith.focuslauncher.core.data.repository.AppDrawerRepo
4 | import dev.mslalith.focuslauncher.core.launcherapps.manager.launcherapps.LauncherAppsManager
5 | import javax.inject.Inject
6 |
7 | class LoadAllAppsUseCase @Inject constructor(
8 | private val launcherAppsManager: LauncherAppsManager,
9 | private val appDrawerRepo: AppDrawerRepo
10 | ) {
11 | suspend operator fun invoke(forceLoad: Boolean = false) {
12 | appDrawerRepo.apply {
13 | if (!forceLoad && !areAppsEmptyInDatabase()) return
14 | addApps(apps = launcherAppsManager.loadAllApps().map { it.app })
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/domain/src/main/kotlin/dev/mslalith/focuslauncher/core/domain/settings/UpdateReportCrashesSettingUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.domain.settings
2 |
3 | import dev.mslalith.focuslauncher.core.data.repository.settings.GeneralSettingsRepo
4 | import dev.mslalith.focuslauncher.core.settings.sentry.SentrySettings
5 | import javax.inject.Inject
6 |
7 | class UpdateReportCrashesSettingUseCase @Inject constructor(
8 | private val sentrySettings: SentrySettings,
9 | private val generalSettingsRepo: GeneralSettingsRepo
10 | ) {
11 | suspend operator fun invoke(enabled: Boolean) {
12 | generalSettingsRepo.updateReportCrashes(enabled = enabled)
13 | with(sentrySettings) {
14 | if (enabled) enableSentry() else disableSentry()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/domain/src/main/kotlin/dev/mslalith/focuslauncher/core/domain/theme/ChangeThemeUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.domain.theme
2 |
3 | import dev.mslalith.focuslauncher.core.data.repository.ThemeRepo
4 | import dev.mslalith.focuslauncher.core.model.Theme
5 | import javax.inject.Inject
6 |
7 | class ChangeThemeUseCase @Inject constructor(
8 | private val themeRepo: ThemeRepo
9 | ) {
10 | suspend operator fun invoke(theme: Theme) = themeRepo.changeTheme(theme = theme)
11 | }
12 |
--------------------------------------------------------------------------------
/core/domain/src/main/kotlin/dev/mslalith/focuslauncher/core/domain/theme/GetThemeUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.domain.theme
2 |
3 | import dev.mslalith.focuslauncher.core.data.repository.ThemeRepo
4 | import dev.mslalith.focuslauncher.core.model.Theme
5 | import kotlinx.coroutines.flow.Flow
6 | import javax.inject.Inject
7 |
8 | class GetThemeUseCase @Inject constructor(
9 | private val themeRepo: ThemeRepo
10 | ) {
11 | operator fun invoke(): Flow = themeRepo.currentThemeFlow
12 | }
13 |
--------------------------------------------------------------------------------
/core/domain/src/test/kotlin/dev/mslalith/focuslauncher/core/domain/utils/Extensions.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.domain.utils
2 |
3 | import dev.mslalith.focuslauncher.core.launcherapps.model.IconPack
4 | import dev.mslalith.focuslauncher.core.model.app.App
5 |
6 | internal fun List.toIconPacks(): List = map(App::toIconPack)
7 |
8 | internal fun App.toIconPack(): IconPack = IconPack(
9 | label = name,
10 | packageName = packageName
11 | )
12 |
--------------------------------------------------------------------------------
/core/launcherapps/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/launcherapps/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.library")
3 | id("focuslauncher.android.hilt")
4 | }
5 |
6 | android {
7 | namespace = "dev.mslalith.focuslauncher.core.launcherapps"
8 | }
9 |
10 | dependencies {
11 | implementation(projects.core.model)
12 | implementation(projects.core.data)
13 |
14 | testImplementation(projects.core.testing)
15 | }
16 |
--------------------------------------------------------------------------------
/core/launcherapps/src/main/kotlin/dev/mslalith/focuslauncher/core/launcherapps/manager/iconcache/IconCacheManager.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.launcherapps.manager.iconcache
2 |
3 | import android.graphics.drawable.Drawable
4 | import dev.mslalith.focuslauncher.core.launcherapps.parser.IconPackXmlParser
5 | import dev.mslalith.focuslauncher.core.model.IconPackType
6 | import dev.mslalith.focuslauncher.core.model.app.AppWithComponent
7 |
8 | internal interface IconCacheManager {
9 | fun clearCache()
10 | fun iconPackFor(packageName: String): IconPackXmlParser
11 | fun iconFor(appWithComponent: AppWithComponent, iconPackType: IconPackType): Drawable
12 | }
13 |
--------------------------------------------------------------------------------
/core/launcherapps/src/main/kotlin/dev/mslalith/focuslauncher/core/launcherapps/manager/iconpack/IconPackManager.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.launcherapps.manager.iconpack
2 |
3 | import dev.mslalith.focuslauncher.core.launcherapps.model.IconPack
4 | import dev.mslalith.focuslauncher.core.model.IconPackLoadEvent
5 | import dev.mslalith.focuslauncher.core.model.IconPackType
6 | import kotlinx.coroutines.flow.Flow
7 |
8 | interface IconPackManager {
9 | val iconPacksFlow: Flow>
10 | val iconPackLoadEventFlow: Flow
11 |
12 | fun fetchInstalledIconPacks()
13 | suspend fun loadIconPack(iconPackType: IconPackType)
14 | suspend fun reloadIconPack()
15 | }
16 |
--------------------------------------------------------------------------------
/core/launcherapps/src/main/kotlin/dev/mslalith/focuslauncher/core/launcherapps/manager/launcherapps/LauncherAppsManager.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.launcherapps.manager.launcherapps
2 |
3 | import dev.mslalith.focuslauncher.core.model.app.AppWithComponent
4 |
5 | interface LauncherAppsManager {
6 | suspend fun loadAllApps(): List
7 | suspend fun loadApp(packageName: String): AppWithComponent?
8 | suspend fun defaultFavoriteApps(): List
9 | }
10 |
--------------------------------------------------------------------------------
/core/launcherapps/src/main/kotlin/dev/mslalith/focuslauncher/core/launcherapps/model/DrawableInfo.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.launcherapps.model
2 |
3 | import androidx.annotation.DrawableRes
4 | import java.util.Calendar
5 |
6 | internal sealed class DrawableInfo(
7 | open val drawableName: String
8 | ) {
9 | @DrawableRes
10 | abstract fun getDrawableResId(): Int
11 | }
12 |
13 | internal class SimpleDrawableInfo(
14 | override val drawableName: String,
15 | @DrawableRes val drawableId: Int
16 | ) : DrawableInfo(drawableName = drawableName) {
17 | override fun getDrawableResId(): Int = drawableId
18 | }
19 |
20 | internal class CalendarDrawableInfo(
21 | override val drawableName: String
22 | ) : DrawableInfo(drawableName = drawableName) {
23 |
24 | private val drawableForDay = IntArray(size = 31)
25 |
26 | fun setDrawableForDay(day: Int, @DrawableRes drawableId: Int) {
27 | drawableForDay[day] = drawableId
28 | }
29 |
30 | override fun getDrawableResId(): Int = drawableForDay[Calendar.getInstance()[Calendar.DAY_OF_MONTH] - 1]
31 | }
32 |
--------------------------------------------------------------------------------
/core/launcherapps/src/main/kotlin/dev/mslalith/focuslauncher/core/launcherapps/model/IconPack.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.launcherapps.model
2 |
3 | data class IconPack(
4 | val label: String,
5 | val packageName: String
6 | )
7 |
--------------------------------------------------------------------------------
/core/launcherapps/src/main/kotlin/dev/mslalith/focuslauncher/core/launcherapps/providers/icons/IconProvider.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.launcherapps.providers.icons
2 |
3 | import android.graphics.drawable.Drawable
4 | import dev.mslalith.focuslauncher.core.model.IconPackType
5 | import dev.mslalith.focuslauncher.core.model.app.AppWithComponent
6 |
7 | interface IconProvider {
8 | fun iconFor(appWithComponent: AppWithComponent, iconPackType: IconPackType): Drawable
9 | }
10 |
--------------------------------------------------------------------------------
/core/launcherapps/src/main/kotlin/dev/mslalith/focuslauncher/core/launcherapps/providers/icons/impl/IconProviderImpl.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.launcherapps.providers.icons.impl
2 |
3 | import android.graphics.drawable.Drawable
4 | import dev.mslalith.focuslauncher.core.launcherapps.manager.iconcache.IconCacheManager
5 | import dev.mslalith.focuslauncher.core.launcherapps.providers.icons.IconProvider
6 | import dev.mslalith.focuslauncher.core.model.IconPackType
7 | import dev.mslalith.focuslauncher.core.model.app.AppWithComponent
8 | import javax.inject.Inject
9 |
10 | internal class IconProviderImpl @Inject constructor(
11 | private val iconCacheManager: IconCacheManager
12 | ) : IconProvider {
13 |
14 | override fun iconFor(appWithComponent: AppWithComponent, iconPackType: IconPackType): Drawable = iconCacheManager.iconFor(
15 | appWithComponent = appWithComponent,
16 | iconPackType = iconPackType
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/core/launcherapps/src/main/kotlin/dev/mslalith/focuslauncher/core/launcherapps/providers/icons/test/TestIconProvider.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.launcherapps.providers.icons.test
2 |
3 | import android.graphics.Color
4 | import android.graphics.drawable.ColorDrawable
5 | import android.graphics.drawable.Drawable
6 | import dev.mslalith.focuslauncher.core.launcherapps.providers.icons.IconProvider
7 | import dev.mslalith.focuslauncher.core.model.IconPackType
8 | import dev.mslalith.focuslauncher.core.model.app.AppWithComponent
9 |
10 | object TestIconProvider : IconProvider {
11 |
12 | private var drawable = ColorDrawable(Color.WHITE)
13 |
14 | fun setIconColor(color: Int) {
15 | drawable = ColorDrawable(color)
16 | }
17 |
18 | override fun iconFor(appWithComponent: AppWithComponent, iconPackType: IconPackType): Drawable = drawable
19 | }
20 |
--------------------------------------------------------------------------------
/core/lint/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/lint/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.kotlin.library")
3 | }
4 |
5 | dependencies {
6 | detektPlugins(libs.detekt.formatting)
7 | }
8 |
--------------------------------------------------------------------------------
/core/lint/src/main/kotlin/dev/mslalith/focuslauncher/core/lint/detekt/IgnoreCyclomaticComplexMethod.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.lint.detekt
2 |
3 | @Retention(value = AnnotationRetention.BINARY)
4 | @Target(AnnotationTarget.FUNCTION)
5 | annotation class IgnoreCyclomaticComplexMethod
6 |
--------------------------------------------------------------------------------
/core/lint/src/main/kotlin/dev/mslalith/focuslauncher/core/lint/detekt/IgnoreLongMethod.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.lint.detekt
2 |
3 | @Retention(value = AnnotationRetention.BINARY)
4 | @Target(AnnotationTarget.FUNCTION)
5 | annotation class IgnoreLongMethod
6 |
--------------------------------------------------------------------------------
/core/lint/src/main/kotlin/dev/mslalith/focuslauncher/core/lint/detekt/IgnoreNestedBlockDepth.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.lint.detekt
2 |
3 | @Retention(value = AnnotationRetention.BINARY)
4 | @Target(AnnotationTarget.FUNCTION)
5 | annotation class IgnoreNestedBlockDepth
6 |
--------------------------------------------------------------------------------
/core/lint/src/main/kotlin/dev/mslalith/focuslauncher/core/lint/kover/IgnoreInKoverReport.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.lint.kover
2 |
3 | @Retention(value = AnnotationRetention.BINARY)
4 | @Target(
5 | AnnotationTarget.FILE,
6 | AnnotationTarget.CLASS,
7 | AnnotationTarget.FUNCTION,
8 | AnnotationTarget.PROPERTY
9 | )
10 | annotation class IgnoreInKoverReport
11 |
--------------------------------------------------------------------------------
/core/model/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/model/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.library")
3 | id("focuslauncher.lint")
4 | alias(libs.plugins.kotlin.serialization)
5 | }
6 |
7 | android {
8 | namespace = "dev.mslalith.focuslauncher.core.model"
9 | }
10 |
11 | dependencies {
12 | implementation(projects.core.resources)
13 |
14 | implementation(platform(libs.androidx.compose.bom))
15 | implementation(libs.androidx.compose.runtime)
16 |
17 | implementation(libs.kotlinx.datetime)
18 | implementation(libs.kotlinx.serialization)
19 | implementation(libs.androidx.annotation)
20 | implementation(libs.suncalc)
21 | }
22 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/AppDrawerViewType.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model
2 |
3 | enum class AppDrawerViewType(
4 | val index: Int,
5 | val uiText: UiText
6 | ) {
7 | LIST(
8 | index = 0,
9 | uiText = UiText.Resource(stringRes = R.string.list)
10 | ),
11 | GRID(
12 | index = 1,
13 | uiText = UiText.Resource(stringRes = R.string.grid)
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/BuildFlavor.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model
2 |
3 | var BUILD_FLAVOR: BuildFlavor = BuildFlavor.Store
4 |
5 | enum class BuildFlavor(val id: String) {
6 | Dev(id = "dev"),
7 | Store(id = "store");
8 |
9 | companion object {
10 | fun fromId(id: String) = entries.firstOrNull { it.id == id } ?: Store
11 | }
12 |
13 | fun isDev(): Boolean = this == Dev
14 | }
15 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/ClockAlignment.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model
2 |
3 | enum class ClockAlignment(
4 | val index: Int,
5 | val uiText: UiText
6 | ) {
7 | START(
8 | index = 0,
9 | uiText = UiText.Resource(stringRes = R.string.start)
10 | ),
11 | CENTER(
12 | index = 1,
13 | uiText = UiText.Resource(stringRes = R.string.center)
14 | ),
15 | END(
16 | index = 2,
17 | uiText = UiText.Resource(stringRes = R.string.end)
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/ConfirmSelectableItemType.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model
2 |
3 | import androidx.annotation.DrawableRes
4 | import androidx.compose.runtime.Immutable
5 |
6 | @Immutable
7 | sealed class ConfirmSelectableItemType {
8 | data class Icon(
9 | @DrawableRes val iconRes: Int,
10 | val contentDescription: String? = null
11 | ) : ConfirmSelectableItemType()
12 |
13 | data class Checkbox(
14 | val checked: Boolean,
15 | val disabled: Boolean = false
16 | ) : ConfirmSelectableItemType()
17 | }
18 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/CurrentPlace.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model
2 |
3 | import dev.mslalith.focuslauncher.core.model.location.LatLng
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class CurrentPlace(
8 | val latLng: LatLng,
9 | val address: String
10 | )
11 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/IconPackLoadEvent.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model
2 |
3 | sealed class IconPackLoadEvent(open val iconPackType: IconPackType) {
4 | data class Loading(override val iconPackType: IconPackType) : IconPackLoadEvent(iconPackType = iconPackType)
5 | data class Loaded(override val iconPackType: IconPackType) : IconPackLoadEvent(iconPackType = iconPackType)
6 | data class Reloading(override val iconPackType: IconPackType) : IconPackLoadEvent(iconPackType = iconPackType)
7 | data class Reloaded(override val iconPackType: IconPackType) : IconPackLoadEvent(iconPackType = iconPackType)
8 | }
9 |
10 | val IconPackLoadEvent.isTerminal: Boolean
11 | get() = when (this) {
12 | is IconPackLoadEvent.Loaded, is IconPackLoadEvent.Reloaded -> true
13 | else -> false
14 | }
15 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/IconPackType.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model
2 |
3 | sealed interface IconPackType {
4 | data object System : IconPackType
5 | data class Custom(val packageName: String) : IconPackType
6 |
7 | val value: String
8 | get() = when (this) {
9 | is Custom -> packageName
10 | System -> "default"
11 | }
12 |
13 | companion object {
14 | fun isSystemPack(value: String): Boolean = value == "default"
15 |
16 | fun from(value: String): IconPackType = when (value) {
17 | "default" -> System
18 | else -> Custom(packageName = value)
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/PackageAction.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model
2 |
3 | sealed interface PackageAction {
4 | data class Added(val packageName: String) : PackageAction
5 | data class Updated(val packageName: String) : PackageAction
6 | data class Removed(val packageName: String) : PackageAction
7 | }
8 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/Quote.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model
2 |
3 | import androidx.compose.runtime.Immutable
4 |
5 | @Immutable
6 | data class Quote(
7 | val id: String,
8 | val quote: String,
9 | val author: String
10 | )
11 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/Theme.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model
2 |
3 | enum class Theme(val uiText: UiText) {
4 | FOLLOW_SYSTEM(uiText = UiText.Resource(stringRes = R.string.follow_system)),
5 | NOT_WHITE(uiText = UiText.Resource(stringRes = R.string.not_white)),
6 | SAID_DARK(uiText = UiText.Resource(stringRes = R.string.said_dark))
7 | }
8 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/UiText.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model
2 |
3 | import android.content.Context
4 | import androidx.annotation.StringRes
5 | import androidx.compose.runtime.Immutable
6 |
7 | @Immutable
8 | sealed interface UiText {
9 |
10 | data class Static(val text: String) : UiText
11 | data class Resource(@StringRes val stringRes: Int) : UiText
12 |
13 | fun string(context: Context): String = when (this) {
14 | is Static -> text
15 | is Resource -> context.getString(stringRes)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/WidgetType.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model
2 |
3 | enum class WidgetType(val uiText: UiText) {
4 | CLOCK(uiText = UiText.Resource(stringRes = R.string.clock)),
5 | LUNAR_PHASE(uiText = UiText.Resource(stringRes = R.string.lunar_phase)),
6 | QUOTES(uiText = UiText.Resource(stringRes = R.string.quotes))
7 | }
8 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/app/App.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model.app
2 |
3 | import androidx.compose.runtime.Immutable
4 | import kotlinx.serialization.Serializable
5 |
6 | @Immutable
7 | @Serializable
8 | data class App(
9 | val name: String,
10 | val displayName: String = name,
11 | val packageName: String,
12 | val isSystem: Boolean
13 | )
14 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/app/AppWithColor.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model.app
2 |
3 | import android.graphics.Color
4 | import androidx.compose.runtime.Immutable
5 |
6 | @Immutable
7 | data class AppWithColor(
8 | val app: App,
9 | val color: Color?
10 | )
11 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/app/AppWithComponent.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model.app
2 |
3 | import android.content.ComponentName
4 |
5 | data class AppWithComponent(
6 | val app: App,
7 | val componentName: ComponentName
8 | )
9 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/app/AppWithIcon.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model.app
2 |
3 | import android.graphics.drawable.Drawable
4 | import androidx.compose.runtime.Immutable
5 | import dev.mslalith.focuslauncher.core.model.extensions.generateHashCode
6 |
7 | @Immutable
8 | data class AppWithIcon(
9 | val app: App,
10 | val icon: Drawable
11 | ) {
12 | val uniqueKey: Int
13 | get() = listOf(app.packageName, icon).generateHashCode()
14 | }
15 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/app/SelectedApp.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model.app
2 |
3 | import androidx.compose.runtime.Immutable
4 |
5 | @Immutable
6 | data class SelectedApp(
7 | val app: App,
8 | val isSelected: Boolean,
9 | val disabled: Boolean = false
10 | )
11 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/app/SelectedHiddenApp.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model.app
2 |
3 | import androidx.compose.runtime.Immutable
4 |
5 | @Immutable
6 | data class SelectedHiddenApp(
7 | val app: App,
8 | val isSelected: Boolean,
9 | val isFavorite: Boolean
10 | )
11 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/appdrawer/AppDrawerIconViewType.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model.appdrawer
2 |
3 | import dev.mslalith.focuslauncher.core.model.R
4 | import dev.mslalith.focuslauncher.core.model.UiText
5 |
6 | enum class AppDrawerIconViewType(
7 | val index: Int,
8 | val uiText: UiText
9 | ) {
10 | TEXT(index = 1, uiText = UiText.Resource(stringRes = R.string.text)),
11 | ICONS(index = 2, uiText = UiText.Resource(stringRes = R.string.icons)),
12 | COLORED(index = 3, uiText = UiText.Resource(stringRes = R.string.colored))
13 | }
14 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/appdrawer/AppDrawerItem.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model.appdrawer
2 |
3 | import android.graphics.Color
4 | import android.graphics.drawable.Drawable
5 | import androidx.compose.runtime.Immutable
6 | import dev.mslalith.focuslauncher.core.model.app.App
7 | import dev.mslalith.focuslauncher.core.model.extensions.generateHashCode
8 |
9 | @Immutable
10 | data class AppDrawerItem(
11 | val app: App,
12 | val isFavorite: Boolean,
13 | val icon: Drawable,
14 | val color: Color?
15 | ) {
16 | val uniqueKey: Int
17 | get() = listOf(app.packageName, icon, color).generateHashCode()
18 | }
19 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/extensions/UtilityExtensions.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model.extensions
2 |
3 | fun List.generateHashCode(): Int {
4 | if (isEmpty()) return hashCode()
5 |
6 | var result = first().hashCode()
7 | drop(n = 1).forEach {
8 | result = 31 * result + it.hashCode()
9 | }
10 | return result
11 | }
12 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/location/LatLng.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model.location
2 |
3 | import androidx.compose.runtime.Immutable
4 | import kotlinx.serialization.Serializable
5 |
6 | @Immutable
7 | @Serializable
8 | data class LatLng(
9 | val latitude: Double,
10 | val longitude: Double
11 | )
12 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/lunarphase/LunarPhaseDirection.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model.lunarphase
2 |
3 | enum class LunarPhaseDirection {
4 | NEW_TO_FULL,
5 | FULL_TO_NEW
6 | }
7 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/lunarphase/NextPhaseDetails.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model.lunarphase
2 |
3 | import androidx.compose.runtime.Immutable
4 | import kotlinx.datetime.LocalDateTime
5 |
6 | @Immutable
7 | data class NextPhaseDetails(
8 | val newMoon: LocalDateTime?,
9 | val fullMoon: LocalDateTime?
10 | )
11 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/lunarphase/RiseAndSetDetails.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model.lunarphase
2 |
3 | import kotlinx.datetime.LocalDateTime
4 |
5 | data class RiseAndSetDetails(
6 | val riseDateTime: LocalDateTime?,
7 | val setDateTime: LocalDateTime?
8 | )
9 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/lunarphase/UpcomingLunarPhase.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model.lunarphase
2 |
3 | import androidx.compose.runtime.Immutable
4 | import kotlinx.datetime.LocalDateTime
5 |
6 | @Immutable
7 | data class UpcomingLunarPhase(
8 | val lunarPhase: LunarPhase,
9 | val dateTime: LocalDateTime?,
10 | val isMicroMoon: Boolean,
11 | val isSuperMoon: Boolean
12 | )
13 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/place/Address.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model.place
2 |
3 | data class Address(
4 | val state: String?,
5 | val country: String?
6 | )
7 |
--------------------------------------------------------------------------------
/core/model/src/main/kotlin/dev/mslalith/focuslauncher/core/model/place/Place.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.model.place
2 |
3 | import dev.mslalith.focuslauncher.core.model.location.LatLng
4 |
5 | data class Place(
6 | val id: Long,
7 | val license: String,
8 | val latLng: LatLng,
9 | val displayName: String,
10 | val address: Address
11 | ) {
12 | companion object {
13 | fun default() = Place(
14 | id = -1,
15 | license = "",
16 | latLng = LatLng(
17 | latitude = 0.0,
18 | longitude = 0.0
19 | ),
20 | displayName = "",
21 | address = Address(
22 | state = null,
23 | country = null
24 | )
25 | )
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/core/resources/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/resources/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.library")
3 | }
4 |
5 | android {
6 | namespace = "dev.mslalith.focuslauncher.core.resources"
7 | }
8 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_align_horizontal_center.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_align_horizontal_left.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_align_horizontal_right.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_app_drawer_colored.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_app_drawer_icons.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_app_drawer_text.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_arrow_left.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_broom.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_check.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_close.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_delete.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_device_mobile.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_drag_indicator.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_edit.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_grid.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_hand_swipe_left.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_heart.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_house.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_info.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_list.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_logo_github.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_logo_phosphor.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_logo_twitter.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_map_pin.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_map_pin_line.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_moon_stars.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_quote.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_search.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_star.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_star_outline.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_sun_dim.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/drawable/ic_visibility_off.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/core/resources/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #313131
4 |
--------------------------------------------------------------------------------
/core/screens/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.library")
3 | alias(libs.plugins.kotlin.parcelize)
4 | }
5 |
6 | android {
7 | namespace = "dev.mslalith.focuslauncher.core.screens"
8 | }
9 |
10 | dependencies {
11 | implementation(projects.core.model)
12 |
13 | implementation(libs.circuit.runtime)
14 | }
15 |
--------------------------------------------------------------------------------
/core/screens/src/main/kotlin/dev/mslalith/focuslauncher/core/screens/Screens.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.screens
2 |
3 | import com.slack.circuit.runtime.screen.Screen
4 | import kotlinx.parcelize.Parcelize
5 |
6 | @Parcelize
7 | data object LauncherScreen : Screen
8 |
9 | @Parcelize
10 | data object HomePageScreen : Screen
11 |
12 | @Parcelize
13 | data object SettingsPageScreen : Screen
14 |
15 | @Parcelize
16 | data object AppDrawerPageScreen : Screen
17 |
18 | @Parcelize
19 | data object EditFavoritesScreen : Screen
20 |
21 | @Parcelize
22 | data object HideAppsScreen: Screen
23 |
24 | @Parcelize
25 | data object CurrentPlaceScreen : Screen
26 |
27 | @Parcelize
28 | data object IconPackScreen : Screen
29 |
30 | @Parcelize
31 | data object AboutScreen : Screen
32 |
33 | @Parcelize
34 | data object DeveloperScreen : Screen
35 |
--------------------------------------------------------------------------------
/core/screens/src/main/kotlin/dev/mslalith/focuslauncher/core/screens/UiComponentScreens.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.screens
2 |
3 | import com.slack.circuit.runtime.screen.Screen
4 | import kotlinx.parcelize.Parcelize
5 |
6 | @Parcelize
7 | data object ClockWidgetUiComponentScreen : Screen
8 |
9 | @Parcelize
10 | data object LunarCalendarUiComponentScreen : Screen
11 |
12 | @Parcelize
13 | data object QuoteForYouUiComponentScreen : Screen
14 |
15 | @Parcelize
16 | data object FavoritesListUiComponentScreen : Screen
17 |
--------------------------------------------------------------------------------
/core/settings/sentry/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.library")
3 | id("focuslauncher.android.hilt")
4 | }
5 |
6 | android {
7 | namespace = "dev.mslalith.focuslauncher.core.settings.sentry"
8 | }
9 |
10 | dependencies {
11 | implementation(projects.core.data)
12 |
13 | implementation(platform(libs.sentry.bom))
14 | implementation(libs.sentry.android.core)
15 | }
16 |
--------------------------------------------------------------------------------
/core/settings/sentry/src/main/kotlin/dev/mslalith/focuslauncher/core/settings/sentry/FakeSentrySettings.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.settings.sentry
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import kotlinx.coroutines.flow.MutableStateFlow
5 |
6 | class FakeSentrySettings : SentrySettings {
7 |
8 | private val _isEnabledStateFlow = MutableStateFlow(value = true)
9 | override val isEnabled: Flow = _isEnabledStateFlow
10 |
11 | private var sentryDsnInternal: String = ""
12 |
13 | override fun enableSentry() = Unit
14 |
15 | override fun disableSentry() = Unit
16 |
17 | override fun getSentryDsn(): String = sentryDsnInternal
18 |
19 | fun setSentryDsn(dsn: String) {
20 | sentryDsnInternal = dsn
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/core/settings/sentry/src/main/kotlin/dev/mslalith/focuslauncher/core/settings/sentry/SentrySettings.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.settings.sentry
2 |
3 | import kotlinx.coroutines.flow.Flow
4 |
5 | interface SentrySettings {
6 | val isEnabled: Flow
7 |
8 | fun enableSentry()
9 | fun disableSentry()
10 | fun getSentryDsn(): String
11 | }
12 |
--------------------------------------------------------------------------------
/core/settings/sentry/src/main/kotlin/dev/mslalith/focuslauncher/core/settings/sentry/di/SentryModule.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.settings.sentry.di
2 |
3 | import dagger.Binds
4 | import dagger.Module
5 | import dagger.hilt.InstallIn
6 | import dagger.hilt.components.SingletonComponent
7 | import dev.mslalith.focuslauncher.core.settings.sentry.SentrySettings
8 | import dev.mslalith.focuslauncher.core.settings.sentry.SentrySettingsImpl
9 | import javax.inject.Singleton
10 |
11 | @Module
12 | @InstallIn(SingletonComponent::class)
13 | abstract class SentryModule {
14 |
15 | @Binds
16 | @Singleton
17 | abstract fun bindSentrySettings(sentrySettingsImpl: SentrySettingsImpl): SentrySettings
18 | }
19 |
--------------------------------------------------------------------------------
/core/settings/sentry/src/main/kotlin/dev/mslalith/focuslauncher/core/settings/sentry/di/TestSentryModule.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.settings.sentry.di
2 |
3 | import dagger.Module
4 | import dagger.Provides
5 | import dagger.hilt.components.SingletonComponent
6 | import dagger.hilt.testing.TestInstallIn
7 | import dev.mslalith.focuslauncher.core.settings.sentry.FakeSentrySettings
8 | import dev.mslalith.focuslauncher.core.settings.sentry.SentrySettings
9 | import javax.inject.Singleton
10 |
11 | @Module
12 | @TestInstallIn(
13 | components = [SingletonComponent::class],
14 | replaces = [SentryModule::class]
15 | )
16 | internal object TestSentryModule {
17 |
18 | @Provides
19 | @Singleton
20 | fun provideSentrySettings(): SentrySettings = FakeSentrySettings()
21 | }
22 |
--------------------------------------------------------------------------------
/core/testing-circuit/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.library")
3 | }
4 |
5 | android {
6 | namespace = "dev.mslalith.focuslauncher.core.testing.circuit"
7 |
8 | packaging {
9 | resources.excludes.add("META-INF/*")
10 | }
11 | }
12 |
13 | dependencies {
14 | api(projects.core.testing)
15 |
16 | implementation(libs.circuit.foundation)
17 | api(libs.circuit.test)
18 | }
19 |
--------------------------------------------------------------------------------
/core/testing-compose/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/testing-compose/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.library")
3 | id("focuslauncher.android.library.compose")
4 | }
5 |
6 | android {
7 | namespace = "dev.mslalith.focuslauncher.core.testing.compose"
8 | }
9 |
10 | dependencies {
11 | implementation(projects.core.model)
12 |
13 | implementation(libs.androidx.compose.ui.test)
14 | }
15 |
--------------------------------------------------------------------------------
/core/testing-compose/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/compose/assertion/AssertBiasAlignment.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing.compose.assertion
2 |
3 | import androidx.compose.ui.BiasAlignment
4 | import androidx.compose.ui.test.SemanticsMatcher
5 | import androidx.compose.ui.test.SemanticsNodeInteraction
6 | import androidx.compose.ui.test.assert
7 | import dev.mslalith.focuslauncher.core.testing.compose.TestSemanticsProperties
8 |
9 | fun SemanticsNodeInteraction.assertBiasAlignment(
10 | biasAlignment: BiasAlignment.Horizontal
11 | ): SemanticsNodeInteraction = assert(
12 | matcher = SemanticsMatcher.expectValue(
13 | key = TestSemanticsProperties.BiasAlignmentHorizontal,
14 | expectedValue = biasAlignment
15 | )
16 | )
17 |
18 | fun SemanticsNodeInteraction.assertBiasAlignment(
19 | biasAlignment: BiasAlignment.Vertical
20 | ): SemanticsNodeInteraction = assert(
21 | matcher = SemanticsMatcher.expectValue(
22 | key = TestSemanticsProperties.BiasAlignmentVertical,
23 | expectedValue = biasAlignment
24 | )
25 | )
26 |
--------------------------------------------------------------------------------
/core/testing-compose/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/compose/assertion/AssertPrimitiveTypes.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing.compose.assertion
2 |
3 | import androidx.compose.ui.test.SemanticsMatcher
4 | import androidx.compose.ui.test.SemanticsNodeInteraction
5 | import androidx.compose.ui.test.assert
6 | import dev.mslalith.focuslauncher.core.testing.compose.TestSemanticsProperties
7 |
8 | fun SemanticsNodeInteraction.assertStringType(
9 | value: String
10 | ): SemanticsNodeInteraction = assert(
11 | matcher = SemanticsMatcher.expectValue(
12 | key = TestSemanticsProperties.StringType,
13 | expectedValue = value
14 | )
15 | )
16 |
17 | fun SemanticsNodeInteraction.assertBooleanType(
18 | value: Boolean
19 | ): SemanticsNodeInteraction = assert(
20 | matcher = SemanticsMatcher.expectValue(
21 | key = TestSemanticsProperties.BooleanType,
22 | expectedValue = value
23 | )
24 | )
25 |
--------------------------------------------------------------------------------
/core/testing-compose/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/compose/assertion/AssertSelectedApp.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing.compose.assertion
2 |
3 | import androidx.compose.ui.test.SemanticsNodeInteraction
4 | import androidx.compose.ui.test.assert
5 | import dev.mslalith.focuslauncher.core.model.app.SelectedApp
6 | import dev.mslalith.focuslauncher.core.testing.compose.matcher.hasSelectedApp
7 |
8 | fun SemanticsNodeInteraction.assertSelectedApp(
9 | selectedApp: SelectedApp
10 | ): SemanticsNodeInteraction = assert(matcher = hasSelectedApp(selectedApp = selectedApp))
11 |
--------------------------------------------------------------------------------
/core/testing-compose/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/compose/assertion/AssertSelectedHiddenApp.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing.compose.assertion
2 |
3 | import androidx.compose.ui.test.SemanticsNodeInteraction
4 | import androidx.compose.ui.test.assert
5 | import dev.mslalith.focuslauncher.core.model.app.SelectedHiddenApp
6 | import dev.mslalith.focuslauncher.core.testing.compose.matcher.hasSelectedHiddenApp
7 |
8 | fun SemanticsNodeInteraction.assertSelectedHiddenApp(
9 | selectedHiddenApp: SelectedHiddenApp
10 | ): SemanticsNodeInteraction = assert(matcher = hasSelectedHiddenApp(selectedHiddenApp = selectedHiddenApp))
11 |
--------------------------------------------------------------------------------
/core/testing-compose/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/compose/assertion/AssertWidgetType.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing.compose.assertion
2 |
3 | import androidx.compose.ui.test.SemanticsMatcher
4 | import androidx.compose.ui.test.SemanticsNodeInteraction
5 | import androidx.compose.ui.test.assert
6 | import dev.mslalith.focuslauncher.core.model.WidgetType
7 | import dev.mslalith.focuslauncher.core.testing.compose.TestSemanticsProperties
8 |
9 | fun SemanticsNodeInteraction.assertWidgetType(
10 | widgetType: WidgetType
11 | ): SemanticsNodeInteraction = assert(
12 | matcher = SemanticsMatcher.expectValue(
13 | key = TestSemanticsProperties.WidgetType,
14 | expectedValue = widgetType
15 | )
16 | )
17 |
--------------------------------------------------------------------------------
/core/testing-compose/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/compose/extensions/GeneralExtensions.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing.compose.extensions
2 |
3 | import androidx.compose.ui.test.SemanticsNodeInteraction
4 | import androidx.compose.ui.test.SemanticsNodeInteractionCollection
5 | import androidx.compose.ui.test.performClick
6 | import androidx.compose.ui.test.performScrollTo
7 | import androidx.compose.ui.test.printToString
8 |
9 | fun SemanticsNodeInteractionCollection.printToConsole(maxDepth: Int = 100) {
10 | printToString(maxDepth = maxDepth).let(::println)
11 | }
12 |
13 | fun SemanticsNodeInteraction.printToConsole(maxDepth: Int = 100) {
14 | printToString(maxDepth = maxDepth).let(::println)
15 | }
16 |
17 | fun SemanticsNodeInteraction.performScrollToAndClick() {
18 | performScrollTo()
19 | performClick()
20 | }
21 |
--------------------------------------------------------------------------------
/core/testing-compose/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/compose/matcher/GeneralMatchers.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing.compose.matcher
2 |
3 | import androidx.compose.ui.test.SemanticsMatcher
4 | import androidx.compose.ui.test.SemanticsNodeInteraction
5 |
6 | fun SemanticsNodeInteraction.onMatchWith(
7 | matcher: SemanticsMatcher
8 | ): Boolean = matcher.matches(node = fetchSemanticsNode())
9 |
--------------------------------------------------------------------------------
/core/testing-compose/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/compose/matcher/MatcherPrimitiveTypes.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing.compose.matcher
2 |
3 | import androidx.compose.ui.test.SemanticsMatcher
4 | import dev.mslalith.focuslauncher.core.testing.compose.TestSemanticsProperties
5 |
6 | fun hasString(
7 | value: String
8 | ): SemanticsMatcher = SemanticsMatcher.expectValue(
9 | key = TestSemanticsProperties.StringType,
10 | expectedValue = value
11 | )
12 |
13 | fun hasBoolean(
14 | value: Boolean
15 | ): SemanticsMatcher = SemanticsMatcher.expectValue(
16 | key = TestSemanticsProperties.BooleanType,
17 | expectedValue = value
18 | )
19 |
--------------------------------------------------------------------------------
/core/testing-compose/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/compose/matcher/MatcherSelectedApp.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing.compose.matcher
2 |
3 | import androidx.compose.ui.test.SemanticsMatcher
4 | import dev.mslalith.focuslauncher.core.model.app.SelectedApp
5 | import dev.mslalith.focuslauncher.core.testing.compose.TestSemanticsProperties
6 |
7 | fun hasSelectedApp(
8 | selectedApp: SelectedApp
9 | ): SemanticsMatcher = SemanticsMatcher.expectValue(
10 | key = TestSemanticsProperties.SelectedApp,
11 | expectedValue = selectedApp
12 | )
13 |
--------------------------------------------------------------------------------
/core/testing-compose/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/compose/matcher/MatcherSelectedHiddenApp.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing.compose.matcher
2 |
3 | import androidx.compose.ui.test.SemanticsMatcher
4 | import dev.mslalith.focuslauncher.core.model.app.SelectedHiddenApp
5 | import dev.mslalith.focuslauncher.core.testing.compose.TestSemanticsProperties
6 |
7 | fun hasSelectedHiddenApp(
8 | selectedHiddenApp: SelectedHiddenApp
9 | ): SemanticsMatcher = SemanticsMatcher.expectValue(
10 | key = TestSemanticsProperties.SelectedHiddenApp,
11 | expectedValue = selectedHiddenApp
12 | )
13 |
--------------------------------------------------------------------------------
/core/testing-compose/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/compose/matcher/MatcherWidgetType.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing.compose.matcher
2 |
3 | import androidx.compose.ui.test.SemanticsMatcher
4 | import dev.mslalith.focuslauncher.core.model.WidgetType
5 | import dev.mslalith.focuslauncher.core.testing.compose.TestSemanticsProperties
6 |
7 | fun hasWidgetType(
8 | widgetType: WidgetType
9 | ): SemanticsMatcher = SemanticsMatcher.expectValue(
10 | key = TestSemanticsProperties.WidgetType,
11 | expectedValue = widgetType
12 | )
13 |
--------------------------------------------------------------------------------
/core/testing-compose/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/compose/modifier/testsemantics/TestSemanticsModifier.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing.compose.modifier.testsemantics
2 |
3 | import androidx.compose.ui.Modifier
4 | import androidx.compose.ui.platform.testTag
5 | import androidx.compose.ui.semantics.semantics
6 | import dev.mslalith.focuslauncher.core.testing.compose.modifier.testsemantics.impl.TestSemanticsScopeImpl
7 |
8 | fun Modifier.testSemantics(
9 | tag: String,
10 | block: TestSemanticsScope.() -> Unit = {}
11 | ): Modifier = this then testTag(tag = tag) then semantics {
12 | TestSemanticsScopeImpl(semanticsPropertyReceiver = this).block()
13 | }
14 |
--------------------------------------------------------------------------------
/core/testing-compose/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/compose/modifier/testsemantics/TestSemanticsScope.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing.compose.modifier.testsemantics
2 |
3 | import androidx.compose.ui.BiasAlignment
4 | import dev.mslalith.focuslauncher.core.model.WidgetType
5 | import dev.mslalith.focuslauncher.core.model.app.SelectedApp
6 | import dev.mslalith.focuslauncher.core.model.app.SelectedHiddenApp
7 |
8 | interface TestSemanticsScope {
9 | fun testString(value: String)
10 | fun testBoolean(value: Boolean)
11 |
12 | fun testBiasAlignment(biasAlignment: BiasAlignment.Horizontal)
13 | fun testBiasAlignment(biasAlignment: BiasAlignment.Vertical)
14 |
15 | fun testSelectedApp(selectedApp: SelectedApp)
16 | fun testSelectedHiddenApp(selectedHiddenApp: SelectedHiddenApp)
17 |
18 | fun testWidgetType(widgetType: WidgetType)
19 | }
20 |
--------------------------------------------------------------------------------
/core/testing/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/testing/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.library")
3 | }
4 |
5 | android {
6 | namespace = "dev.mslalith.focuslauncher.core.testing"
7 |
8 | packaging {
9 | resources.excludes.add("META-INF/*")
10 | }
11 | }
12 |
13 | dependencies {
14 | implementation(projects.core.model)
15 | api(projects.core.launcherapps)
16 |
17 | api(libs.junit4)
18 | implementation(libs.androidx.test.junit)
19 | api(libs.kotlinx.coroutines.test)
20 | implementation(libs.androidx.core.testing)
21 | api(libs.truth)
22 | api(libs.turbine)
23 | api(libs.mockk)
24 | api(libs.robolectric)
25 | implementation(libs.kotlinx.datetime)
26 |
27 | api(libs.androidx.test.runner)
28 | api(libs.hilt.android.testing)
29 |
30 | api(libs.ktor.client.mock)
31 | implementation(libs.ktor.client.core)
32 | implementation(libs.ktor.client.contentNegotiation)
33 | implementation(libs.ktor.serialization)
34 | }
35 |
--------------------------------------------------------------------------------
/core/testing/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/AppRobolectricTestRunner.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing
2 |
3 | import org.robolectric.RobolectricTestRunner
4 | import org.robolectric.annotation.Config
5 |
6 | class AppRobolectricTestRunner(testClass: Class<*>?) : RobolectricTestRunner(testClass) {
7 |
8 | override fun buildGlobalConfig(): Config {
9 | return Config.Builder()
10 | // .setSdk(33)
11 | .build()
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/core/testing/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/CoroutineTest.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing
2 |
3 | import androidx.arch.core.executor.testing.InstantTaskExecutorRule
4 | import app.cash.turbine.TurbineContext
5 | import app.cash.turbine.turbineScope
6 | import dev.mslalith.focuslauncher.core.testing.rules.TestCoroutineRule
7 | import dev.mslalith.focuslauncher.core.testing.rules.newCoroutineScope
8 | import kotlinx.coroutines.test.TestScope
9 | import kotlinx.coroutines.test.runTest
10 | import org.junit.Rule
11 |
12 | open class CoroutineTest {
13 |
14 | @get:Rule(order = 1)
15 | val instantExecutorRule = InstantTaskExecutorRule()
16 |
17 | @get:Rule(order = 2)
18 | val coroutineTestRule = TestCoroutineRule()
19 |
20 | protected val testDispatcher = coroutineTestRule.newCoroutineScope()
21 |
22 | protected fun runCoroutineTest(
23 | testBody: suspend context(TurbineContext, TestScope) () -> Unit
24 | ) = testDispatcher.runTest(
25 | testBody = {
26 | turbineScope { testBody(this, this@runTest) }
27 | }
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/core/testing/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/HiltTestRunner.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing
2 |
3 | import android.app.Application
4 | import android.content.Context
5 | import androidx.test.runner.AndroidJUnitRunner
6 | import dagger.hilt.android.testing.HiltTestApplication
7 |
8 | class HiltTestRunner : AndroidJUnitRunner() {
9 | override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application {
10 | return super.newApplication(cl, HiltTestApplication::class.java.name, context)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/core/testing/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/extensions/LocaleExtensions.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing.extensions
2 |
3 | import java.util.Locale
4 |
5 | fun withLocale(locale: Locale, block: () -> Unit) {
6 | val originalLocale = Locale.getDefault()
7 | Locale.setDefault(locale)
8 | block()
9 | Locale.setDefault(originalLocale)
10 | }
11 |
--------------------------------------------------------------------------------
/core/testing/src/main/kotlin/dev/mslalith/focuslauncher/core/testing/extensions/TurbineExtensions.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.testing.extensions
2 |
3 | import app.cash.turbine.TurbineContext
4 | import kotlinx.coroutines.flow.Flow
5 |
6 | context (TurbineContext)
7 | suspend fun Flow.awaitItem(): T {
8 | val turbine = testIn(scope = this@TurbineContext)
9 | val item = turbine.awaitItem()
10 | turbine.cancel()
11 | return item
12 | }
13 |
14 | context (TurbineContext)
15 | suspend fun Flow.awaitItemChangeUntil(
16 | awaitTill: (T) -> Boolean
17 | ): T {
18 | val turbine = testIn(scope = this@TurbineContext)
19 | var lastItem = turbine.expectMostRecentItem()
20 |
21 | while (!awaitTill(lastItem)) {
22 | lastItem = turbine.awaitItem()
23 | }
24 |
25 | turbine.cancel()
26 | return lastItem
27 | }
28 |
--------------------------------------------------------------------------------
/core/ui/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/ui/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.library")
3 | id("focuslauncher.android.library.compose")
4 | id("focuslauncher.android.library.compose.testing")
5 | }
6 |
7 | android {
8 | namespace = "dev.mslalith.focuslauncher.core.ui"
9 | }
10 |
11 | dependencies {
12 | implementation(projects.core.common)
13 | implementation(projects.core.model)
14 | implementation(projects.core.resources)
15 |
16 | implementation(libs.androidx.activity.compose)
17 | implementation(libs.androidx.lifecycle.runtime.compose)
18 | implementation(libs.androidx.navigation.compose)
19 | implementation(libs.kotlinx.collections.immutable)
20 | }
21 |
--------------------------------------------------------------------------------
/core/ui/src/main/kotlin/dev/mslalith/focuslauncher/core/ui/AppBarWithBackIcon.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.ui
2 |
3 | import androidx.compose.foundation.layout.RowScope
4 | import androidx.compose.material3.ExperimentalMaterial3Api
5 | import androidx.compose.material3.Text
6 | import androidx.compose.material3.TopAppBar
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.res.stringResource
9 |
10 | @OptIn(ExperimentalMaterial3Api::class)
11 | @Composable
12 | fun AppBarWithBackIcon(
13 | title: String,
14 | onBackPressed: () -> Unit,
15 | actions: @Composable RowScope.() -> Unit = {}
16 | ) {
17 | TopAppBar(
18 | navigationIcon = {
19 | RoundIcon(
20 | iconRes = R.drawable.ic_arrow_left,
21 | contentDescription = stringResource(id = R.string.go_back),
22 | onClick = onBackPressed
23 | )
24 | },
25 | title = {
26 | Text(text = title)
27 | },
28 | actions = actions
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/core/ui/src/main/kotlin/dev/mslalith/focuslauncher/core/ui/Spacer.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.ui
2 |
3 | import androidx.compose.foundation.layout.ColumnScope
4 | import androidx.compose.foundation.layout.RowScope
5 | import androidx.compose.foundation.layout.Spacer
6 | import androidx.compose.foundation.layout.height
7 | import androidx.compose.foundation.layout.width
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.unit.Dp
11 |
12 | @Composable
13 | fun HorizontalSpacer(spacing: Dp) = Spacer(modifier = Modifier.width(width = spacing))
14 |
15 | @Composable
16 | fun VerticalSpacer(spacing: Dp) = Spacer(modifier = Modifier.height(height = spacing))
17 |
18 | @Composable
19 | fun RowScope.FillSpacer() = Spacer(modifier = Modifier.weight(weight = 1f))
20 |
21 | @Composable
22 | fun ColumnScope.FillSpacer() = Spacer(modifier = Modifier.weight(weight = 1f))
23 |
--------------------------------------------------------------------------------
/core/ui/src/main/kotlin/dev/mslalith/focuslauncher/core/ui/effects/OnLifecycleEventChange.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.ui.effects
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.DisposableEffect
5 | import androidx.compose.runtime.getValue
6 | import androidx.compose.runtime.rememberUpdatedState
7 | import androidx.compose.ui.platform.LocalLifecycleOwner
8 | import androidx.lifecycle.Lifecycle
9 | import androidx.lifecycle.LifecycleEventObserver
10 |
11 | @Composable
12 | fun OnLifecycleEventChange(
13 | onEvent: (Lifecycle.Event) -> Unit
14 | ) {
15 | val updatedOnEvent by rememberUpdatedState(newValue = onEvent)
16 | val lifecycleOwner by rememberUpdatedState(newValue = LocalLifecycleOwner.current)
17 |
18 | DisposableEffect(key1 = lifecycleOwner) {
19 | val lifecycle = lifecycleOwner.lifecycle
20 | val observer = LifecycleEventObserver { _, event ->
21 | updatedOnEvent(event)
22 | }
23 |
24 | lifecycle.addObserver(observer = observer)
25 | onDispose { lifecycle.removeObserver(observer = observer) }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/core/ui/src/main/kotlin/dev/mslalith/focuslauncher/core/ui/extensions/ScaffoldExtensions.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.ui.extensions
2 |
3 | import androidx.compose.material3.SnackbarDuration
4 | import androidx.compose.material3.SnackbarHostState
5 | import androidx.compose.material3.SnackbarResult
6 | import dev.mslalith.focuslauncher.core.lint.kover.IgnoreInKoverReport
7 |
8 | @IgnoreInKoverReport
9 | suspend fun SnackbarHostState.showDismissibleSnackbar(
10 | message: String,
11 | duration: SnackbarDuration = SnackbarDuration.Short,
12 | dismissVisibleSnackbar: Boolean = true,
13 | discardIfShowing: Boolean = false,
14 | actionLabel: String? = null,
15 | onAction: ((SnackbarResult) -> Unit)? = null
16 | ) {
17 | if (discardIfShowing && currentSnackbarData != null) return
18 |
19 | if (dismissVisibleSnackbar) {
20 | currentSnackbarData?.dismiss()
21 | }
22 |
23 | val snackbarResult = showSnackbar(
24 | message = message,
25 | actionLabel = actionLabel,
26 | duration = duration
27 | )
28 | onAction?.invoke(snackbarResult)
29 | }
30 |
--------------------------------------------------------------------------------
/core/ui/src/main/kotlin/dev/mslalith/focuslauncher/core/ui/extensions/UiTextExtensions.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.ui.extensions
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.res.stringResource
5 | import dev.mslalith.focuslauncher.core.model.UiText
6 |
7 | @Composable
8 | fun UiText.string(): String = when (this) {
9 | is UiText.Static -> text
10 | is UiText.Resource -> stringResource(id = stringRes)
11 | }
12 |
--------------------------------------------------------------------------------
/core/ui/src/main/kotlin/dev/mslalith/focuslauncher/core/ui/providers/ProvideLauncherPagerState.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.core.ui.providers
2 |
3 | import androidx.compose.foundation.ExperimentalFoundationApi
4 | import androidx.compose.foundation.pager.PagerState
5 | import androidx.compose.foundation.pager.rememberPagerState
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.runtime.CompositionLocalProvider
8 | import androidx.compose.runtime.compositionLocalOf
9 | import dev.mslalith.focuslauncher.core.lint.kover.IgnoreInKoverReport
10 |
11 | @OptIn(ExperimentalFoundationApi::class)
12 | @IgnoreInKoverReport
13 | val LocalLauncherPagerState = compositionLocalOf {
14 | error("No PagerState provided")
15 | }
16 |
17 | @OptIn(ExperimentalFoundationApi::class)
18 | @Composable
19 | fun ProvideLauncherPagerState(
20 | content: @Composable () -> Unit
21 | ) {
22 | val pagerState = rememberPagerState(
23 | initialPage = 1,
24 | pageCount = { 3 }
25 | )
26 | CompositionLocalProvider(LocalLauncherPagerState provides pagerState) {
27 | content()
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/feature/appdrawerpage/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/feature/appdrawerpage/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.feature")
3 | id("focuslauncher.screen.new")
4 | }
5 |
6 | android {
7 | namespace = "dev.mslalith.focuslauncher.feature.appdrawerpage"
8 | }
9 |
10 | dependencies {
11 | implementation(libs.kotlinx.collections.immutable)
12 | }
13 |
--------------------------------------------------------------------------------
/feature/appdrawerpage/src/main/kotlin/dev/mslalith/focuslauncher/feature/appdrawerpage/bottomsheet/moreoptions/AppMoreOptionsBottomSheetContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.appdrawerpage.bottomsheet.moreoptions
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 | import dev.mslalith.focuslauncher.core.model.app.App
6 | import dev.mslalith.focuslauncher.core.model.appdrawer.AppDrawerItem
7 |
8 | data class AppMoreOptionsBottomSheetState(
9 | val appDrawerItem: AppDrawerItem,
10 | val eventSink: (AppMoreOptionsBottomSheetUiEvent) -> Unit
11 | ) : CircuitUiState
12 |
13 | sealed interface AppMoreOptionsBottomSheetUiEvent : CircuitUiEvent {
14 | data object GoBack : AppMoreOptionsBottomSheetUiEvent
15 | data class AddToFavorites(val app: App) : AppMoreOptionsBottomSheetUiEvent
16 | data class RemoveFromFavorites(val app: App) : AppMoreOptionsBottomSheetUiEvent
17 | data class AddToHiddenApps(val app: App, val removeFromFavorites: Boolean) : AppMoreOptionsBottomSheetUiEvent
18 | data class ClickUpdateDisplayName(val app: App) : AppMoreOptionsBottomSheetUiEvent
19 | }
20 |
--------------------------------------------------------------------------------
/feature/appdrawerpage/src/main/kotlin/dev/mslalith/focuslauncher/feature/appdrawerpage/bottomsheet/updateappdisplayname/UpdateAppDisplayNameBottomSheetContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.appdrawerpage.bottomsheet.updateappdisplayname
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 | import dev.mslalith.focuslauncher.core.model.app.App
6 |
7 | data class UpdateAppDisplayNameBottomSheetState(
8 | val app: App,
9 | val eventSink: (UpdateAppDisplayNameBottomSheetUiEvent) -> Unit
10 | ) : CircuitUiState
11 |
12 | sealed interface UpdateAppDisplayNameBottomSheetUiEvent : CircuitUiEvent {
13 | data class UpdateDisplayName(val displayName: String) : UpdateAppDisplayNameBottomSheetUiEvent
14 | }
15 |
--------------------------------------------------------------------------------
/feature/appdrawerpage/src/main/kotlin/dev/mslalith/focuslauncher/feature/appdrawerpage/utils/Constants.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.appdrawerpage.utils
2 |
3 | import androidx.compose.ui.unit.dp
4 | import dev.mslalith.focuslauncher.core.lint.kover.IgnoreInKoverReport
5 |
6 | @IgnoreInKoverReport
7 | internal object Constants {
8 | val ITEM_START_PADDING = 24.dp
9 | val ITEM_END_PADDING = 8.dp
10 | val APP_ICON_SIZE = 28.dp
11 | val ICON_INNER_HORIZONTAL_PADDING = 4.dp
12 | }
13 |
--------------------------------------------------------------------------------
/feature/clock24/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/feature/clock24/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.feature")
3 | id("focuslauncher.screen.new")
4 | id("focuslauncher.android.library.compose.testing")
5 | }
6 |
7 | android {
8 | namespace = "dev.mslalith.focuslauncher.feature.clock24"
9 | }
10 |
11 | dependencies {
12 | implementation(libs.kotlinx.datetime)
13 | implementation(libs.kotlinx.collections.immutable)
14 | }
15 |
--------------------------------------------------------------------------------
/feature/clock24/src/main/kotlin/dev/mslalith/focuslauncher/feature/clock24/model/AnalogClockHandlePhase.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.clock24.model
2 |
3 | internal enum class AnalogClockHandlePhase(val angle: Double) {
4 | NONE(angle = 135.0),
5 | TOP(angle = 270.0),
6 | RIGHT(angle = 0.0),
7 | BOTTOM(angle = 90.0),
8 | LEFT(angle = 180.0)
9 | }
10 |
--------------------------------------------------------------------------------
/feature/clock24/src/main/kotlin/dev/mslalith/focuslauncher/feature/clock24/utils/TestTags.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.clock24.utils
2 |
3 | import dev.mslalith.focuslauncher.core.lint.kover.IgnoreInKoverReport
4 |
5 | @IgnoreInKoverReport
6 | internal object TestTags {
7 | const val TAG_CLOCK_COLUMN = "tag_clock_column"
8 | const val TAG_CLOCK24 = "tag_clock24"
9 | const val TAG_REGULAR_CLOCK = "tag_regular_clock"
10 | }
11 |
--------------------------------------------------------------------------------
/feature/clock24/src/main/kotlin/dev/mslalith/focuslauncher/feature/clock24/widget/ClockWidgetUiComponentContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.clock24.widget
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 | import dev.mslalith.focuslauncher.core.model.ClockAlignment
6 |
7 | data class ClockWidgetUiComponentState(
8 | val currentTime: String,
9 | val showClock24: Boolean,
10 | val use24Hour: Boolean,
11 | val clockAlignment: ClockAlignment,
12 | val clock24AnimationDuration: Int,
13 | val eventSink: (ClockWidgetUiComponentUiEvent) -> Unit
14 | ) : CircuitUiState
15 |
16 | sealed interface ClockWidgetUiComponentUiEvent : CircuitUiEvent {
17 | data object RefreshTime : ClockWidgetUiComponentUiEvent
18 | }
19 |
--------------------------------------------------------------------------------
/feature/favorites/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/feature/favorites/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.feature")
3 | id("focuslauncher.screen.new")
4 | }
5 |
6 | android {
7 | namespace = "dev.mslalith.focuslauncher.feature.favorites"
8 | }
9 |
10 | dependencies {
11 | implementation(libs.androidx.palette.ktx)
12 | implementation(libs.kotlinx.collections.immutable)
13 | implementation(libs.reorderable)
14 | }
15 |
--------------------------------------------------------------------------------
/feature/favorites/src/main/kotlin/dev/mslalith/focuslauncher/feature/favorites/FavoritesListUiComponentContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.favorites
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 | import dev.mslalith.focuslauncher.core.model.app.AppWithColor
6 | import kotlinx.collections.immutable.ImmutableList
7 |
8 | data class FavoritesListUiComponentState(
9 | val favoritesList: ImmutableList,
10 | val eventSink: (FavoritesListUiComponentUiEvent) -> Unit
11 | ) : CircuitUiState
12 |
13 | sealed interface FavoritesListUiComponentUiEvent : CircuitUiEvent {
14 | data object AddDefaultAppsIfRequired : FavoritesListUiComponentUiEvent
15 | }
16 |
--------------------------------------------------------------------------------
/feature/favorites/src/main/kotlin/dev/mslalith/focuslauncher/feature/favorites/bottomsheet/FavoritesBottomSheetContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.favorites.bottomsheet
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 | import dev.mslalith.focuslauncher.core.model.app.AppWithColor
6 | import kotlinx.collections.immutable.ImmutableList
7 |
8 | data class FavoritesBottomSheetState(
9 | val favoritesList: ImmutableList,
10 | val eventSink: (FavoritesBottomSheetUiEvent) -> Unit
11 | ) : CircuitUiState
12 |
13 | sealed interface FavoritesBottomSheetUiEvent : CircuitUiEvent {
14 | data class Move(val fromIndex: Int, val toIndex: Int) : FavoritesBottomSheetUiEvent
15 | data class Remove(val appWithColor: AppWithColor) : FavoritesBottomSheetUiEvent
16 | data object Save : FavoritesBottomSheetUiEvent
17 | }
18 |
--------------------------------------------------------------------------------
/feature/homepage/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/feature/homepage/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.feature")
3 | id("focuslauncher.screen.new")
4 | }
5 |
6 | android {
7 | namespace = "dev.mslalith.focuslauncher.feature.homepage"
8 | }
9 |
10 | dependencies {
11 | implementation(projects.feature.clock24)
12 | implementation(projects.feature.lunarcalendar)
13 | implementation(projects.feature.quoteforyou)
14 | implementation(projects.feature.favorites)
15 | }
16 |
--------------------------------------------------------------------------------
/feature/homepage/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/feature/homepage/src/main/kotlin/dev/mslalith/focuslauncher/feature/homepage/HomePageContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.homepage
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 | import dev.mslalith.focuslauncher.feature.clock24.widget.ClockWidgetUiComponentState
6 | import dev.mslalith.focuslauncher.feature.favorites.FavoritesListUiComponentState
7 | import dev.mslalith.focuslauncher.feature.lunarcalendar.widget.LunarCalendarUiComponentState
8 | import dev.mslalith.focuslauncher.feature.quoteforyou.widget.QuoteForYouUiComponentState
9 |
10 | data class HomePageState(
11 | val isPullDownNotificationShadeEnabled: Boolean,
12 | val clockWidgetUiComponentState: ClockWidgetUiComponentState,
13 | val lunarCalendarUiComponentState: LunarCalendarUiComponentState,
14 | val quoteForYouUiComponentState: QuoteForYouUiComponentState,
15 | val favoritesListUiComponentState: FavoritesListUiComponentState
16 | ) : CircuitUiState
17 |
18 | sealed interface HomePageUiEvent : CircuitUiEvent
19 |
--------------------------------------------------------------------------------
/feature/homepage/src/main/kotlin/dev/mslalith/focuslauncher/feature/homepage/model/HomePadding.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.homepage.model
2 |
3 | import androidx.compose.foundation.layout.PaddingValues
4 | import androidx.compose.runtime.compositionLocalOf
5 | import androidx.compose.ui.unit.Dp
6 | import androidx.compose.ui.unit.dp
7 |
8 | internal val LocalHomePadding = compositionLocalOf {
9 | error("No LocalHomePadding provided")
10 | }
11 |
12 | internal data class HomePadding(
13 | val contentPaddingValues: PaddingValues = PaddingValues(
14 | start = 22.dp,
15 | end = 22.dp,
16 | top = 16.dp,
17 | bottom = 22.dp
18 | ),
19 | val lunarPhaseIconSize: Dp = 40.dp,
20 | val favoriteActionItemSize: Dp = 16.dp
21 | )
22 |
--------------------------------------------------------------------------------
/feature/lunarcalendar/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/feature/lunarcalendar/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.feature")
3 | id("focuslauncher.screen.new")
4 | }
5 |
6 | android {
7 | namespace = "dev.mslalith.focuslauncher.feature.lunarcalendar"
8 | }
9 |
10 | dependencies {
11 | implementation(libs.kotlinx.datetime)
12 | }
13 |
--------------------------------------------------------------------------------
/feature/lunarcalendar/src/main/kotlin/dev/mslalith/focuslauncher/feature/lunarcalendar/bottomsheet/lunarphasedetails/LunarPhaseDetailsBottomSheetContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.lunarcalendar.bottomsheet.lunarphasedetails
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 | import dev.mslalith.focuslauncher.core.common.model.State
6 | import dev.mslalith.focuslauncher.core.model.lunarphase.LunarPhaseDetails
7 |
8 | data class LunarPhaseDetailsBottomSheetState(
9 | val lunarPhaseDetails: State
10 | ) : CircuitUiState
11 |
12 | sealed interface LunarPhaseDetailsBottomSheetUiEvent : CircuitUiEvent
13 |
--------------------------------------------------------------------------------
/feature/lunarcalendar/src/main/kotlin/dev/mslalith/focuslauncher/feature/lunarcalendar/bottomsheet/lunarphasedetails/ui/RiseAndSetHeaders.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.lunarcalendar.bottomsheet.lunarphasedetails.ui
2 |
3 | import androidx.compose.foundation.layout.Row
4 | import androidx.compose.material3.MaterialTheme
5 | import androidx.compose.material3.Text
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.graphics.Color
8 | import androidx.compose.ui.res.stringResource
9 | import dev.mslalith.focuslauncher.core.ui.FillSpacer
10 | import dev.mslalith.focuslauncher.feature.lunarcalendar.R
11 |
12 | @Composable
13 | internal fun RiseAndSetHeaders(
14 | contentColor: Color
15 | ) {
16 | Row {
17 | Text(
18 | text = stringResource(id = R.string.moon),
19 | color = contentColor,
20 | style = MaterialTheme.typography.titleMedium
21 | )
22 | FillSpacer()
23 | Text(
24 | text = stringResource(id = R.string.sun),
25 | color = contentColor,
26 | style = MaterialTheme.typography.titleMedium
27 | )
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/feature/lunarcalendar/src/main/kotlin/dev/mslalith/focuslauncher/feature/lunarcalendar/bottomsheet/lunarphasedetails/ui/RiseAndSetIndicator.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.lunarcalendar.bottomsheet.lunarphasedetails.ui
2 |
3 | import androidx.compose.foundation.layout.Box
4 | import androidx.compose.foundation.layout.RowScope
5 | import androidx.compose.material3.MaterialTheme
6 | import androidx.compose.material3.Text
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Alignment
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.graphics.Color
11 |
12 | @Composable
13 | internal fun RowScope.RiseAndSetIndicator(
14 | text: String,
15 | contentColor: Color
16 | ) {
17 | Box(
18 | modifier = Modifier.weight(weight = 1f),
19 | contentAlignment = Alignment.Center
20 | ) {
21 | Text(
22 | text = text,
23 | color = contentColor,
24 | style = MaterialTheme.typography.bodySmall
25 | )
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/feature/lunarcalendar/src/main/kotlin/dev/mslalith/focuslauncher/feature/lunarcalendar/bottomsheet/lunarphasedetails/ui/TodayLunarMoonIconAndPhase.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.lunarcalendar.bottomsheet.lunarphasedetails.ui
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.unit.Dp
6 | import dev.mslalith.focuslauncher.core.model.lunarphase.LunarPhaseDetails
7 | import dev.mslalith.focuslauncher.feature.lunarcalendar.shared.LunarPhaseMoonIcon
8 |
9 | @Composable
10 | internal fun TodayLunarMoonIconAndPhase(
11 | lunarPhaseDetails: LunarPhaseDetails,
12 | moonSize: Dp
13 | ) {
14 | Column {
15 | LunarPhaseMoonIcon(
16 | phaseAngle = lunarPhaseDetails.phaseAngle,
17 | illumination = lunarPhaseDetails.illumination,
18 | moonSize = moonSize * 0.3f
19 | )
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/feature/lunarcalendar/src/main/kotlin/dev/mslalith/focuslauncher/feature/lunarcalendar/widget/LunarCalendarUiComponentContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.lunarcalendar.widget
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 | import dev.mslalith.focuslauncher.core.common.model.State
6 | import dev.mslalith.focuslauncher.core.model.lunarphase.LunarPhaseDetails
7 | import dev.mslalith.focuslauncher.core.model.lunarphase.UpcomingLunarPhase
8 |
9 | data class LunarCalendarUiComponentState(
10 | val showLunarPhase: Boolean,
11 | val showIlluminationPercent: Boolean,
12 | val showUpcomingPhaseDetails: Boolean,
13 | val lunarPhaseDetails: State,
14 | val upcomingLunarPhase: State
15 | ) : CircuitUiState
16 |
17 | sealed interface LunarCalendarUiComponentUiEvent : CircuitUiEvent
18 |
--------------------------------------------------------------------------------
/feature/quoteforyou/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/feature/quoteforyou/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.feature")
3 | id("focuslauncher.screen.new")
4 | }
5 |
6 | android {
7 | namespace = "dev.mslalith.focuslauncher.feature.quoteforyou"
8 | }
9 |
--------------------------------------------------------------------------------
/feature/quoteforyou/src/main/kotlin/dev/mslalith/focuslauncher/feature/quoteforyou/bottomsheet/quotewidgetsettings/QuoteWidgetSettingsBottomSheetContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.quoteforyou.bottomsheet.quotewidgetsettings
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 | import dev.mslalith.focuslauncher.core.common.model.State
6 | import dev.mslalith.focuslauncher.core.model.Quote
7 |
8 | data class QuoteWidgetSettingsBottomSheetState(
9 | val showQuotes: Boolean,
10 | val isFetchingQuotes: Boolean,
11 | val currentQuote: State,
12 | val eventSink: (QuoteWidgetSettingsBottomSheetUiEvent) -> Unit
13 | ) : CircuitUiState
14 |
15 | sealed interface QuoteWidgetSettingsBottomSheetUiEvent : CircuitUiEvent {
16 | data object ToggleShowQuoteWidget : QuoteWidgetSettingsBottomSheetUiEvent
17 | data object FetchQuoteWidget : QuoteWidgetSettingsBottomSheetUiEvent
18 | data object FetchNextQuote : QuoteWidgetSettingsBottomSheetUiEvent
19 | }
20 |
--------------------------------------------------------------------------------
/feature/quoteforyou/src/main/kotlin/dev/mslalith/focuslauncher/feature/quoteforyou/widget/QuoteForYouUiComponentContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.quoteforyou.widget
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 | import dev.mslalith.focuslauncher.core.common.model.State
6 | import dev.mslalith.focuslauncher.core.model.Quote
7 |
8 | data class QuoteForYouUiComponentState(
9 | val showQuotes: Boolean,
10 | val currentQuote: State,
11 | val eventSink: (QuoteForYouUiComponentUiEvent) -> Unit
12 | ) : CircuitUiState
13 |
14 | sealed interface QuoteForYouUiComponentUiEvent : CircuitUiEvent {
15 | data object FetchNextQuote : QuoteForYouUiComponentUiEvent
16 | }
17 |
--------------------------------------------------------------------------------
/feature/settingspage/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/feature/settingspage/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.feature")
3 | id("focuslauncher.screen.new")
4 | id("focuslauncher.android.library.compose.testing")
5 | }
6 |
7 | android {
8 | namespace = "dev.mslalith.focuslauncher.feature.settingspage"
9 | }
10 |
11 | dependencies {
12 | implementation(projects.feature.theme)
13 | implementation(projects.feature.clock24)
14 | implementation(projects.feature.lunarcalendar)
15 | implementation(projects.feature.quoteforyou)
16 |
17 | implementation(libs.kotlinx.collections.immutable)
18 |
19 | testImplementation(projects.core.settings.sentry)
20 | }
21 |
--------------------------------------------------------------------------------
/feature/settingspage/src/main/kotlin/dev/mslalith/focuslauncher/feature/settingspage/SettingsPageContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.settingspage
2 |
3 | import android.content.Context
4 | import com.slack.circuit.runtime.CircuitUiEvent
5 | import com.slack.circuit.runtime.CircuitUiState
6 | import com.slack.circuit.runtime.screen.Screen
7 |
8 | data class SettingsPageState(
9 | val showStatusBar: Boolean,
10 | val canDrawNotificationShade: Boolean,
11 | val showIconPack: Boolean,
12 | val isDefaultLauncher: Boolean,
13 | val showDeveloperOption: Boolean,
14 | val eventSink: (SettingsPageUiEvent) -> Unit
15 | ) : CircuitUiState
16 |
17 | sealed interface SettingsPageUiEvent : CircuitUiEvent {
18 | data object ToggleStatusBarVisibility : SettingsPageUiEvent
19 | data object ToggleNotificationShade : SettingsPageUiEvent
20 | data class RefreshIsDefaultLauncher(val context: Context) : SettingsPageUiEvent
21 | data class GoTo(val screen: Screen) : SettingsPageUiEvent
22 | data class OnBottomSheetOpened(val screen: Screen) : SettingsPageUiEvent
23 | }
24 |
--------------------------------------------------------------------------------
/feature/settingspage/src/main/kotlin/dev/mslalith/focuslauncher/feature/settingspage/bottomsheet/privacy/PrivacySettingsBottomSheetContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.settingspage.bottomsheet.privacy
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 |
6 | data class PrivacySettingsBottomSheetState(
7 | val reportCrashes: Boolean,
8 | val eventSink: (PrivacySettingsBottomSheetUiEvent) -> Unit
9 | ) : CircuitUiState
10 |
11 | sealed interface PrivacySettingsBottomSheetUiEvent : CircuitUiEvent {
12 | data object ToggleReportCrashes : PrivacySettingsBottomSheetUiEvent
13 | }
14 |
--------------------------------------------------------------------------------
/feature/settingspage/src/main/kotlin/dev/mslalith/focuslauncher/feature/settingspage/settingsitems/About.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.settingspage.settingsitems
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.Modifier
5 | import androidx.compose.ui.res.stringResource
6 | import dev.mslalith.focuslauncher.core.testing.compose.modifier.testsemantics.testSemantics
7 | import dev.mslalith.focuslauncher.feature.settingspage.R
8 | import dev.mslalith.focuslauncher.feature.settingspage.shared.SettingsItem
9 | import dev.mslalith.focuslauncher.feature.settingspage.utils.TestTags
10 |
11 | @Composable
12 | internal fun About(
13 | onClick: () -> Unit
14 | ) {
15 | SettingsItem(
16 | modifier = Modifier.testSemantics(tag = TestTags.ITEM_ABOUT),
17 | text = stringResource(id = R.string.about),
18 | onClick = onClick
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/feature/settingspage/src/main/kotlin/dev/mslalith/focuslauncher/feature/settingspage/settingsitems/AppDrawer.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.settingspage.settingsitems
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.Modifier
5 | import androidx.compose.ui.res.stringResource
6 | import dev.mslalith.focuslauncher.core.testing.compose.modifier.testsemantics.testSemantics
7 | import dev.mslalith.focuslauncher.feature.settingspage.R
8 | import dev.mslalith.focuslauncher.feature.settingspage.shared.SettingsItem
9 | import dev.mslalith.focuslauncher.feature.settingspage.utils.TestTags
10 |
11 | @Composable
12 | internal fun AppDrawer(
13 | onClick: () -> Unit
14 | ) {
15 | SettingsItem(
16 | modifier = Modifier.testSemantics(tag = TestTags.ITEM_APP_DRAWER),
17 | text = stringResource(id = R.string.app_drawer),
18 | onClick = onClick
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/feature/settingspage/src/main/kotlin/dev/mslalith/focuslauncher/feature/settingspage/settingsitems/ChangeTheme.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.settingspage.settingsitems
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.Modifier
5 | import androidx.compose.ui.res.stringResource
6 | import dev.mslalith.focuslauncher.core.testing.compose.modifier.testsemantics.testSemantics
7 | import dev.mslalith.focuslauncher.feature.settingspage.R
8 | import dev.mslalith.focuslauncher.feature.settingspage.shared.SettingsItem
9 | import dev.mslalith.focuslauncher.feature.settingspage.utils.TestTags
10 |
11 | @Composable
12 | internal fun ChangeTheme(
13 | onClick: () -> Unit
14 | ) {
15 | SettingsItem(
16 | modifier = Modifier.testSemantics(tag = TestTags.ITEM_CHANGE_THEME),
17 | text = stringResource(id = R.string.change_theme),
18 | onClick = onClick
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/feature/settingspage/src/main/kotlin/dev/mslalith/focuslauncher/feature/settingspage/settingsitems/Developer.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.settingspage.settingsitems
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.Modifier
5 | import androidx.compose.ui.res.stringResource
6 | import dev.mslalith.focuslauncher.core.testing.compose.modifier.testsemantics.testSemantics
7 | import dev.mslalith.focuslauncher.feature.settingspage.R
8 | import dev.mslalith.focuslauncher.feature.settingspage.shared.SettingsItem
9 | import dev.mslalith.focuslauncher.feature.settingspage.utils.TestTags
10 |
11 | @Composable
12 | internal fun Developer(
13 | onClick: () -> Unit
14 | ) {
15 | SettingsItem(
16 | modifier = Modifier.testSemantics(tag = TestTags.ITEM_DEVELOPER),
17 | text = stringResource(id = R.string.developer),
18 | onClick = onClick
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/feature/settingspage/src/main/kotlin/dev/mslalith/focuslauncher/feature/settingspage/settingsitems/EditFavorites.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.settingspage.settingsitems
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.Modifier
5 | import androidx.compose.ui.res.stringResource
6 | import dev.mslalith.focuslauncher.core.testing.compose.modifier.testsemantics.testSemantics
7 | import dev.mslalith.focuslauncher.feature.settingspage.R
8 | import dev.mslalith.focuslauncher.feature.settingspage.shared.SettingsItem
9 | import dev.mslalith.focuslauncher.feature.settingspage.utils.TestTags
10 |
11 | @Composable
12 | internal fun EditFavorites(
13 | onClick: () -> Unit
14 | ) {
15 | SettingsItem(
16 | modifier = Modifier.testSemantics(tag = TestTags.ITEM_EDIT_FAVORITES),
17 | text = stringResource(id = R.string.edit_favorites),
18 | onClick = onClick
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/feature/settingspage/src/main/kotlin/dev/mslalith/focuslauncher/feature/settingspage/settingsitems/HideApps.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.settingspage.settingsitems
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.Modifier
5 | import androidx.compose.ui.res.stringResource
6 | import dev.mslalith.focuslauncher.core.testing.compose.modifier.testsemantics.testSemantics
7 | import dev.mslalith.focuslauncher.feature.settingspage.R
8 | import dev.mslalith.focuslauncher.feature.settingspage.shared.SettingsItem
9 | import dev.mslalith.focuslauncher.feature.settingspage.utils.TestTags
10 |
11 | @Composable
12 | internal fun HideApps(
13 | onClick: () -> Unit
14 | ) {
15 | SettingsItem(
16 | modifier = Modifier.testSemantics(tag = TestTags.ITEM_HIDE_APPS),
17 | text = stringResource(id = R.string.hide_apps),
18 | onClick = onClick
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/feature/settingspage/src/main/kotlin/dev/mslalith/focuslauncher/feature/settingspage/settingsitems/Privacy.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.settingspage.settingsitems
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.Modifier
5 | import androidx.compose.ui.res.stringResource
6 | import dev.mslalith.focuslauncher.core.testing.compose.modifier.testsemantics.testSemantics
7 | import dev.mslalith.focuslauncher.feature.settingspage.R
8 | import dev.mslalith.focuslauncher.feature.settingspage.shared.SettingsItem
9 | import dev.mslalith.focuslauncher.feature.settingspage.utils.TestTags
10 |
11 | @Composable
12 | internal fun Privacy(
13 | onClick: () -> Unit
14 | ) {
15 | SettingsItem(
16 | modifier = Modifier.testSemantics(tag = TestTags.ITEM_PRIVACY),
17 | text = stringResource(id = R.string.privacy),
18 | onClick = onClick
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/feature/theme/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/feature/theme/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.feature")
3 | id("focuslauncher.screen.new")
4 | }
5 |
6 | android {
7 | namespace = "dev.mslalith.focuslauncher.feature.theme"
8 | }
9 |
10 | dependencies {
11 | implementation(libs.kotlinx.collections.immutable)
12 | }
13 |
--------------------------------------------------------------------------------
/feature/theme/src/main/kotlin/dev/mslalith/focuslauncher/feature/theme/LauncherThemeContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.theme
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 | import dev.mslalith.focuslauncher.core.model.Theme
6 |
7 | data class LauncherThemeState(
8 | val theme: Theme
9 | ) : CircuitUiState
10 |
11 | sealed interface LauncherThemeUiEvent : CircuitUiEvent
12 |
--------------------------------------------------------------------------------
/feature/theme/src/main/kotlin/dev/mslalith/focuslauncher/feature/theme/LauncherThemePresenter.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.theme
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.getValue
5 | import androidx.compose.runtime.remember
6 | import com.slack.circuit.retained.collectAsRetainedState
7 | import com.slack.circuit.runtime.presenter.Presenter
8 | import dev.mslalith.focuslauncher.core.domain.theme.GetThemeUseCase
9 | import dev.mslalith.focuslauncher.core.model.Constants.Defaults.Settings.General.DEFAULT_THEME
10 | import javax.inject.Inject
11 |
12 | class LauncherThemePresenter @Inject constructor(
13 | private val getThemeUseCase: GetThemeUseCase
14 | ) : Presenter {
15 |
16 | @Composable
17 | override fun present(): LauncherThemeState {
18 | val currentTheme by remember { getThemeUseCase() }.collectAsRetainedState(initial = DEFAULT_THEME)
19 |
20 | return LauncherThemeState(
21 | theme = currentTheme
22 | )
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/feature/theme/src/main/kotlin/dev/mslalith/focuslauncher/feature/theme/bottomsheet/themeselection/ThemeSelectionBottomSheetContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.theme.bottomsheet.themeselection
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 | import dev.mslalith.focuslauncher.core.model.Theme
6 | import dev.mslalith.focuslauncher.feature.theme.model.ThemeWithIcon
7 | import kotlinx.collections.immutable.ImmutableList
8 |
9 | data class ThemeSelectionBottomSheetState(
10 | val currentTheme: Theme,
11 | val allThemes: ImmutableList,
12 | val eventSink: (ThemeSelectionBottomSheetUiEvent) -> Unit
13 | ) : CircuitUiState
14 |
15 | sealed interface ThemeSelectionBottomSheetUiEvent : CircuitUiEvent {
16 | data class SelectedTheme(val theme: Theme?) : ThemeSelectionBottomSheetUiEvent
17 | }
18 |
--------------------------------------------------------------------------------
/feature/theme/src/main/kotlin/dev/mslalith/focuslauncher/feature/theme/model/ThemeWithIcon.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.feature.theme.model
2 |
3 | import androidx.annotation.DrawableRes
4 | import androidx.compose.runtime.Immutable
5 | import dev.mslalith.focuslauncher.core.model.Theme
6 |
7 | @Immutable
8 | data class ThemeWithIcon(
9 | val theme: Theme,
10 | @DrawableRes val iconRes: Int
11 | )
12 |
--------------------------------------------------------------------------------
/feature/theme/src/main/res/font/nunitosans_regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/feature/theme/src/main/res/font/nunitosans_regular.ttf
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/images/animated_clock.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/images/animated_clock.mp4
--------------------------------------------------------------------------------
/images/change_theme.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/images/change_theme.mp4
--------------------------------------------------------------------------------
/images/edit_favorites.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/images/edit_favorites.mp4
--------------------------------------------------------------------------------
/images/hide_apps.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/images/hide_apps.mp4
--------------------------------------------------------------------------------
/images/icon_pack.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/images/icon_pack.mp4
--------------------------------------------------------------------------------
/images/lunar_phase_info.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/images/lunar_phase_info.mp4
--------------------------------------------------------------------------------
/images/what_is.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mslalith/focus_launcher/e12448d99d8d8f2980eca8af960aee0745c2f468/images/what_is.png
--------------------------------------------------------------------------------
/screens/about/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/screens/about/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.feature")
3 | id("focuslauncher.screen.new")
4 | id("focuslauncher.android.library.compose.testing")
5 | }
6 |
7 | android {
8 | namespace = "dev.mslalith.focuslauncher.screens.about"
9 | }
10 |
--------------------------------------------------------------------------------
/screens/about/src/main/kotlin/dev/mslalith/focuslauncher/screens/about/AboutContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.screens.about
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 |
6 | data class AboutState(
7 | val eventSink: (AboutUiEvent) -> Unit
8 | ) : CircuitUiState
9 |
10 | sealed interface AboutUiEvent : CircuitUiEvent {
11 | data object GoBack : AboutUiEvent
12 | }
13 |
--------------------------------------------------------------------------------
/screens/about/src/main/kotlin/dev/mslalith/focuslauncher/screens/about/AboutPresenter.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.screens.about
2 |
3 | import androidx.compose.runtime.Composable
4 | import com.slack.circuit.codegen.annotations.CircuitInject
5 | import com.slack.circuit.runtime.Navigator
6 | import com.slack.circuit.runtime.presenter.Presenter
7 | import dagger.assisted.Assisted
8 | import dagger.assisted.AssistedInject
9 | import dagger.hilt.components.SingletonComponent
10 | import dev.mslalith.focuslauncher.core.screens.AboutScreen
11 |
12 | @CircuitInject(AboutScreen::class, SingletonComponent::class)
13 | class AboutPresenter @AssistedInject constructor(
14 | @Assisted private val navigator: Navigator
15 | ) : Presenter {
16 |
17 | @Composable
18 | override fun present(): AboutState {
19 | return AboutState {
20 | when (it) {
21 | AboutUiEvent.GoBack -> navigator.pop()
22 | }
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/screens/about/src/test/kotlin/dev/mslalith/focuslauncher/screens/about/AboutPresenterTest.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.screens.about
2 |
3 | import dev.mslalith.focuslauncher.core.testing.AppRobolectricTestRunner
4 | import dev.mslalith.focuslauncher.core.testing.circuit.PresenterTest
5 | import org.junit.FixMethodOrder
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 | import org.junit.runners.MethodSorters
9 |
10 | @RunWith(AppRobolectricTestRunner::class)
11 | @FixMethodOrder(value = MethodSorters.NAME_ASCENDING)
12 | class AboutPresenterTest : PresenterTest() {
13 |
14 | override fun presenterUnderTest() = AboutPresenter(
15 | navigator = navigator
16 | )
17 |
18 | @Test
19 | fun `01 - verify pop on back press`() = runPresenterTest {
20 | val state = awaitItem()
21 | state.eventSink(AboutUiEvent.GoBack)
22 | navigator.awaitPop()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/screens/currentplace/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/screens/currentplace/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.feature")
3 | id("focuslauncher.screen.new")
4 | id("focuslauncher.android.library.compose.testing")
5 | }
6 |
7 | android {
8 | namespace = "dev.mslalith.focuslauncher.screens.currentplace"
9 | }
10 |
11 | dependencies {
12 | implementation(libs.osmdroid)
13 | }
14 |
--------------------------------------------------------------------------------
/screens/currentplace/src/main/kotlin/dev/mslalith/focuslauncher/screens/currentplace/CurrentPlaceContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.screens.currentplace
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 | import dev.mslalith.focuslauncher.core.common.model.LoadingState
6 | import dev.mslalith.focuslauncher.core.model.location.LatLng
7 |
8 | data class CurrentPlaceState(
9 | val latLng: LatLng,
10 | val initialLatLng: LatLng,
11 | val addressState: LoadingState,
12 | val isOnline: Boolean,
13 | val canSave: Boolean,
14 | val eventSink: (CurrentPlaceUiEvent) -> Unit
15 | ) : CircuitUiState
16 |
17 | sealed interface CurrentPlaceUiEvent : CircuitUiEvent {
18 | data object GoBack : CurrentPlaceUiEvent
19 | data object SavePlace : CurrentPlaceUiEvent
20 | data class UpdateCurrentLatLng(val latLng: LatLng) : CurrentPlaceUiEvent
21 | }
22 |
--------------------------------------------------------------------------------
/screens/developer/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.feature")
3 | id("focuslauncher.screen.new")
4 | id("focuslauncher.android.library.compose.testing")
5 | alias(libs.plugins.kotlin.serialization)
6 | }
7 |
8 | android {
9 | namespace = "dev.mslalith.focuslauncher.screens.developer"
10 | }
11 |
12 | dependencies {
13 | implementation(libs.kotlinx.serialization)
14 | }
15 |
--------------------------------------------------------------------------------
/screens/developer/src/main/kotlin/dev/mslalith/focuslauncher/screens/developer/DeveloperContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.screens.developer
2 |
3 | import android.net.Uri
4 | import com.slack.circuit.runtime.CircuitUiEvent
5 | import com.slack.circuit.runtime.CircuitUiState
6 |
7 | data class DeveloperState(
8 | val defaultFavoritesName: String,
9 | val isFavoritesReading: Boolean,
10 | val isFavoritesSaving: Boolean,
11 | val eventSink: (DeveloperUiEvent) -> Unit
12 | ) : CircuitUiState
13 |
14 | sealed interface DeveloperUiEvent : CircuitUiEvent {
15 | data object GoBack : DeveloperUiEvent
16 |
17 | data class ReadFavoritesFromUri(val uri: Uri) : DeveloperUiEvent
18 | data class SaveFavoritesFromUri(val uri: Uri) : DeveloperUiEvent
19 | }
20 |
--------------------------------------------------------------------------------
/screens/developer/src/main/kotlin/dev/mslalith/focuslauncher/screens/developer/file/FavoritesCacheFileInteractor.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.screens.developer.file
2 |
3 | import android.content.Context
4 | import dagger.hilt.android.qualifiers.ApplicationContext
5 | import dev.mslalith.focuslauncher.core.model.app.App
6 | import kotlinx.serialization.builtins.ListSerializer
7 | import kotlinx.serialization.json.Json
8 | import javax.inject.Inject
9 |
10 | class FavoritesCacheFileInteractor @Inject constructor(
11 | @ApplicationContext private val context: Context
12 | ) : CacheFileInteractor>(
13 | context = context
14 | ) {
15 |
16 | override val fileName: String = "saved_favorites.json"
17 |
18 | override fun default(): List = emptyList()
19 |
20 | override fun List.toJson(): String = Json.encodeToString(
21 | serializer = ListSerializer(elementSerializer = App.serializer()),
22 | value = this
23 | )
24 |
25 | override fun fromJson(json: String): List = Json.decodeFromString(string = json)
26 | }
27 |
--------------------------------------------------------------------------------
/screens/editfavorites/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/screens/editfavorites/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.feature")
3 | id("focuslauncher.screen.new")
4 | id("focuslauncher.android.library.compose.testing")
5 | }
6 |
7 | android {
8 | namespace = "dev.mslalith.focuslauncher.screens.editfavorites"
9 | }
10 |
11 | dependencies {
12 | implementation(projects.core.ui)
13 |
14 | implementation(libs.kotlinx.collections.immutable)
15 | }
16 |
--------------------------------------------------------------------------------
/screens/editfavorites/src/main/kotlin/dev/mslalith/focuslauncher/screens/editfavorites/EditFavoritesContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.screens.editfavorites
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 | import dev.mslalith.focuslauncher.core.model.app.App
6 | import dev.mslalith.focuslauncher.core.model.app.SelectedApp
7 | import kotlinx.collections.immutable.ImmutableList
8 |
9 | data class EditFavoritesState(
10 | val favoriteApps: ImmutableList,
11 | val showHiddenApps: Boolean,
12 | val eventSink: (EditFavoritesUiEvent) -> Unit
13 | ) : CircuitUiState
14 |
15 | sealed interface EditFavoritesUiEvent : CircuitUiEvent {
16 | data class AddToFavorites(val app: App) : EditFavoritesUiEvent
17 | data class RemoveFromFavorites(val app: App) : EditFavoritesUiEvent
18 | data object ClearFavorites : EditFavoritesUiEvent
19 |
20 | data object ToggleShowingHiddenApps : EditFavoritesUiEvent
21 | data object GoBack : EditFavoritesUiEvent
22 | }
23 |
--------------------------------------------------------------------------------
/screens/editfavorites/src/main/kotlin/dev/mslalith/focuslauncher/screens/editfavorites/ui/FavoriteListItem.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.screens.editfavorites.ui
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.Modifier
5 | import dev.mslalith.focuslauncher.core.model.app.SelectedApp
6 | import dev.mslalith.focuslauncher.core.ui.SelectableCheckboxItem
7 |
8 | @Composable
9 | internal fun FavoriteListItem(
10 | modifier: Modifier = Modifier,
11 | selectedApp: SelectedApp,
12 | onAppClick: () -> Unit
13 | ) {
14 | SelectableCheckboxItem(
15 | modifier = modifier,
16 | text = selectedApp.app.displayName,
17 | checked = selectedApp.isSelected,
18 | disabled = selectedApp.disabled,
19 | onClick = onAppClick
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/screens/editfavorites/src/main/kotlin/dev/mslalith/focuslauncher/screens/editfavorites/utils/TestTags.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.screens.editfavorites.utils
2 |
3 | import dev.mslalith.focuslauncher.core.lint.kover.IgnoreInKoverReport
4 |
5 | @IgnoreInKoverReport
6 | internal object TestTags {
7 | const val TAG_FAVORITES_LIST_ITEM = "tag_favorites_list_item"
8 | const val TAG_FAVORITES_LIST = "tag_favorites_list"
9 | const val TAG_CLEAR_FAVORITES_FAB = "tag_clear_favorites_fab"
10 | const val TAG_TOGGLE_HIDDEN_APPS = "tag_toggle_hidden_apps"
11 | }
12 |
--------------------------------------------------------------------------------
/screens/hideapps/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/screens/hideapps/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.feature")
3 | id("focuslauncher.screen.new")
4 | id("focuslauncher.android.library.compose.testing")
5 | }
6 |
7 | android {
8 | namespace = "dev.mslalith.focuslauncher.screens.hideapps"
9 | }
10 |
11 | dependencies {
12 | implementation(libs.kotlinx.collections.immutable)
13 | }
14 |
--------------------------------------------------------------------------------
/screens/hideapps/src/main/kotlin/dev/mslalith/focuslauncher/screens/hideapps/HideAppsContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.screens.hideapps
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 | import dev.mslalith.focuslauncher.core.model.app.App
6 | import dev.mslalith.focuslauncher.core.model.app.SelectedHiddenApp
7 | import kotlinx.collections.immutable.ImmutableList
8 |
9 | data class HideAppsState(
10 | val hiddenApps: ImmutableList,
11 | val eventSink: (HideAppsUiEvent) -> Unit
12 | ) : CircuitUiState
13 |
14 | sealed interface HideAppsUiEvent : CircuitUiEvent {
15 | data class AddToHiddenApps(val app: App) : HideAppsUiEvent
16 | data class RemoveFromHiddenApps(val app: App) : HideAppsUiEvent
17 | data object ClearHiddenApps : HideAppsUiEvent
18 |
19 | data class RemoveFromFavorites(val app: App) : HideAppsUiEvent
20 | data object GoBack : HideAppsUiEvent
21 | }
22 |
--------------------------------------------------------------------------------
/screens/hideapps/src/main/kotlin/dev/mslalith/focuslauncher/screens/hideapps/utils/TestTags.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.screens.hideapps.utils
2 |
3 | import dev.mslalith.focuslauncher.core.lint.kover.IgnoreInKoverReport
4 |
5 | @IgnoreInKoverReport
6 | internal object TestTags {
7 | const val TAG_HIDDEN_APPS_LIST_ITEM = "tag_hidden_apps_list_item"
8 | const val TAG_HIDDEN_APPS_LIST = "tag_hidden_apps_list"
9 | const val TAG_CLEAR_HIDDEN_APPS_FAB = "tag_clear_hidden_apps_fab"
10 | }
11 |
--------------------------------------------------------------------------------
/screens/iconpack/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/screens/iconpack/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.feature")
3 | id("focuslauncher.screen.new")
4 | id("focuslauncher.android.library.compose.testing")
5 | }
6 |
7 | android {
8 | namespace = "dev.mslalith.focuslauncher.screens.iconpack"
9 | }
10 |
11 | dependencies {
12 | implementation(projects.feature.appdrawerpage)
13 |
14 | implementation(libs.kotlinx.collections.immutable)
15 | }
16 |
--------------------------------------------------------------------------------
/screens/iconpack/src/main/kotlin/dev/mslalith/focuslauncher/screens/iconpack/IconPackContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.screens.iconpack
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 | import dev.mslalith.focuslauncher.core.common.model.LoadingState
6 | import dev.mslalith.focuslauncher.core.model.IconPackType
7 | import dev.mslalith.focuslauncher.core.model.app.AppWithIcon
8 | import dev.mslalith.focuslauncher.core.model.appdrawer.AppDrawerItem
9 | import kotlinx.collections.immutable.ImmutableList
10 |
11 | data class IconPackState(
12 | val allApps: LoadingState>,
13 | val iconPacks: ImmutableList,
14 | val iconPackType: IconPackType?,
15 | val canSave: Boolean,
16 | val eventSink: (IconPackUiEvent) -> Unit
17 | ) : CircuitUiState
18 |
19 | sealed interface IconPackUiEvent : CircuitUiEvent {
20 | data object GoBack : IconPackUiEvent
21 | data object SaveIconPack : IconPackUiEvent
22 | data class UpdateSelectedIconPackApp(val iconPackType: IconPackType) : IconPackUiEvent
23 | }
24 |
--------------------------------------------------------------------------------
/screens/launcher/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/screens/launcher/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("focuslauncher.android.feature")
3 | id("focuslauncher.screen.new")
4 | id("focuslauncher.android.library.compose.testing")
5 | }
6 |
7 | android {
8 | namespace = "dev.mslalith.focuslauncher.screens.launcher"
9 | }
10 |
11 | dependencies {
12 | implementation(projects.feature.homepage)
13 | implementation(projects.feature.settingspage)
14 | implementation(projects.feature.appdrawerpage)
15 | }
16 |
--------------------------------------------------------------------------------
/screens/launcher/src/main/kotlin/dev/mslalith/focuslauncher/screens/launcher/LauncherContract.kt:
--------------------------------------------------------------------------------
1 | package dev.mslalith.focuslauncher.screens.launcher
2 |
3 | import com.slack.circuit.runtime.CircuitUiEvent
4 | import com.slack.circuit.runtime.CircuitUiState
5 | import dev.mslalith.focuslauncher.feature.appdrawerpage.AppDrawerPageState
6 | import dev.mslalith.focuslauncher.feature.homepage.HomePageState
7 | import dev.mslalith.focuslauncher.feature.settingspage.SettingsPageState
8 |
9 | data class LauncherState(
10 | val settingsPageState: SettingsPageState,
11 | val homePageState: HomePageState,
12 | val appDrawerPageState: AppDrawerPageState
13 | ) : CircuitUiState
14 |
15 | sealed interface LauncherUiEvent : CircuitUiEvent
16 |
--------------------------------------------------------------------------------
/scripts/pre-push:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "Running Git pre-push hook"
4 |
5 | echo "Running static analysis"
6 | ./gradlew detekt --daemon
7 |
8 | STATUS=$?
9 |
10 | [ $STATUS -ne 0 ] && exit 1
11 | exit 0
12 |
--------------------------------------------------------------------------------