├── .github ├── FUNDING.yml └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── .idea └── icon.svg ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── build.gradle.kts ├── compose-destinations-bottom-sheet ├── .gitignore ├── build.gradle.kts ├── consumer-rules.pro ├── gradle.properties ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── ramcosta │ └── composedestinations │ └── bottomsheet │ ├── manualcomposablecalls │ └── ManualComposableCallsBuilder.kt │ ├── scope │ └── DestinationScopeInternals.kt │ ├── spec │ └── DestinationStyleBottomSheet.kt │ └── utils │ └── NavGraphBuilderDestinationExtensions.kt ├── compose-destinations-codegen ├── .gitignore ├── build.gradle.kts ├── gradle.properties └── src │ └── main │ └── java │ └── com │ └── ramcosta │ └── composedestinations │ └── codegen │ ├── CodeGenerator.kt │ ├── commons │ ├── Constants.kt │ ├── Exceptions.kt │ ├── GeneratedExceptions.kt │ ├── ModuleOutputUtils.kt │ ├── RawNavGraphTreeBuilder.kt │ ├── TypeUtils.kt │ └── Utils.kt │ ├── facades │ ├── CodeOutputStreamMaker.kt │ └── Logger.kt │ ├── model │ ├── ActivityDestinationParams.kt │ ├── ClassKind.kt │ ├── CodeGenConfig.kt │ ├── CodeGenProcessedDestination.kt │ ├── CodeGenType.kt │ ├── CustomNavType.kt │ ├── DeepLink.kt │ ├── DefaultValue.kt │ ├── DestinationGeneratingParams.kt │ ├── DestinationNavArgsType.kt │ ├── DestinationStyleType.kt │ ├── ExternalRoute.kt │ ├── Importable.kt │ ├── NavGraphGenParams.kt │ ├── NavGraphInfo.kt │ ├── NavTypeSerializer.kt │ ├── Parameter.kt │ ├── SubModuleInfo.kt │ ├── TypeArgument.kt │ └── Visibility.kt │ ├── servicelocator │ └── ServiceLocator.kt │ ├── templates │ ├── ArgsToSavedStateHandleTemplate.kt │ ├── DestinationTemplate.kt │ ├── ModuleDestinationsTemplate.kt │ ├── ModuleNavGraphTemplate.kt │ ├── NavArgsGetterTemplate.kt │ ├── NavGraphsObjectTemplate.kt │ ├── core │ │ └── FileTemplate.kt │ └── navtype │ │ ├── CustomTypeSerializerNavTypeTemplate.kt │ │ ├── DefaultKtxSerializableNavTypeSerializerTemplate.kt │ │ ├── KtxSerializableNavTypeTemplate.kt │ │ ├── NavTypeTemplateCommons.kt │ │ ├── ParcelableNavTypeTemplate.kt │ │ ├── SerializableNavTypeTemplate.kt │ │ └── arrays │ │ ├── ArrayCommons.kt │ │ ├── CustomTypeArrayListNavTypeTemplate.kt │ │ ├── CustomTypeArrayNavTypeTemplate.kt │ │ ├── KtxSerializableArrayListNavTypeTemplate.kt │ │ ├── KtxSerializableArrayNavTypeTemplate.kt │ │ ├── ParcelableArrayListNavTypeTemplate.kt │ │ ├── ParcelableArrayNavTypeTemplate.kt │ │ ├── SerializableArrayListNavTypeTemplate.kt │ │ └── SerializableArrayNavTypeTemplate.kt │ ├── validators │ └── InitialValidator.kt │ └── writers │ ├── ArgsToSavedStateHandleUtilsWriter.kt │ ├── CustomNavTypesWriter.kt │ ├── DefaultKtxSerializableNavTypeSerializerWriter.kt │ ├── DestinationsWriter.kt │ ├── MermaidGraphWriter.kt │ ├── ModuleOutputWriter.kt │ ├── ModuleRegistryWriter.kt │ ├── NavArgsGetterUtilsWriter.kt │ ├── SingleDestinationWriter.kt │ ├── helpers │ ├── FileWriter.kt │ ├── ImportableHelper.kt │ └── NavArgResolver.kt │ └── sub │ ├── DestinationContentFunctionWriter.kt │ ├── DestinationsModeWriter.kt │ ├── NavArgumentBridgeCodeBuilder.kt │ ├── NavGraphsPrettyKdocWriter.kt │ ├── NavGraphsSingleObjectWriter.kt │ └── SingleNavGraphWriter.kt ├── compose-destinations-ksp ├── .gitignore ├── build.gradle.kts ├── gradle.properties └── src │ ├── main │ ├── kotlin │ │ └── com │ │ │ └── ramcosta │ │ │ └── composedestinations │ │ │ └── ksp │ │ │ ├── ProcessorProvider.kt │ │ │ ├── codegen │ │ │ ├── KspCodeOutputStreamMaker.kt │ │ │ └── KspLogger.kt │ │ │ ├── commons │ │ │ ├── DefaultParameterValueReader.kt │ │ │ ├── DestinationMappingUtils.kt │ │ │ ├── KSFileSourceMapper.kt │ │ │ ├── MutableKSFileSourceMapper.kt │ │ │ └── Utils.kt │ │ │ └── processors │ │ │ ├── ConfigParser.kt │ │ │ ├── DestinationAnnotationsPath.kt │ │ │ ├── KspToCodeGenDestinationsMapper.kt │ │ │ ├── KspToCodeGenNavGraphsMapper.kt │ │ │ └── Processor.kt │ └── resources │ │ └── META-INF │ │ └── services │ │ └── com.google.devtools.ksp.processing.SymbolProcessorProvider │ └── test │ └── kotlin │ └── com │ └── ramcosta │ └── composedestinations │ └── ksp │ └── commons │ └── DefaultParameterValueReaderTest.kt ├── compose-destinations-wear ├── .gitignore ├── build.gradle.kts ├── consumer-rules.pro ├── gradle.properties ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── ramcosta │ └── composedestinations │ └── wear │ └── WearNavHostEngine.kt ├── compose-destinations ├── .gitignore ├── build.gradle.kts ├── consumer-rules.pro ├── gradle.properties ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── ramcosta │ └── composedestinations │ ├── DefaultNavHostEngine.kt │ ├── DestinationsNavHost.kt │ ├── animations │ ├── NavHostAnimatedDestinationStyle.kt │ └── defaults │ │ └── DefaultAnimationParams.kt │ ├── annotation │ ├── ActivityDestination.kt │ ├── Destination.kt │ ├── ExternalDestination.kt │ ├── ExternalModuleDestinations.kt │ ├── ExternalModuleGraph.kt │ ├── ExternalNavGraph.kt │ ├── NavGraph.kt │ ├── NavHostDefaultStartArgs.kt │ ├── NavHostGraph.kt │ ├── internal │ │ ├── GeneratedCodeExternalDestinations.kt │ │ └── InternalDestinationsApi.kt │ └── parameters │ │ ├── CodeGenVisibility.kt │ │ ├── DeepLink.kt │ │ └── NavHostParam.kt │ ├── manualcomposablecalls │ ├── DestinationLambda.kt │ ├── ManualComposableCalls.kt │ └── ManualComposableCallsBuilder.kt │ ├── navargs │ ├── DestinationsNavType.kt │ ├── DestinationsNavTypeSerializer.kt │ ├── NavTypeSerializer.kt │ ├── parcelable │ │ └── DefaultParcelableNavTypeSerializer.kt │ ├── primitives │ │ ├── CommonConstants.kt │ │ ├── DestinationsBooleanNavType.kt │ │ ├── DestinationsEnumNavType.kt │ │ ├── DestinationsFloatNavType.kt │ │ ├── DestinationsIntNavType.kt │ │ ├── DestinationsLongNavType.kt │ │ ├── DestinationsStringNavType.kt │ │ ├── array │ │ │ ├── DestinationsBooleanArrayNavType.kt │ │ │ ├── DestinationsEnumArrayNavType.kt │ │ │ ├── DestinationsFloatArrayNavType.kt │ │ │ ├── DestinationsIntArrayNavType.kt │ │ │ ├── DestinationsLongArrayNavType.kt │ │ │ └── DestinationsStringArrayNavType.kt │ │ └── arraylist │ │ │ ├── DestinationsBooleanArrayListNavType.kt │ │ │ ├── DestinationsEnumArrayListNavType.kt │ │ │ ├── DestinationsFloatArrayListNavType.kt │ │ │ ├── DestinationsIntArrayListNavType.kt │ │ │ ├── DestinationsLongArrayListNavType.kt │ │ │ └── DestinationsStringArrayListNavType.kt │ ├── serializable │ │ └── DefaultSerializableNavTypeSerializer.kt │ └── utils │ │ └── NavArgEncodingUtils.kt │ ├── navigation │ ├── DestinationDependenciesContainer.kt │ ├── DestinationsNavController.kt │ ├── DestinationsNavOptionsBuilder.kt │ ├── DestinationsNavigator.kt │ └── EmptyDestinationsNavigator.kt │ ├── result │ ├── EmptyResultBackNavigator.kt │ ├── EmptyResultRecipient.kt │ ├── NavResult.kt │ ├── OpenResultRecipient.kt │ ├── ResultBackNavigator.kt │ ├── ResultBackNavigatorImpl.kt │ ├── ResultCommons.kt │ ├── ResultRecipient.kt │ └── ResultRecipientImpl.kt │ ├── scope │ ├── DestinationScope.kt │ ├── DestinationScopeInternals.kt │ ├── DestinationScopeWithNoDependencies.kt │ └── NavGraphBuilderDestinationScope.kt │ ├── spec │ ├── ActivityDestinationSpec.kt │ ├── BaseRoute.kt │ ├── DestinationSpec.kt │ ├── DestinationStyle.kt │ ├── Direction.kt │ ├── DirectionDestinationSpec.kt │ ├── DirectionNavGraphSpec.kt │ ├── ModuleDestinationsContainer.kt │ ├── NavGraphSpec.kt │ ├── NavHostEngine.kt │ ├── NavHostGraphSpec.kt │ ├── Route.kt │ └── RouteOrDirection.kt │ ├── utils │ ├── NavControllerExt.kt │ ├── NavGraphBuilderDestinationExtensions.kt │ ├── NavGraphSpecHolder.kt │ └── SpecExtensions.kt │ └── wrapper │ └── DestinationWrapper.kt ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── playground ├── app │ ├── .gitignore │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── src │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── ramcosta │ │ │ │ └── samples │ │ │ │ └── playground │ │ │ │ ├── AppNavigation.kt │ │ │ │ ├── MainActivity.kt │ │ │ │ ├── OtherActivity.kt │ │ │ │ ├── PlaygroundApp.kt │ │ │ │ ├── commons │ │ │ │ ├── DestinationsAppExtensions.kt │ │ │ │ ├── DrawerController.kt │ │ │ │ ├── NavGraphDefinition.kt │ │ │ │ └── composables │ │ │ │ │ ├── MyBottomBar.kt │ │ │ │ │ ├── MyDrawer.kt │ │ │ │ │ ├── MyTopBar.kt │ │ │ │ │ └── PlaygroundScaffold.kt │ │ │ │ ├── di │ │ │ │ ├── DependencyContainer.kt │ │ │ │ └── ViewModelFactory.kt │ │ │ │ └── ui │ │ │ │ ├── screens │ │ │ │ ├── Feed.kt │ │ │ │ ├── GoToProfileConfirmation.kt │ │ │ │ ├── TestScreen.kt │ │ │ │ ├── greeting │ │ │ │ │ ├── GreetingScreen.kt │ │ │ │ │ ├── GreetingTransitions.kt │ │ │ │ │ ├── GreetingUiEvents.kt │ │ │ │ │ ├── GreetingUiState.kt │ │ │ │ │ └── GreetingViewModel.kt │ │ │ │ ├── profile │ │ │ │ │ ├── GetProfileLikeCountUseCase.kt │ │ │ │ │ ├── ProfileScreen.kt │ │ │ │ │ ├── ProfileScreenNavArgs.kt │ │ │ │ │ ├── ProfileTransitions.kt │ │ │ │ │ ├── ProfileUiEvents.kt │ │ │ │ │ ├── ProfileUiState.kt │ │ │ │ │ └── ProfileViewModel.kt │ │ │ │ ├── settings │ │ │ │ │ ├── Settings.kt │ │ │ │ │ ├── SettingsViewModel.kt │ │ │ │ │ └── ThemeSettings.kt │ │ │ │ ├── styles │ │ │ │ │ ├── AppDialog.kt │ │ │ │ │ └── SettingsTransitions.kt │ │ │ │ └── wrappers │ │ │ │ │ ├── DrawerOpeningWrapper.kt │ │ │ │ │ └── HidingScreenWrapper.kt │ │ │ │ └── theme │ │ │ │ ├── Color.kt │ │ │ │ ├── Shape.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── values-night │ │ │ └── themes.xml │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── themes.xml │ │ └── test │ │ └── kotlin │ │ └── com │ │ └── ramcosta │ │ ├── composedestinations │ │ ├── ksp │ │ │ └── ProcessorProviderTests.kt │ │ └── navargs │ │ │ └── ktxserializable │ │ │ └── DefaultKtxSerializableNavTypeSerializerTest.kt │ │ └── samples │ │ └── playground │ │ └── ui │ │ └── screens │ │ └── profile │ │ └── ProfileScreenNavArgsTest.kt ├── core │ ├── .gitignore │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── ramcosta │ │ └── playground │ │ └── core │ │ ├── ArgsFromAnotherModule.kt │ │ ├── BlogPostArgs.kt │ │ └── TestWithDefaultValueArgs.kt ├── docs │ ├── FeatureXNavGraph.mmd │ ├── FeatureYNavGraph.mmd │ ├── MyTopLevelNavGraph.mmd │ ├── RootNavGraph.mmd │ └── SubFeatureYNavGraph.mmd ├── featurex │ ├── .gitignore │ ├── build.gradle.kts │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── ramcosta │ │ └── playground │ │ └── featurex │ │ ├── FeatureXNavGraphs.kt │ │ ├── FeatureXWrapper.kt │ │ └── screens │ │ ├── FeatureXHomeScreen.kt │ │ └── InternalArgsScreen.kt ├── featurey │ ├── .gitignore │ ├── build.gradle.kts │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ ├── src │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── com │ │ │ └── ramcosta │ │ │ └── playground │ │ │ └── featurey │ │ │ ├── FeatureYNavGraphs.kt │ │ │ ├── FeatureYWrapper.kt │ │ │ └── screens │ │ │ ├── FeatureYHomeScreen.kt │ │ │ ├── FeatureYInternalArgsScreen.kt │ │ │ └── PublicFeatureYSideScreen.kt │ └── sub │ │ ├── .gitignore │ │ ├── build.gradle.kts │ │ ├── consumer-rules.pro │ │ ├── proguard-rules.pro │ │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── ramcosta │ │ └── playground │ │ └── subfeaturey │ │ ├── SubFeatureYGraph.kt │ │ └── screens │ │ ├── SubFeatureYHome.kt │ │ ├── SubFeatureYInternalArgsScreen.kt │ │ └── SubFeatureYPublicSideScreen.kt └── featurez │ ├── .gitignore │ ├── build.gradle.kts │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── ramcosta │ └── playground │ └── featurez │ ├── FeatureZHomeScreen.kt │ └── screens │ ├── FeatureZSecondScreen.kt │ └── PublicFeatureYSideScreen.kt ├── publish.gradle ├── sample-wear ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── ramcosta │ │ └── destinations │ │ └── sample │ │ └── wear │ │ ├── MainActivity.kt │ │ ├── MainViewModel.kt │ │ ├── SampleApp.kt │ │ ├── account │ │ ├── AccountScreen.kt │ │ └── AccountViewModel.kt │ │ ├── core │ │ ├── di │ │ │ └── DependencyContainer.kt │ │ └── viewmodel │ │ │ └── ViewModelUtils.kt │ │ ├── login │ │ ├── LoginScreen.kt │ │ └── data │ │ │ └── LoginStateRepository.kt │ │ ├── settings │ │ └── SettingScreen.kt │ │ ├── tasks │ │ ├── data │ │ │ ├── StepsRepository.kt │ │ │ └── TasksRepository.kt │ │ ├── domain │ │ │ ├── Step.kt │ │ │ └── Task.kt │ │ └── presentation │ │ │ ├── details │ │ │ ├── StepDetailsViewModel.kt │ │ │ ├── StepScreen.kt │ │ │ ├── StepScreenNavArgs.kt │ │ │ ├── TaskDetailsViewModel.kt │ │ │ ├── TaskScreen.kt │ │ │ └── TaskScreenNavArgs.kt │ │ │ ├── list │ │ │ ├── TaskItem.kt │ │ │ ├── TaskListScreen.kt │ │ │ ├── TaskListViewModel.kt │ │ │ └── TaskUiItem.kt │ │ │ └── new │ │ │ ├── AddStepScreen.kt │ │ │ ├── AddStepViewModel.kt │ │ │ ├── AddTaskScreen.kt │ │ │ └── AddTaskViewModel.kt │ │ └── ui │ │ ├── composables │ │ ├── SampleScaffold.kt │ │ └── TitleConfirmDialog.kt │ │ └── theme │ │ └── Theme.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-mdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ └── values │ └── strings.xml ├── sample ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── ramcosta │ │ └── destinations │ │ └── sample │ │ ├── MainActivity.kt │ │ ├── MainViewModel.kt │ │ ├── SampleApp.kt │ │ ├── account │ │ ├── AccountScreen.kt │ │ └── AccountViewModel.kt │ │ ├── core │ │ ├── di │ │ │ └── DependencyContainer.kt │ │ └── viewmodel │ │ │ └── ViewModelUtils.kt │ │ ├── login │ │ ├── LoginScreen.kt │ │ └── data │ │ │ └── LoginStateRepository.kt │ │ ├── settings │ │ └── SettingScreen.kt │ │ ├── tasks │ │ ├── data │ │ │ ├── StepsRepository.kt │ │ │ └── TasksRepository.kt │ │ ├── domain │ │ │ ├── Step.kt │ │ │ └── Task.kt │ │ └── presentation │ │ │ ├── details │ │ │ ├── StepDetailsViewModel.kt │ │ │ ├── StepScreen.kt │ │ │ ├── StepScreenNavArgs.kt │ │ │ ├── TaskDetailsViewModel.kt │ │ │ ├── TaskScreen.kt │ │ │ └── TaskScreenNavArgs.kt │ │ │ ├── list │ │ │ ├── TaskItem.kt │ │ │ ├── TaskListScreen.kt │ │ │ ├── TaskListViewModel.kt │ │ │ └── TaskUiItem.kt │ │ │ └── new │ │ │ ├── AddStepDialog.kt │ │ │ ├── AddStepViewModel.kt │ │ │ ├── AddTaskDialog.kt │ │ │ └── AddTaskViewModel.kt │ │ └── ui │ │ ├── composables │ │ ├── BottomBar.kt │ │ ├── SampleScaffold.kt │ │ ├── TitleConfirmDialog.kt │ │ └── TopBar.kt │ │ └── theme │ │ ├── Color.kt │ │ ├── Shape.kt │ │ ├── Theme.kt │ │ └── Type.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-mdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ └── values │ ├── colors.xml │ ├── strings.xml │ └── themes.xml └── settings.gradle.kts /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: raamcosta 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: rafaelamcosta 14 | custom: 'https://paypal.me/zkeme' 15 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | run-name: Testing build. 3 | 4 | on: 5 | push: 6 | 7 | jobs: 8 | 9 | build: 10 | name: Build 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v3 15 | 16 | - name: Set up JDK 17 17 | uses: actions/setup-java@v3 18 | with: 19 | java-version: 17 20 | distribution: 'zulu' 21 | 22 | - name: Build 23 | uses: gradle/gradle-build-action@v2 24 | with: 25 | gradle-version: wrapper 26 | arguments: build 27 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | run-name: Publishing new version to maven central. 3 | on: 4 | push: 5 | tags: 6 | - '*.*.*' 7 | 8 | env: 9 | ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.ORG_GRADLE_PROJECT_mavenCentralUsername }} 10 | ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.ORG_GRADLE_PROJECT_mavenCentralPassword }} 11 | ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.ORG_GRADLE_PROJECT_signingInMemoryKey }} 12 | ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.ORG_GRADLE_PROJECT_signingInMemoryKeyId }} 13 | ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.ORG_GRADLE_PROJECT_signingInMemoryKeyPassword }} 14 | 15 | jobs: 16 | release: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v3 22 | 23 | - name: Set up JDK 17 24 | uses: actions/setup-java@v3 25 | with: 26 | java-version: 17 27 | distribution: 'zulu' 28 | 29 | - name: Cache 30 | uses: actions/cache@v3 31 | with: 32 | path: ~/.gradle/caches 33 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} 34 | restore-keys: | 35 | ${{ runner.os }}-gradle- 36 | 37 | - name: Build 38 | uses: gradle/gradle-build-action@v2 39 | with: 40 | gradle-version: wrapper 41 | arguments: build 42 | 43 | - name: Run unit tests 44 | run: ./gradlew test 45 | 46 | - name: Publish to Maven Central 47 | run: ./gradlew publishAllPublicationsToMavenCentral 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/ 5 | !/.idea/icon.svg 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | .cxx 11 | local.properties 12 | /secring.gpg 13 | /playground/docs/*NavGraph.html 14 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all subprojects/modules. 2 | import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask 3 | 4 | plugins { 5 | alias(libs.plugins.dependencyCheckPlugin) 6 | alias(libs.plugins.compose.compiler).apply(false) 7 | } 8 | 9 | buildscript { 10 | 11 | repositories { 12 | google() 13 | mavenCentral() 14 | } 15 | 16 | dependencies { 17 | classpath(libs.agp) 18 | classpath(libs.kotlin) 19 | classpath(libs.kotlinSerialization) 20 | classpath(libs.mavenPublishPlugin) 21 | } 22 | } 23 | 24 | tasks.register("clean") { 25 | delete(rootProject.buildDir) 26 | } 27 | 28 | /** 29 | * Run ./gradlew dependencyUpdates to check for new updates 30 | * in dependencies used. 31 | * More info at: https://github.com/ben-manes/gradle-versions-plugin 32 | */ 33 | tasks.withType { 34 | rejectVersionIf { 35 | // Don't allow non-stable versions, unless we are already using one for this dependency 36 | isNonStable(candidate.version) && !isNonStable(currentVersion) 37 | } 38 | } 39 | 40 | /** 41 | * Decides if this version is stable or not. 42 | */ 43 | fun isNonStable(version: String): Boolean { 44 | val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.uppercase().contains(it) } 45 | val regex = "^[0-9,.v-]+(-r)?$".toRegex() 46 | val isStable = stableKeyword || regex.matches(version) 47 | return !isStable 48 | } 49 | -------------------------------------------------------------------------------- /compose-destinations-bottom-sheet/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /compose-destinations-bottom-sheet/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.library") 3 | kotlin("android") 4 | alias(libs.plugins.compose.compiler) 5 | } 6 | 7 | apply(from = "${rootProject.projectDir}/publish.gradle") 8 | 9 | android { 10 | 11 | namespace = "com.ramcosta.composedestinations.bottomsheet" 12 | compileSdk = libs.versions.compileSdk.get().toIntOrNull() 13 | 14 | defaultConfig { 15 | minSdk = libs.versions.minSdk.get().toIntOrNull() 16 | 17 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 18 | consumerProguardFiles.add(File("consumer-rules.pro")) 19 | } 20 | 21 | buildTypes { 22 | release { 23 | isMinifyEnabled = false 24 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") 25 | } 26 | } 27 | 28 | compileOptions { 29 | sourceCompatibility = JavaVersion.VERSION_1_8 30 | targetCompatibility = JavaVersion.VERSION_1_8 31 | } 32 | 33 | kotlinOptions { 34 | jvmTarget = JavaVersion.VERSION_1_8.toString() 35 | } 36 | 37 | buildFeatures { 38 | compose = true 39 | } 40 | } 41 | 42 | kotlin { 43 | compilerOptions { 44 | freeCompilerArgs.addAll( 45 | "-opt-in=kotlin.RequiresOptIn", 46 | "-opt-in=com.ramcosta.composedestinations.annotation.internal.InternalDestinationsApi" 47 | ) 48 | } 49 | } 50 | 51 | dependencies { 52 | 53 | implementation(project(mapOf("path" to ":compose-destinations"))) 54 | 55 | api(libs.compose.material.navigation) 56 | } 57 | -------------------------------------------------------------------------------- /compose-destinations-bottom-sheet/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raamcosta/compose-destinations/34a138134b053bc588554e46bcdf360f8210f3d6/compose-destinations-bottom-sheet/consumer-rules.pro -------------------------------------------------------------------------------- /compose-destinations-bottom-sheet/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=bottom-sheet 2 | POM_NAME=bottom-sheet -------------------------------------------------------------------------------- /compose-destinations-bottom-sheet/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.kts. 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 -------------------------------------------------------------------------------- /compose-destinations-bottom-sheet/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /compose-destinations-bottom-sheet/src/main/java/com/ramcosta/composedestinations/bottomsheet/scope/DestinationScopeInternals.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.bottomsheet.scope 2 | 3 | import androidx.compose.foundation.layout.ColumnScope 4 | import androidx.compose.runtime.Composable 5 | import androidx.navigation.NavBackStackEntry 6 | import androidx.navigation.NavController 7 | import com.ramcosta.composedestinations.navigation.DependenciesContainerBuilder 8 | import com.ramcosta.composedestinations.scope.BottomSheetDestinationScope 9 | import com.ramcosta.composedestinations.scope.BottomSheetNavGraphBuilderDestinationScope 10 | import com.ramcosta.composedestinations.scope.DestinationScopeImpl 11 | import com.ramcosta.composedestinations.scope.NavGraphBuilderDestinationScopeImpl 12 | import com.ramcosta.composedestinations.spec.TypedDestinationSpec 13 | 14 | internal class BottomSheetDestinationScopeImpl( 15 | override val destination: TypedDestinationSpec, 16 | override val navBackStackEntry: NavBackStackEntry, 17 | override val navController: NavController, 18 | columnScope: ColumnScope, 19 | override val dependenciesContainerBuilder: @Composable DependenciesContainerBuilder<*>.() -> Unit, 20 | ) : DestinationScopeImpl(), 21 | BottomSheetDestinationScope, 22 | ColumnScope by columnScope 23 | 24 | internal class BottomSheetNavGraphBuilderDestinationScopeImpl( 25 | override val destination: TypedDestinationSpec, 26 | override val navBackStackEntry: NavBackStackEntry, 27 | columnScope: ColumnScope, 28 | ) : NavGraphBuilderDestinationScopeImpl(), 29 | BottomSheetNavGraphBuilderDestinationScope, 30 | ColumnScope by columnScope -------------------------------------------------------------------------------- /compose-destinations-codegen/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /compose-destinations-codegen/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java-library") 3 | id("kotlin") 4 | } 5 | 6 | apply(from = "${rootProject.projectDir}/publish.gradle") 7 | 8 | java { 9 | sourceCompatibility = JavaVersion.VERSION_1_8 10 | targetCompatibility = JavaVersion.VERSION_1_8 11 | } 12 | 13 | tasks.withType().configureEach { 14 | kotlinOptions { 15 | jvmTarget = JavaVersion.VERSION_1_8.toString() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /compose-destinations-codegen/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=codegen 2 | POM_NAME=codegen -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/commons/Exceptions.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.commons 2 | 3 | class IllegalDestinationsSetup(message: String, cause: Throwable? = null) : RuntimeException(message, cause) 4 | 5 | class MissingRequiredDependency(message: String) : RuntimeException(message) -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/commons/GeneratedExceptions.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.commons 2 | 3 | object GeneratedExceptions { 4 | fun missingMandatoryArgument(argName: String) = "throw RuntimeException(\"'${argName}' argument is mandatory, but was not present!\")" 5 | fun nonMandatoryNonNullableMissingArgument(argName: String) = "throw RuntimeException(\"'${argName}' argument is not mandatory and not nullable but was not present!\")" 6 | } 7 | -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/facades/CodeOutputStreamMaker.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.facades 2 | 3 | import java.io.OutputStream 4 | 5 | interface CodeOutputStreamMaker { 6 | 7 | fun makeFile( 8 | name: String, 9 | packageName: String, 10 | extensionName: String = "kt", 11 | vararg sourceIds: String, 12 | ): OutputStream 13 | 14 | val packageNamesWrittenTo: List 15 | } -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/facades/Logger.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.facades 2 | 3 | import java.io.File 4 | 5 | interface Logger { 6 | val debugMode: Boolean 7 | 8 | val debugModeOutputPath: String? 9 | 10 | val prettyPrinter: PPrinter 11 | 12 | fun logging(message: String) 13 | 14 | fun info(message: String) 15 | 16 | fun warn(message: String) 17 | 18 | fun error(message: String) 19 | 20 | fun exception(e: Throwable) 21 | 22 | companion object { 23 | lateinit var instance: Logger 24 | } 25 | } 26 | 27 | interface PPrinter { 28 | fun pprint(any: Any): String 29 | } 30 | 31 | inline fun Logger.debug(message: PPrinter.() -> String) = synchronized(this) { 32 | debugModeOutputPath?.let { 33 | File(it) 34 | .run { 35 | parentFile.mkdirs() 36 | appendText("***************************************\n") 37 | appendText(message(prettyPrinter)) 38 | appendText("\n***************************************\n") 39 | appendText("\n\n") 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/model/ActivityDestinationParams.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.model 2 | 3 | data class ActivityDestinationParams( 4 | val targetPackage: String?, 5 | val action: String?, 6 | val dataUri: String?, 7 | val dataPattern: String?, 8 | ) -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/model/ClassKind.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.model 2 | 3 | enum class ClassKind { 4 | CLASS, OBJECT //... 5 | } -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/model/CodeGenType.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.model 2 | 3 | interface CodeGenType { 4 | val importable: Importable 5 | val typeArguments: List 6 | val requireOptInAnnotations: List 7 | val isEnum: Boolean 8 | val isParcelable: Boolean 9 | val isSerializable: Boolean 10 | val isKtxSerializable: Boolean 11 | val valueClassInnerInfo: ValueClassInnerInfo? 12 | val visibility: Visibility 13 | } 14 | 15 | data class TypeInfo( 16 | val value: Type, 17 | val isNullable: Boolean, 18 | val hasCustomTypeSerializer: Boolean 19 | ): CodeGenType by value 20 | 21 | data class Type( 22 | override val importable: Importable, 23 | override val typeArguments: List, 24 | override val requireOptInAnnotations: List, 25 | override val isEnum: Boolean, 26 | override val isParcelable: Boolean, 27 | override val isSerializable: Boolean, 28 | override val isKtxSerializable: Boolean, 29 | override val valueClassInnerInfo: ValueClassInnerInfo?, 30 | override val visibility: Visibility, 31 | ): CodeGenType 32 | 33 | data class ValueClassInnerInfo( 34 | val typeInfo: TypeInfo, 35 | val isConstructorPublic: Boolean, 36 | val publicNonNullableField: String? 37 | ) 38 | -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/model/CustomNavType.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.model 2 | 3 | data class CustomNavType( 4 | val name: String, 5 | val serializer: NavTypeSerializer?, 6 | val importable: Importable 7 | ) -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/model/DeepLink.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.model 2 | 3 | data class DeepLink( 4 | val action: String, 5 | val mimeType: String, 6 | val uriPattern: String, 7 | ) -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/model/DefaultValue.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.model 2 | 3 | import com.ramcosta.composedestinations.codegen.commons.IllegalDestinationsSetup 4 | 5 | sealed interface DefaultValue { 6 | 7 | data class Error(val throwable: Throwable) : DefaultValue 8 | 9 | data object NonExistent: DefaultValue 10 | 11 | data class Available( 12 | val code: String, 13 | val imports: List = emptyList() 14 | ) : DefaultValue 15 | } 16 | 17 | fun DefaultValue.unwrapAvailable(): DefaultValue.Available? = when (this) { 18 | is DefaultValue.Available -> this 19 | is DefaultValue.Error -> throw IllegalDestinationsSetup("Error reading default value", throwable) 20 | is DefaultValue.NonExistent -> null 21 | } 22 | -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/model/DestinationNavArgsType.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.model 2 | 3 | data class RawNavArgsClass( 4 | val parameters: List, 5 | val visibility: Visibility, 6 | val type: Importable, 7 | // useful only for nav graph args cases 8 | val extraStartRouteArgs: RawNavArgsClass? = null 9 | ) 10 | -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/model/Importable.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.model 2 | 3 | import com.ramcosta.composedestinations.codegen.commons.sanitizePackageName 4 | 5 | data class Importable( 6 | val simpleName: String, 7 | val qualifiedName: String 8 | ) { 9 | 10 | val preferredSimpleName = getCodeFriendlyName() 11 | internal val importStatement = getImportStatement().sanitizePackageName() 12 | 13 | private fun getImportStatement() = "import " + if (preferredSimpleName == simpleName) { 14 | qualifiedName 15 | } else { 16 | val suffixToRemove = preferredSimpleName.split(".").drop(1).joinToString(".") 17 | qualifiedName.removeSuffix(".$suffixToRemove") 18 | } 19 | 20 | private fun getCodeFriendlyName(): String { 21 | val splits = qualifiedName.split(".") 22 | if (!splits.last()[0].isUpperCase()) { 23 | // if last is not starting with uppercase, we don't do anything here 24 | return simpleName 25 | } 26 | 27 | val result = StringBuilder() 28 | for (part in splits) { 29 | if (part.firstOrNull()?.isUpperCase() == true) { 30 | if (result.isNotEmpty()) { 31 | result.append('.') 32 | } 33 | result.append(part) 34 | } 35 | } 36 | 37 | return result.toString() 38 | } 39 | } -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/model/NavGraphInfo.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.model 2 | 3 | data class NavGraphInfo( 4 | val isNavHostGraph: Boolean, 5 | val graphType: Importable 6 | ) -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/model/NavTypeSerializer.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.model 2 | 3 | data class NavTypeSerializer( 4 | val classKind: ClassKind, 5 | val serializerType: Importable, 6 | val genericType: Importable, 7 | ) 8 | -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/model/Parameter.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.model 2 | 3 | data class Parameter( 4 | val name: String, 5 | val type: TypeInfo, 6 | val hasDefault: Boolean, 7 | val isMarkedNavHostParam: Boolean, 8 | val defaultValue: DefaultValue, 9 | ) { 10 | val isMandatory: Boolean get() = !type.isNullable && !hasDefault 11 | } -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/model/SubModuleInfo.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.model 2 | 3 | data class SubModuleInfo( 4 | val name: String?, 5 | val genPackageName: String, 6 | val hasNavArgsPackage: Boolean, 7 | val topLevelGraphs: List, 8 | val publicResultSenders: List 9 | ) 10 | 11 | data class DestinationResultSenderInfo( 12 | val genDestinationQualifiedName: String, 13 | val resultTypeQualifiedName: String, 14 | val isResultTypeNullable: Boolean, 15 | val resultNavTypeQualifiedName: String 16 | ) -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/model/TypeArgument.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.model 2 | 3 | sealed interface TypeArgument { 4 | 5 | data class Typed( 6 | val type: TypeInfo, 7 | val varianceLabel: String 8 | ): TypeArgument 9 | 10 | data class Error( 11 | val linesStr: String, 12 | ) : TypeArgument 13 | 14 | data object GenericType: TypeArgument 15 | 16 | data object Star : TypeArgument { 17 | const val varianceLabel = "*" 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/model/Visibility.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.model 2 | 3 | enum class Visibility { 4 | PUBLIC, INTERNAL, PRIVATE 5 | } -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/templates/ArgsToSavedStateHandleTemplate.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.templates 2 | 3 | import com.ramcosta.composedestinations.codegen.codeGenBasePackageName 4 | import com.ramcosta.composedestinations.codegen.commons.CORE_PACKAGE_NAME 5 | import com.ramcosta.composedestinations.codegen.templates.core.FileTemplate 6 | import com.ramcosta.composedestinations.codegen.templates.core.setOfImportable 7 | 8 | const val VISIBILITY_PLACEHOLDER = "@VISIBILITY_PLACEHOLDER@" 9 | const val ARGS_DATA_CLASS_SIMPLE_NAME = "@ARGS_DATA_CLASS_SIMPLE_NAME@" 10 | const val NAV_TYPE_PUT_IN_BUNDLE_CALLS_PLACEHOLDER = "@NAV_TYPE_PUT_IN_BUNDLE_CALLS_PLACEHOLDER@" 11 | const val ALL_ARGS_SAVED_STATE_HANDLE_FUNCTIONS_PLACEHOLDER = "@ALL_ARGS_SAVED_STATE_HANDLE_FUNCTIONS_PLACEHOLDER@" 12 | 13 | val argsToSavedStateHandleTemplate = FileTemplate( 14 | packageStatement = "package $codeGenBasePackageName.navargs", 15 | imports = setOfImportable( 16 | "androidx.lifecycle.SavedStateHandle", 17 | "$CORE_PACKAGE_NAME.navargs.primitives.*", 18 | "$CORE_PACKAGE_NAME.navargs.primitives.array.*", 19 | "$CORE_PACKAGE_NAME.navargs.primitives.arraylist.*", 20 | ), 21 | sourceCode = """ 22 | $ALL_ARGS_SAVED_STATE_HANDLE_FUNCTIONS_PLACEHOLDER 23 | """.trimIndent() 24 | ) 25 | -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/templates/ModuleDestinationsTemplate.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.templates 2 | 3 | import com.ramcosta.composedestinations.codegen.codeGenBasePackageName 4 | import com.ramcosta.composedestinations.codegen.commons.CORE_PACKAGE_NAME 5 | import com.ramcosta.composedestinations.codegen.templates.core.FileTemplate 6 | import com.ramcosta.composedestinations.codegen.templates.core.setOfImportable 7 | 8 | const val MODULE_DESTINATIONS_PLACEHOLDER = "@MODULE_DESTINATIONS_PLACEHOLDER@" 9 | const val MODULE_EXTERNAL_DESTINATIONS_PLACEHOLDER = "@MODULE_EXTERNAL_DESTINATIONS_PLACEHOLDER@" 10 | const val MODULE_DESTINATIONS_CLASS_NAME_PLACEHOLDER = "@MODULE_DESTINATIONS_CLASS_NAME_PLACEHOLDER@" 11 | 12 | val moduleDestinationTemplate = FileTemplate( 13 | packageStatement = "package $codeGenBasePackageName", 14 | imports = setOfImportable( 15 | "$CORE_PACKAGE_NAME.spec.*", 16 | "${codeGenBasePackageName}.destinations.*", 17 | "$CORE_PACKAGE_NAME.annotation.internal.GeneratedCodeExternalDestinations" 18 | ), 19 | sourceCode = """ 20 | ${REQUIRE_OPT_IN_ANNOTATIONS_PLACEHOLDER}public data object $MODULE_DESTINATIONS_CLASS_NAME_PLACEHOLDER : ModuleDestinationsContainer { 21 | public override val destinations: List = listOf( 22 | $MODULE_DESTINATIONS_PLACEHOLDER 23 | ) 24 | 25 | $MODULE_EXTERNAL_DESTINATIONS_PLACEHOLDER 26 | public object Includes 27 | } 28 | 29 | """.trimIndent() 30 | ) -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/templates/NavGraphsObjectTemplate.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.templates 2 | 3 | import com.ramcosta.composedestinations.codegen.codeGenBasePackageName 4 | import com.ramcosta.composedestinations.codegen.commons.CORE_ALIAS_DESTINATION_SPEC 5 | import com.ramcosta.composedestinations.codegen.commons.CORE_ALIAS_NAV_GRAPH_SPEC 6 | import com.ramcosta.composedestinations.codegen.commons.CORE_PACKAGE_NAME 7 | import com.ramcosta.composedestinations.codegen.commons.GENERATED_NAV_GRAPHS_OBJECT 8 | import com.ramcosta.composedestinations.codegen.moduleName 9 | import com.ramcosta.composedestinations.codegen.templates.core.FileTemplate 10 | import com.ramcosta.composedestinations.codegen.templates.core.setOfImportable 11 | 12 | const val NAV_GRAPHS_PLACEHOLDER = "[NAV_GRAPHS_PLACEHOLDER]" 13 | const val NAV_GRAPHS_PRETTY_KDOC_PLACEHOLDER = "[NAV_GRAPHS_PRETTY_KDOC_PLACEHOLDER]" 14 | 15 | val navGraphsObjectTemplate = FileTemplate( 16 | packageStatement = "package $codeGenBasePackageName", 17 | imports = setOfImportable( 18 | "${codeGenBasePackageName}.destinations.*", 19 | "${codeGenBasePackageName}.navgraphs.*", 20 | "$CORE_PACKAGE_NAME.spec.*", 21 | ), 22 | sourceCode = """ 23 | /** 24 | * Class generated if any Composable is annotated with `@Destination`. 25 | * It aggregates all [$CORE_ALIAS_DESTINATION_SPEC]s in their [$CORE_ALIAS_NAV_GRAPH_SPEC]s.$NAV_GRAPHS_PRETTY_KDOC_PLACEHOLDER 26 | */ 27 | internal object $moduleName$GENERATED_NAV_GRAPHS_OBJECT { 28 | 29 | $NAV_GRAPHS_PLACEHOLDER 30 | } 31 | """.trimIndent() 32 | ) -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/templates/core/FileTemplate.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.templates.core 2 | 3 | import com.ramcosta.composedestinations.codegen.model.Importable 4 | 5 | class FileTemplate( 6 | val packageStatement: String, 7 | val imports: Set, 8 | val sourceCode: String 9 | ) 10 | 11 | fun setOfImportable(vararg qualifiedNames: Any?): Set { 12 | return qualifiedNames.mapNotNullTo(mutableSetOf()) { 13 | it ?: return@mapNotNullTo null 14 | 15 | if (it is String) { 16 | Importable(it.substring(it.lastIndexOf(".") + 1), it) 17 | } else { 18 | it as Importable 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/templates/navtype/NavTypeTemplateCommons.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.templates.navtype 2 | 3 | const val NAV_TYPE_VISIBILITY = "[NAV_TYPE_VISIBILITY]" 4 | const val NAV_TYPE_NAME = "[NAV_TYPE_NAME]" 5 | const val NAV_TYPE_CLASS_SIMPLE_NAME = "[NAV_TYPE_CLASS_SIMPLE_NAME]" 6 | const val CLASS_SIMPLE_NAME_CAMEL_CASE = "[CLASS_SIMPLE_NAME_CAMEL_CASE]" 7 | const val PARSE_VALUE_CAST_TO_CLASS = "[PARSE_VALUE_CAST_TO_CLASS]" 8 | const val DESTINATIONS_NAV_TYPE_SERIALIZER_TYPE = "[DESTINATIONS_NAV_TYPE_SERIALIZER_TYPE]" 9 | const val SERIALIZER_SIMPLE_CLASS_NAME = "[SERIALIZER_SIMPLE_CLASS_NAME]" 10 | -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/templates/navtype/arrays/ArrayCommons.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.templates.navtype.arrays 2 | 3 | 4 | const val TYPE_ARG_CLASS_SIMPLE_NAME = "[TYPE_ARG_CLASS_SIMPLE_NAME]" 5 | const val ARRAY_CUSTOM_NAV_TYPE_NAME = "[ARRAY_CUSTOM_NAV_TYPE_NAME]" 6 | const val SERIALIZER_TYPE_ARG_CLASS_SIMPLE_NAME = "[SERIALIZER_TYPE_ARG_CLASS_SIMPLE_NAME]" 7 | const val NAV_TYPE_INITIALIZATION_CODE = "[NAV_TYPE_INITIALIZATION_CODE]" -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/writers/DefaultKtxSerializableNavTypeSerializerWriter.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.writers 2 | 3 | import com.ramcosta.composedestinations.codegen.facades.CodeOutputStreamMaker 4 | import com.ramcosta.composedestinations.codegen.templates.navtype.DEFAULT_KTX_SERIALIZABLE_NAV_TYPE_SERIALIZER_TEMPLATE_NAME 5 | import com.ramcosta.composedestinations.codegen.templates.navtype.defaultKtxSerializableNavTypeSerializerTemplate 6 | import com.ramcosta.composedestinations.codegen.templates.navtype.defaultKtxSerializableNavTypeSerializerTemplatePkg 7 | import com.ramcosta.composedestinations.codegen.writers.helpers.writeSourceFile 8 | 9 | class DefaultKtxSerializableNavTypeSerializerWriter( 10 | private val codeGenerator: CodeOutputStreamMaker, 11 | ) { 12 | fun write() { 13 | codeGenerator.makeFile( 14 | packageName = defaultKtxSerializableNavTypeSerializerTemplatePkg, 15 | name = DEFAULT_KTX_SERIALIZABLE_NAV_TYPE_SERIALIZER_TEMPLATE_NAME, 16 | ).writeSourceFile(defaultKtxSerializableNavTypeSerializerTemplate) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/writers/DestinationsWriter.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.writers 2 | 3 | import com.ramcosta.composedestinations.codegen.facades.CodeOutputStreamMaker 4 | import com.ramcosta.composedestinations.codegen.model.CodeGenConfig 5 | import com.ramcosta.composedestinations.codegen.model.CodeGenProcessedDestination 6 | import com.ramcosta.composedestinations.codegen.model.CustomNavType 7 | import com.ramcosta.composedestinations.codegen.model.SubModuleInfo 8 | import com.ramcosta.composedestinations.codegen.model.Type 9 | import com.ramcosta.composedestinations.codegen.writers.helpers.ImportableHelper 10 | import com.ramcosta.composedestinations.codegen.writers.helpers.NavArgResolver 11 | 12 | internal class DestinationsWriter( 13 | private val codeGenConfig: CodeGenConfig, 14 | private val codeGenerator: CodeOutputStreamMaker, 15 | private val customNavTypeByType: Map, 16 | private val submodules: List, 17 | ) { 18 | 19 | fun write( 20 | destinations: List, 21 | ) { 22 | 23 | destinations.forEach { destination -> 24 | val importableHelper = ImportableHelper() 25 | SingleDestinationWriter( 26 | codeGenConfig, 27 | codeGenerator, 28 | NavArgResolver(customNavTypeByType, importableHelper), 29 | destination, 30 | importableHelper, 31 | customNavTypeByType, 32 | submodules 33 | ).write() 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /compose-destinations-codegen/src/main/java/com/ramcosta/composedestinations/codegen/writers/helpers/FileWriter.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.codegen.writers.helpers 2 | 3 | import com.ramcosta.composedestinations.codegen.commons.plusAssign 4 | import com.ramcosta.composedestinations.codegen.model.Importable 5 | import com.ramcosta.composedestinations.codegen.templates.core.FileTemplate 6 | import java.io.OutputStream 7 | 8 | fun OutputStream.writeSourceFile( 9 | packageStatement: String, 10 | importableHelper: ImportableHelper, 11 | sourceCode: String, 12 | fileOptIns: Set = emptySet() 13 | ) { 14 | this.use { 15 | it += "${fileOptInsCode(fileOptIns)}$packageStatement\n${importableHelper.addResolvedImportsToSrcCode(sourceCode)}" 16 | } 17 | } 18 | 19 | fun OutputStream.writeSourceFile( 20 | fileTemplate: FileTemplate 21 | ) { 22 | writeSourceFile( 23 | packageStatement = fileTemplate.packageStatement, 24 | importableHelper = ImportableHelper(fileTemplate.imports), 25 | sourceCode = fileTemplate.sourceCode 26 | ) 27 | } 28 | 29 | private fun fileOptInsCode( 30 | fileOptIns: Set, 31 | ): String { 32 | if (fileOptIns.isEmpty()) { 33 | return "" 34 | } 35 | return "@file:OptIn(${fileOptIns.joinToString(", ") { it.qualifiedName + "::class" }})\n\n" 36 | } 37 | -------------------------------------------------------------------------------- /compose-destinations-ksp/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /compose-destinations-ksp/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") 3 | } 4 | 5 | apply(from = "${rootProject.projectDir}/publish.gradle") 6 | 7 | java { 8 | sourceCompatibility = JavaVersion.VERSION_1_8 9 | targetCompatibility = JavaVersion.VERSION_1_8 10 | } 11 | 12 | kotlin { 13 | compilerOptions { 14 | freeCompilerArgs.addAll("-opt-in=kotlin.RequiresOptIn") 15 | } 16 | 17 | jvmToolchain(8) 18 | } 19 | 20 | dependencies { 21 | implementation(project(":compose-destinations-codegen")) 22 | 23 | implementation(libs.pprint) 24 | implementation(libs.ksp.api) 25 | implementation(libs.test.junit) 26 | } 27 | -------------------------------------------------------------------------------- /compose-destinations-ksp/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=ksp 2 | POM_NAME=ksp -------------------------------------------------------------------------------- /compose-destinations-ksp/src/main/kotlin/com/ramcosta/composedestinations/ksp/ProcessorProvider.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.ksp 2 | 3 | import com.google.devtools.ksp.processing.SymbolProcessor 4 | import com.google.devtools.ksp.processing.SymbolProcessorEnvironment 5 | import com.google.devtools.ksp.processing.SymbolProcessorProvider 6 | import com.ramcosta.composedestinations.ksp.processors.Processor 7 | 8 | class ProcessorProvider : SymbolProcessorProvider { 9 | override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { 10 | return Processor( 11 | environment.codeGenerator, 12 | environment.logger, 13 | environment.options 14 | ) 15 | } 16 | } -------------------------------------------------------------------------------- /compose-destinations-ksp/src/main/kotlin/com/ramcosta/composedestinations/ksp/codegen/KspCodeOutputStreamMaker.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.ksp.codegen 2 | 3 | import com.google.devtools.ksp.processing.CodeGenerator 4 | import com.google.devtools.ksp.processing.Dependencies 5 | import com.ramcosta.composedestinations.codegen.facades.CodeOutputStreamMaker 6 | import com.ramcosta.composedestinations.ksp.commons.KSFileSourceMapper 7 | import java.io.OutputStream 8 | 9 | class KspCodeOutputStreamMaker( 10 | private val codeGenerator: CodeGenerator, 11 | private val sourceMapper: KSFileSourceMapper 12 | ) : CodeOutputStreamMaker { 13 | 14 | override val packageNamesWrittenTo = mutableListOf() 15 | 16 | override fun makeFile( 17 | name: String, 18 | packageName: String, 19 | extensionName: String, 20 | vararg sourceIds: String 21 | ): OutputStream { 22 | 23 | val sources = sourceIds.mapNotNull { sourceMapper.mapToKSFile(it) }.toTypedArray() 24 | val dependencies = if (sources.isEmpty()) { 25 | Dependencies.ALL_FILES 26 | } else { 27 | Dependencies( 28 | true, 29 | *sources 30 | ) 31 | } 32 | 33 | packageNamesWrittenTo.add(packageName) 34 | 35 | return codeGenerator.createNewFile( 36 | dependencies = dependencies, 37 | fileName = name, 38 | packageName = packageName, 39 | extensionName = extensionName 40 | ) 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /compose-destinations-ksp/src/main/kotlin/com/ramcosta/composedestinations/ksp/codegen/KspLogger.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.ksp.codegen 2 | 3 | import com.google.devtools.ksp.processing.KSPLogger 4 | import com.ramcosta.composedestinations.codegen.facades.Logger 5 | import com.ramcosta.composedestinations.codegen.facades.PPrinter 6 | import com.ramcosta.composedestinations.codegen.model.CodeGenConfig 7 | import io.exoquery.fansi.Attrs 8 | import java.util.Locale 9 | 10 | class KspLogger( 11 | private val codeGenConfig: CodeGenConfig, 12 | private val kspLogger: KSPLogger 13 | ): Logger { 14 | 15 | override val debugMode: Boolean = codeGenConfig.debugModeOutputDir != null 16 | override val debugModeOutputPath: String? = codeGenConfig.debugModeOutputDir?.let { 17 | "$it/composeDestinationsDebug/${codeGenConfig.registryId.lowercase(Locale.ROOT)}.txt" 18 | } 19 | 20 | private val _prettyPrinter: PPrinter = object: PPrinter { 21 | override fun pprint(any: Any): String { 22 | return io.exoquery.pprint(any, defaultHeight = Int.MAX_VALUE, colorLiteral = Attrs.Empty, colorApplyPrefix = Attrs.Empty).toString() 23 | } 24 | } 25 | override val prettyPrinter: PPrinter 26 | get() { 27 | check(debugMode) 28 | return _prettyPrinter 29 | } 30 | 31 | override fun logging(message: String) = kspLogger.logging(message) 32 | 33 | override fun info(message: String) = kspLogger.info(message) 34 | 35 | override fun warn(message: String) = kspLogger.warn(message) 36 | 37 | override fun error(message: String) = kspLogger.error(message) 38 | 39 | override fun exception(e: Throwable) = kspLogger.exception(e) 40 | } 41 | -------------------------------------------------------------------------------- /compose-destinations-ksp/src/main/kotlin/com/ramcosta/composedestinations/ksp/commons/KSFileSourceMapper.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.ksp.commons 2 | 3 | import com.google.devtools.ksp.symbol.KSFile 4 | 5 | fun interface KSFileSourceMapper { 6 | 7 | fun mapToKSFile(sourceId: String): KSFile? 8 | } -------------------------------------------------------------------------------- /compose-destinations-ksp/src/main/kotlin/com/ramcosta/composedestinations/ksp/commons/MutableKSFileSourceMapper.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.ksp.commons 2 | 3 | import com.google.devtools.ksp.symbol.KSFile 4 | 5 | internal class MutableKSFileSourceMapper: KSFileSourceMapper { 6 | 7 | private val sourceFilesById = mutableMapOf() 8 | 9 | operator fun set(sourceId: String, file: KSFile?) { 10 | sourceFilesById[sourceId] = file 11 | } 12 | 13 | override fun mapToKSFile(sourceId: String): KSFile? { 14 | return sourceFilesById[sourceId] 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /compose-destinations-ksp/src/main/kotlin/com/ramcosta/composedestinations/ksp/processors/DestinationAnnotationsPath.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.ksp.processors 2 | 3 | import com.google.devtools.ksp.symbol.KSAnnotation 4 | import com.google.devtools.ksp.symbol.KSFunctionDeclaration 5 | 6 | data class DestinationAnnotationsPath( 7 | val annotations: List, 8 | val function: KSFunctionDeclaration 9 | ) -------------------------------------------------------------------------------- /compose-destinations-ksp/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider: -------------------------------------------------------------------------------- 1 | com.ramcosta.composedestinations.ksp.ProcessorProvider -------------------------------------------------------------------------------- /compose-destinations-wear/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /compose-destinations-wear/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.library") 3 | kotlin("android") 4 | alias(libs.plugins.compose.compiler) 5 | } 6 | 7 | apply(from = "${rootProject.projectDir}/publish.gradle") 8 | 9 | android { 10 | 11 | namespace = "com.ramcosta.composedestinations.wear" 12 | compileSdk = libs.versions.compileSdk.get().toIntOrNull() 13 | 14 | defaultConfig { 15 | minSdk = 25 16 | 17 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 18 | consumerProguardFiles.add(File("consumer-rules.pro")) 19 | } 20 | 21 | buildTypes { 22 | release { 23 | isMinifyEnabled = false 24 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") 25 | } 26 | } 27 | 28 | compileOptions { 29 | sourceCompatibility = JavaVersion.VERSION_1_8 30 | targetCompatibility = JavaVersion.VERSION_1_8 31 | } 32 | 33 | kotlinOptions { 34 | jvmTarget = JavaVersion.VERSION_1_8.toString() 35 | } 36 | 37 | buildFeatures { 38 | compose = true 39 | } 40 | 41 | } 42 | 43 | kotlin { 44 | jvmToolchain(11) 45 | compilerOptions { 46 | freeCompilerArgs.addAll( 47 | "-opt-in=kotlin.RequiresOptIn", 48 | "-opt-in=com.ramcosta.composedestinations.annotation.internal.InternalDestinationsApi" 49 | ) 50 | } 51 | } 52 | 53 | dependencies { 54 | api(project(mapOf("path" to ":compose-destinations"))) 55 | 56 | api(libs.wear.compose.navigation) 57 | } 58 | -------------------------------------------------------------------------------- /compose-destinations-wear/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raamcosta/compose-destinations/34a138134b053bc588554e46bcdf360f8210f3d6/compose-destinations-wear/consumer-rules.pro -------------------------------------------------------------------------------- /compose-destinations-wear/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=wear-core 2 | POM_NAME=wear-core -------------------------------------------------------------------------------- /compose-destinations-wear/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.kts. 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 -------------------------------------------------------------------------------- /compose-destinations-wear/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /compose-destinations/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /compose-destinations/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.library") 3 | kotlin("android") 4 | alias(libs.plugins.compose.compiler) 5 | } 6 | 7 | apply(from = "${rootProject.projectDir}/publish.gradle") 8 | 9 | android { 10 | 11 | namespace = "com.ramcosta.composedestinations" 12 | compileSdk = libs.versions.compileSdk.get().toIntOrNull() 13 | 14 | defaultConfig { 15 | minSdk = libs.versions.minSdk.get().toIntOrNull() 16 | targetSdk = libs.versions.targetSdk.get().toIntOrNull() 17 | 18 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 19 | consumerProguardFiles.add(File("consumer-rules.pro")) 20 | } 21 | 22 | buildTypes { 23 | release { 24 | isMinifyEnabled = false 25 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") 26 | } 27 | } 28 | 29 | lint { 30 | disable.add("EmptyNavDeepLink") 31 | } 32 | 33 | compileOptions { 34 | sourceCompatibility = JavaVersion.VERSION_1_8 35 | targetCompatibility = JavaVersion.VERSION_1_8 36 | } 37 | 38 | kotlinOptions { 39 | jvmTarget = JavaVersion.VERSION_1_8.toString() 40 | } 41 | 42 | buildFeatures { 43 | compose = true 44 | } 45 | } 46 | 47 | kotlin { 48 | compilerOptions { 49 | freeCompilerArgs.addAll( 50 | "-opt-in=kotlin.RequiresOptIn", 51 | "-opt-in=com.ramcosta.composedestinations.annotation.internal.InternalDestinationsApi" 52 | ) 53 | } 54 | } 55 | 56 | dependencies { 57 | api(libs.compose.navigation) 58 | } 59 | -------------------------------------------------------------------------------- /compose-destinations/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raamcosta/compose-destinations/34a138134b053bc588554e46bcdf360f8210f3d6/compose-destinations/consumer-rules.pro -------------------------------------------------------------------------------- /compose-destinations/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=core 2 | POM_NAME=core -------------------------------------------------------------------------------- /compose-destinations/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.kts. 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 -------------------------------------------------------------------------------- /compose-destinations/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/animations/NavHostAnimatedDestinationStyle.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.animations 2 | 3 | import androidx.compose.animation.AnimatedContentTransitionScope 4 | import androidx.compose.animation.EnterTransition 5 | import androidx.compose.animation.ExitTransition 6 | import androidx.navigation.NavBackStackEntry 7 | import com.ramcosta.composedestinations.spec.DestinationStyle 8 | 9 | /** 10 | * Like [DestinationStyle.Animated] but doesn't allow the return of null values. 11 | * 12 | * This is mainly used for [com.ramcosta.composedestinations.DestinationsNavHost] call 13 | * and [com.ramcosta.composedestinations.annotation.NavHostGraph.defaultTransitions] since 14 | * for NavHost graphs, transitions must be defined (even if they're "No animations"). 15 | */ 16 | abstract class NavHostAnimatedDestinationStyle: DestinationStyle.Animated() { 17 | 18 | abstract override val enterTransition: AnimatedContentTransitionScope.() -> EnterTransition 19 | 20 | abstract override val exitTransition: AnimatedContentTransitionScope.() -> ExitTransition 21 | 22 | override val popEnterTransition: AnimatedContentTransitionScope.() -> EnterTransition 23 | get() = enterTransition 24 | 25 | override val popExitTransition: AnimatedContentTransitionScope.() -> ExitTransition 26 | get() = exitTransition 27 | } -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/animations/defaults/DefaultAnimationParams.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.animations.defaults 2 | 3 | import androidx.compose.animation.AnimatedContentTransitionScope 4 | import androidx.compose.animation.EnterTransition 5 | import androidx.compose.animation.ExitTransition 6 | import androidx.compose.animation.core.tween 7 | import androidx.compose.animation.fadeIn 8 | import androidx.compose.animation.fadeOut 9 | import androidx.navigation.NavBackStackEntry 10 | import com.ramcosta.composedestinations.animations.NavHostAnimatedDestinationStyle 11 | 12 | /** 13 | * Disables all animations for this NavHost by default. 14 | */ 15 | object NoTransitions : NavHostAnimatedDestinationStyle() { 16 | override val enterTransition: AnimatedContentTransitionScope.() -> EnterTransition = { 17 | EnterTransition.None 18 | } 19 | 20 | override val exitTransition: AnimatedContentTransitionScope.() -> ExitTransition = { 21 | ExitTransition.None 22 | } 23 | } 24 | 25 | /** 26 | * Uses default fading animations for this NavHost by default. 27 | */ 28 | object DefaultFadingTransitions: NavHostAnimatedDestinationStyle() { 29 | override val enterTransition: AnimatedContentTransitionScope.() -> EnterTransition = { 30 | fadeIn(animationSpec = tween(700)) 31 | } 32 | 33 | override val exitTransition: AnimatedContentTransitionScope.() -> ExitTransition = { 34 | fadeOut(animationSpec = tween(700)) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/annotation/ExternalModuleDestinations.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.annotation 2 | 3 | import com.ramcosta.composedestinations.spec.DestinationSpec 4 | import com.ramcosta.composedestinations.spec.ModuleDestinationsContainer 5 | import kotlin.reflect.KClass 6 | 7 | /** 8 | * Annotation used to import all destinations from [T] [ModuleDestinationsContainer]. 9 | * You can then override certain things about individual destinations, using 10 | * the [overriding] parameter. 11 | * 12 | * @param overriding can be used to pass individual [ExternalDestination] 13 | * to control parameters available with that annotation for that specific [DestinationSpec] 14 | */ 15 | @Repeatable 16 | @Retention(AnnotationRetention.SOURCE) 17 | annotation class ExternalModuleDestinations( 18 | val overriding: Array> = [] 19 | ) 20 | 21 | /** 22 | * Use [with] parameters to override them for [destination] specifically. 23 | */ 24 | @Retention(AnnotationRetention.SOURCE) 25 | annotation class OverrideDestination( 26 | val destination: KClass, 27 | val with: ExternalDestination, 28 | ) 29 | -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/annotation/ExternalModuleGraph.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.annotation 2 | 3 | /** 4 | * Marks the [Destination] or [NavGraph] as having a parent navigation graph 5 | * defined in another module. 6 | * This will make the destination / nav graph not usable unless it is imported using 7 | * [ExternalDestination] or [ExternalNavGraph]. 8 | */ 9 | @Retention(AnnotationRetention.SOURCE) 10 | annotation class ExternalModuleGraph -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/annotation/ExternalNavGraph.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.annotation 2 | 3 | import com.ramcosta.composedestinations.annotation.parameters.DeepLink 4 | import com.ramcosta.composedestinations.spec.DestinationStyle 5 | import com.ramcosta.composedestinations.spec.NavGraphSpec 6 | import kotlin.reflect.KClass 7 | 8 | /** 9 | * Can be used in a companion object of a [NavGraph] annotated annotation 10 | * to include a [NavGraphSpec] from another module as nested in the navigation graph. 11 | * 12 | * Example: 13 | * 14 | * ``` 15 | * @NavGraph 16 | * annotation class ProfileGraph { 17 | * @ExternalNavGraph 18 | * companion object Includes // name of the companion object does not matter 19 | * } 20 | * ``` 21 | * 22 | * This would create a "Profile" nav graph nested on "Root" graph 23 | * and with "AnotherModuleNavGraph" as a nested graph of "Profile". 24 | * 25 | * @param T the [NavGraphSpec] from another module to include in the navigation graph 26 | * @param start defines this navigation graph as the start of the navigation graph 27 | * it is being included on. 28 | * @param deepLinks adds [DeepLink]s to this nav graph. Both these and the deep links 29 | * defined on the declaring module (if any) can be used to navigate to this nav graph. 30 | * @param defaultTransitions overrides animations set on the declaring module (if any). 31 | * See [NavGraph.defaultTransitions] for more info on these animations. 32 | */ 33 | @Repeatable 34 | @Retention(AnnotationRetention.SOURCE) 35 | annotation class ExternalNavGraph( 36 | val start: Boolean = false, 37 | val deepLinks: Array = [], 38 | val defaultTransitions: KClass = NoOverride::class, 39 | ) { 40 | companion object { 41 | internal object NoOverride: DestinationStyle.Animated() 42 | } 43 | } -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/annotation/NavHostDefaultStartArgs.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.annotation 2 | 3 | /** 4 | * Annotation used on a public top level function to make it be used 5 | * as the start navigation arguments of a [NavHostGraph]. 6 | * Useful when the start route of the [NavHostGraph] has mandatory 7 | * arguments and you don't want to make them non mandatory (because 8 | * maybe when you navigate to that destination, defaults make no sense, 9 | * they only make sense when the app starts initially). 10 | * 11 | * Example: 12 | * ``` 13 | * @NavHostDefaultStartArgs 14 | * fun defaultRootGraphStartArgs() = MyStartDestinationArgs( 15 | * //fill default arg values here 16 | * ) 17 | * ``` 18 | * 19 | * Note that `RootGraph` in the example is the [NavHostGraph] you want 20 | * to use these on, and the `MyStartDestinationArgs` is the start route 21 | * arguments class (which needs to match with the nav arguments class of 22 | * the NavHost's start route). 23 | * 24 | * @param T annotation annotated with [NavHostGraph] to which you want 25 | * to se the default nav arguments to. 26 | */ 27 | @Target(AnnotationTarget.FUNCTION) 28 | @Retention(AnnotationRetention.SOURCE) 29 | annotation class NavHostDefaultStartArgs -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/annotation/NavHostGraph.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.annotation 2 | 3 | import com.ramcosta.composedestinations.animations.NavHostAnimatedDestinationStyle 4 | import com.ramcosta.composedestinations.animations.defaults.NoTransitions 5 | import com.ramcosta.composedestinations.annotation.parameters.CodeGenVisibility 6 | import kotlin.reflect.KClass 7 | 8 | /** 9 | * Like [NavGraph] but denotes a top level nav graph, i.e one that is not nested in any other 10 | * nav graph (aka it doesn't have a parent). 11 | * These are used to pass to [com.ramcosta.composedestinations.DestinationsNavHost] call. 12 | * 13 | * [RootGraph] is one such graph that can be used out of the box. 14 | */ 15 | @Target(AnnotationTarget.ANNOTATION_CLASS) 16 | annotation class NavHostGraph( 17 | val defaultTransitions: KClass = NoTransitions::class, 18 | val route: String = NavGraph.ANNOTATION_NAME, 19 | val visibility: CodeGenVisibility = CodeGenVisibility.PUBLIC 20 | ) 21 | 22 | /** 23 | * Out the box [NavHostGraph] that can be used in destinations or graphs if there 24 | * isn't a need to specify one of the parameters of [NavHostGraph]. 25 | */ 26 | @NavHostGraph 27 | annotation class RootGraph -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/annotation/internal/GeneratedCodeExternalDestinations.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.annotation.internal 2 | 3 | import com.ramcosta.composedestinations.spec.DestinationSpec 4 | import kotlin.reflect.KClass 5 | 6 | /** 7 | * To be used by generated code. 8 | */ 9 | @Retention(AnnotationRetention.BINARY) 10 | annotation class GeneratedCodeExternalDestinations( 11 | val destinations: Array> 12 | ) 13 | -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/annotation/internal/InternalDestinationsApi.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.annotation.internal 2 | 3 | @RequiresOptIn(message = "This API is internal to Compose Destinations library. Do NOT use it!") 4 | @Retention(AnnotationRetention.BINARY) 5 | @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY) 6 | annotation class InternalDestinationsApi 7 | -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/annotation/parameters/CodeGenVisibility.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.annotation.parameters 2 | 3 | /** 4 | * Controls visibility of the generated Destination or NavGraph. 5 | */ 6 | enum class CodeGenVisibility { 7 | PUBLIC, 8 | INTERNAL, 9 | } -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/annotation/parameters/DeepLink.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.annotation.parameters 2 | 3 | /** 4 | * Can be used in [com.ramcosta.composedestinations.annotation.Destination] to describe a deep link 5 | * connection to that destination. 6 | */ 7 | @Retention(AnnotationRetention.SOURCE) 8 | annotation class DeepLink( 9 | val action: String = "", 10 | val mimeType: String = "", 11 | val uriPattern: String = "" 12 | ) 13 | 14 | /** 15 | * Can be used in the suffix part of the [DeepLink.uriPattern] 16 | * to signal code generation to replace this with the full 17 | * route of the [com.ramcosta.composedestinations.annotation.Destination] that contains arguments. 18 | * 19 | * Example: 20 | * 21 | * ``` 22 | * @Destination( 23 | * route = "profile", 24 | * deepLinks = [ 25 | * DeepLink( 26 | * uriPattern = "https://myapp.com/$FULL_ROUTE_PLACEHOLDER" 27 | * ) 28 | * ] 29 | * ) 30 | * @Composable 31 | * fun ProfileScreen( 32 | * id: String 33 | * ) 34 | * ``` 35 | * 36 | * Since the `ProfileScreen` has an argument (`id`), the final uriPattern 37 | * used will be `"https://myapp.com/profile/{id}"` 38 | */ 39 | const val FULL_ROUTE_PLACEHOLDER = "@ramcosta.destinations.fullroute@" -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/annotation/parameters/NavHostParam.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.annotation.parameters 2 | 3 | /** 4 | * Annotation that can be used in arguments of Destination Composable 5 | * functions to signal Compose Destinations to not consider them as 6 | * navigation arguments. 7 | * By default, all arguments with types Compose Destinations allows 8 | * you to pass as a navigation argument are considered navigation 9 | * arguments. So use this if you have an argument with such type 10 | * but you don't want to pass it from previous destination. 11 | * 12 | * There are usually two types of arguments you can set on Destination 13 | * annotated functions: 14 | * - Navigation arguments 15 | * - Parameters passed in from DestinationsNavHost level 16 | * 17 | * Arguments marked with this annotation, need to be passed in from 18 | * DestinationsNavHost. 19 | */ 20 | @Target(AnnotationTarget.VALUE_PARAMETER) 21 | @Retention(AnnotationRetention.SOURCE) 22 | annotation class NavHostParam 23 | -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/manualcomposablecalls/DestinationLambda.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.manualcomposablecalls 2 | 3 | import android.annotation.SuppressLint 4 | import androidx.annotation.RestrictTo 5 | import androidx.compose.runtime.Composable 6 | import com.ramcosta.composedestinations.scope.AnimatedDestinationScope 7 | import com.ramcosta.composedestinations.scope.BottomSheetDestinationScope 8 | import com.ramcosta.composedestinations.scope.DestinationScope 9 | 10 | @SuppressLint("ComposableNaming") 11 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 12 | sealed class DestinationLambda { 13 | 14 | @Composable 15 | abstract operator fun invoke( 16 | destinationScope: DestinationScope 17 | ) 18 | 19 | class Normal( 20 | val content: @Composable AnimatedDestinationScope.() -> Unit 21 | ) : DestinationLambda() { 22 | 23 | @Composable 24 | override operator fun invoke( 25 | destinationScope: DestinationScope 26 | ) { 27 | (destinationScope as AnimatedDestinationScope).content() 28 | } 29 | } 30 | 31 | class Dialog( 32 | val content: @Composable DestinationScope.() -> Unit 33 | ) : DestinationLambda() { 34 | 35 | @Composable 36 | override operator fun invoke( 37 | destinationScope: DestinationScope 38 | ) { 39 | destinationScope.content() 40 | } 41 | } 42 | 43 | class BottomSheet( 44 | val content: @Composable BottomSheetDestinationScope.() -> Unit 45 | ) : DestinationLambda() { 46 | 47 | @Composable 48 | override operator fun invoke( 49 | destinationScope: DestinationScope 50 | ) { 51 | (destinationScope as BottomSheetDestinationScope).content() 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/manualcomposablecalls/ManualComposableCalls.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.manualcomposablecalls 2 | 3 | import androidx.annotation.RestrictTo 4 | import androidx.navigation.NavDeepLink 5 | import com.ramcosta.composedestinations.spec.DestinationStyle 6 | import com.ramcosta.composedestinations.spec.Route 7 | 8 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 9 | class ManualComposableCalls internal constructor( 10 | private val map: Map>, 11 | private val animations: Map, 12 | private val deepLinks: Map> 13 | ) { 14 | 15 | operator fun get(route: String): DestinationLambda<*>? { 16 | return map[route] 17 | } 18 | 19 | fun manualAnimation(route: String): DestinationStyle.Animated? { 20 | return animations[route] 21 | } 22 | 23 | fun manualDeepLinks(route: String): List? { 24 | return deepLinks[route] 25 | } 26 | } 27 | 28 | fun Route.allDeepLinks(manualComposableCalls: ManualComposableCalls?): List { 29 | val manualDeepLinks = manualComposableCalls?.manualDeepLinks(route) 30 | return if (manualDeepLinks != null) { 31 | manualDeepLinks + deepLinks 32 | } else { 33 | deepLinks 34 | } 35 | } -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/navargs/DestinationsNavType.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.navargs 2 | 3 | import android.os.Bundle 4 | import androidx.lifecycle.SavedStateHandle 5 | import androidx.navigation.NavType 6 | 7 | /** 8 | * [NavType] version for Compose Destinations library. 9 | * It gets used by the generated code. 10 | */ 11 | abstract class DestinationsNavType: NavType(true) { 12 | 13 | abstract fun serializeValue(value: T): String? 14 | 15 | abstract fun get(savedStateHandle: SavedStateHandle, key: String): T 16 | 17 | abstract fun put(savedStateHandle: SavedStateHandle, key: String, value: T) 18 | 19 | fun safeGet(bundle: Bundle?, key: String): T? { 20 | return bundle?.let { get(it, key) } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/navargs/DestinationsNavTypeSerializer.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.navargs 2 | 3 | /** 4 | * Interface with behaviour for classes that can prepare a nonnull [T] type 5 | * into a string usable in the navigation route and can parse that same string 6 | * representation back into the [T] type. 7 | * 8 | * You can define an implementation for a specific type [T] and annotate it with 9 | * `@NavTypeSerializer` to make the generated code use it. 10 | * 11 | * If [T] is [android.os.Parcelable] then it will be saved in the Android [android.os.Bundle] 12 | * as a Parcelable. [java.io.Serializable] types are saved as Serializable. 13 | * Compose Destinations uses a default [DestinationsNavTypeSerializer] for both [android.os.Parcelable] 14 | * and [java.io.Serializable], so you don't need to create an explicit serializer, unless 15 | * you want to be able to deep link into screens that receive those types of arguments. 16 | * 17 | * For all other types that you turn as navigation argument types with this, they will be saved in the 18 | * [android.os.Bundle] as Strings. 19 | */ 20 | interface DestinationsNavTypeSerializer { 21 | fun toRouteString(value: T): String 22 | 23 | fun fromRouteString(routeStr: String): T 24 | } 25 | -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/navargs/NavTypeSerializer.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.navargs 2 | 3 | /** 4 | * You can annotate a [com.ramcosta.composedestinations.navargs.DestinationsNavTypeSerializer] 5 | * to signal the code generating task to consider the annotated class as a parser/serializer for a 6 | * given type that will be used as navigation argument. 7 | * 8 | * It allows you to make any type be considered a navigation argument type. 9 | * 10 | * Also, it controls how the complex type will be represented in the route which can be good if you 11 | * intend to navigate to the screen using the argument type through a deep link. 12 | * 13 | * @see DestinationsNavTypeSerializer 14 | */ 15 | @Target(AnnotationTarget.CLASS) 16 | @Retention(AnnotationRetention.SOURCE) 17 | annotation class NavTypeSerializer -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/navargs/primitives/CommonConstants.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.navargs.primitives 2 | 3 | const val ENCODED_NULL = "%02null%03" 4 | const val DECODED_NULL: String = "\u0002null\u0003" 5 | 6 | const val encodedComma = "%2C" 7 | 8 | @Suppress("RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS") 9 | fun > Class.valueOfIgnoreCase(enumValueName: String): E { 10 | return enumConstants.firstOrNull { constant -> 11 | constant.name.equals(enumValueName, ignoreCase = true) 12 | } ?: throw IllegalArgumentException( 13 | "Enum value $enumValueName not found for type ${this.name}." 14 | ) 15 | } -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/navargs/primitives/DestinationsEnumNavType.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.navargs.primitives 2 | 3 | import android.os.Bundle 4 | import androidx.lifecycle.SavedStateHandle 5 | import com.ramcosta.composedestinations.navargs.DestinationsNavType 6 | 7 | @Suppress("UNCHECKED_CAST") 8 | class DestinationsEnumNavType>( 9 | private val enumType: Class 10 | ) : DestinationsNavType() { 11 | 12 | override fun put(bundle: Bundle, key: String, value: E?) { 13 | bundle.putSerializable(key, value) 14 | } 15 | 16 | override fun get(bundle: Bundle, key: String): E? { 17 | @Suppress("DEPRECATION") 18 | return bundle.getSerializable(key) as E? 19 | } 20 | 21 | override fun parseValue(value: String): E? { 22 | if (value == DECODED_NULL) return null 23 | 24 | return enumType.valueOfIgnoreCase(value) 25 | } 26 | 27 | override fun serializeValue(value: E?): String { 28 | return value?.name ?: ENCODED_NULL 29 | } 30 | 31 | override fun get(savedStateHandle: SavedStateHandle, key: String): E? { 32 | return savedStateHandle[key] 33 | } 34 | 35 | override fun put(savedStateHandle: SavedStateHandle, key: String, value: E?) { 36 | savedStateHandle[key] = value 37 | } 38 | 39 | } 40 | 41 | -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/navargs/primitives/DestinationsFloatNavType.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.navargs.primitives 2 | 3 | import android.os.Bundle 4 | import androidx.lifecycle.SavedStateHandle 5 | import com.ramcosta.composedestinations.navargs.DestinationsNavType 6 | 7 | val floatNavType: DestinationsNavType = DestinationsFloatNavType 8 | 9 | internal object DestinationsFloatNavType : DestinationsNavType() { 10 | 11 | override fun put(bundle: Bundle, key: String, value: Float?) { 12 | when (val bundleValue = floatToBundleValue(value)) { 13 | is Byte -> bundle.putByte(key, bundleValue) 14 | is Float -> bundle.putFloat(key, bundleValue) 15 | else -> error("Unexpected type ${bundleValue.javaClass}") 16 | } 17 | } 18 | 19 | override fun get(bundle: Bundle, key: String): Float? { 20 | @Suppress("DEPRECATION") 21 | return bundleValueToFloat(bundle[key]) 22 | } 23 | 24 | override fun parseValue(value: String): Float? { 25 | return if (value == DECODED_NULL) { 26 | null 27 | } else { 28 | FloatType.parseValue(value) 29 | } 30 | } 31 | 32 | override fun serializeValue(value: Float?): String { 33 | return value?.toString() ?: ENCODED_NULL 34 | } 35 | 36 | override fun get(savedStateHandle: SavedStateHandle, key: String): Float? { 37 | return bundleValueToFloat(savedStateHandle[key]) 38 | } 39 | 40 | override fun put(savedStateHandle: SavedStateHandle, key: String, value: Float?) { 41 | savedStateHandle[key] = floatToBundleValue(value) 42 | } 43 | 44 | private fun floatToBundleValue(value: Float?) = value ?: 0.toByte() 45 | 46 | private fun bundleValueToFloat(valueForKey: Any?): Float? { 47 | return if (valueForKey is Float) { 48 | valueForKey 49 | } else { 50 | null 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/navargs/primitives/DestinationsIntNavType.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.navargs.primitives 2 | 3 | import android.os.Bundle 4 | import androidx.lifecycle.SavedStateHandle 5 | import com.ramcosta.composedestinations.navargs.DestinationsNavType 6 | 7 | val intNavType: DestinationsNavType = DestinationsIntNavType 8 | 9 | internal object DestinationsIntNavType : DestinationsNavType() { 10 | 11 | override fun put(bundle: Bundle, key: String, value: Int?) { 12 | when (val bundleValue = intToBundleValue(value)) { 13 | is Byte -> bundle.putByte(key, bundleValue) 14 | is Int -> bundle.putInt(key, bundleValue) 15 | else -> error("Unexpected type ${bundleValue.javaClass}") 16 | } 17 | } 18 | 19 | override fun get(bundle: Bundle, key: String): Int? { 20 | @Suppress("DEPRECATION") 21 | return bundleValueToInt(bundle[key]) 22 | } 23 | 24 | override fun parseValue(value: String): Int? { 25 | return if (value == DECODED_NULL) { 26 | null 27 | } else { 28 | IntType.parseValue(value) 29 | } 30 | } 31 | 32 | override fun serializeValue(value: Int?): String { 33 | return value?.toString() ?: ENCODED_NULL 34 | } 35 | 36 | override fun get(savedStateHandle: SavedStateHandle, key: String): Int? { 37 | return bundleValueToInt(savedStateHandle[key]) 38 | } 39 | 40 | override fun put(savedStateHandle: SavedStateHandle, key: String, value: Int?) { 41 | savedStateHandle[key] = intToBundleValue(value) 42 | } 43 | 44 | private fun intToBundleValue(value: Int?) = value ?: 0.toByte() 45 | 46 | private fun bundleValueToInt(valueForKey: Any?): Int? { 47 | return if (valueForKey is Int) { 48 | valueForKey 49 | } else { 50 | null 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/navargs/primitives/DestinationsLongNavType.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.navargs.primitives 2 | 3 | import android.os.Bundle 4 | import androidx.lifecycle.SavedStateHandle 5 | import com.ramcosta.composedestinations.navargs.DestinationsNavType 6 | 7 | val longNavType: DestinationsNavType = DestinationsLongNavType 8 | 9 | internal object DestinationsLongNavType : DestinationsNavType() { 10 | 11 | override fun put(bundle: Bundle, key: String, value: Long?) { 12 | when (val bundleValue = longToBundleValue(value)) { 13 | is Byte -> bundle.putByte(key, bundleValue) 14 | is Long -> bundle.putLong(key, bundleValue) 15 | else -> error("Unexpected type ${bundleValue.javaClass}") 16 | } 17 | } 18 | 19 | override fun get(bundle: Bundle, key: String): Long? { 20 | @Suppress("DEPRECATION") 21 | return bundleValueToLong(bundle[key]) 22 | } 23 | 24 | override fun parseValue(value: String): Long? { 25 | return if (value == DECODED_NULL) { 26 | null 27 | } else { 28 | LongType.parseValue(value) 29 | } 30 | } 31 | 32 | override fun serializeValue(value: Long?): String { 33 | return value?.toString() ?: ENCODED_NULL 34 | } 35 | 36 | override fun get(savedStateHandle: SavedStateHandle, key: String): Long? { 37 | return bundleValueToLong(savedStateHandle[key]) 38 | } 39 | 40 | override fun put(savedStateHandle: SavedStateHandle, key: String, value: Long?) { 41 | savedStateHandle[key] = longToBundleValue(value) 42 | } 43 | 44 | private fun longToBundleValue(value: Long?) = value ?: 0.toByte() 45 | 46 | private fun bundleValueToLong(valueForKey: Any?): Long? { 47 | return if (valueForKey is Long) { 48 | valueForKey 49 | } else { 50 | null 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/navargs/serializable/DefaultSerializableNavTypeSerializer.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.navargs.serializable 2 | 3 | import com.ramcosta.composedestinations.navargs.DestinationsNavTypeSerializer 4 | import com.ramcosta.composedestinations.navargs.utils.base64ToByteArray 5 | import com.ramcosta.composedestinations.navargs.utils.toBase64Str 6 | import java.io.* 7 | 8 | /** 9 | * Default [DestinationsNavTypeSerializer] for [Serializable]s which converts them to Base64 strings 10 | * and then parses them back. 11 | * 12 | * This gets used by the generated code if you don't provide an explicit 13 | * [DestinationsNavTypeSerializer] annotated with `@NavTypeSerializer` for the type being 14 | * passed as navigation argument. 15 | */ 16 | class DefaultSerializableNavTypeSerializer : DestinationsNavTypeSerializer { 17 | 18 | override fun toRouteString(value: Serializable): String { 19 | return value.toBase64() 20 | } 21 | 22 | override fun fromRouteString(routeStr: String): Serializable { 23 | return base64ToSerializable(routeStr) 24 | } 25 | 26 | private fun Serializable.toBase64(): String { 27 | return ByteArrayOutputStream().use { 28 | val out = ObjectOutputStream(it) 29 | out.writeObject(this) 30 | out.flush() 31 | it.toByteArray().toBase64Str() 32 | } 33 | } 34 | 35 | @Suppress("UNCHECKED_CAST") 36 | private fun base64ToSerializable(base64: String): T { 37 | val bytes = base64.base64ToByteArray() 38 | return ObjectInputStream(ByteArrayInputStream(bytes)).use { 39 | it.readObject() as T 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/navargs/utils/NavArgEncodingUtils.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.navargs.utils 2 | 3 | import android.annotation.SuppressLint 4 | import android.net.Uri 5 | import android.os.Build 6 | import android.util.Base64 7 | import java.net.URLEncoder 8 | import java.nio.charset.StandardCharsets 9 | 10 | fun encodeForRoute(arg: String): String { 11 | return if (!isRunningOnUnitTests) { 12 | Uri.encode(arg) 13 | } else { 14 | URLEncoder.encode(arg, StandardCharsets.UTF_8.toString()) 15 | } 16 | } 17 | 18 | @SuppressLint("NewApi") 19 | fun String.base64ToByteArray(): ByteArray { 20 | return if (shouldUseJavaUtil) { 21 | java.util.Base64.getUrlDecoder().decode(toByteArray(StandardCharsets.UTF_8)) 22 | } else { 23 | Base64.decode(toByteArray(StandardCharsets.UTF_8), Base64.URL_SAFE or Base64.NO_WRAP) 24 | } 25 | } 26 | 27 | @SuppressLint("NewApi") 28 | fun ByteArray.toBase64Str(): String { 29 | return if (shouldUseJavaUtil) { 30 | java.util.Base64.getUrlEncoder().encodeToString(this) 31 | } else { 32 | Base64.encodeToString(this, Base64.URL_SAFE or Base64.NO_WRAP) 33 | } 34 | } 35 | 36 | // Both encode/decode from java util and android util seem to 37 | // do exactly the same. 38 | // android.util one doesn't work on Unit tests and java.util doesn't work 39 | // on older devices. 40 | // Also, on unit tests SDK_INT is 0, but we're running on the developer's machine 41 | // So we can still use java.util in that case 42 | @SuppressLint("ObsoleteSdkInt") 43 | private val isRunningOnUnitTests = Build.VERSION.SDK_INT == 0 44 | 45 | private val shouldUseJavaUtil = 46 | Build.VERSION.SDK_INT >= Build.VERSION_CODES.O || isRunningOnUnitTests 47 | 48 | -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/navigation/DestinationsNavOptionsBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.navigation 2 | 3 | import androidx.navigation.NavOptionsBuilder 4 | import androidx.navigation.PopUpToBuilder 5 | import com.ramcosta.composedestinations.spec.RouteOrDirection 6 | 7 | /** 8 | * Like [NavOptionsBuilder] but has Compose Destinations friendly 9 | * version of its APIs. 10 | */ 11 | class DestinationsNavOptionsBuilder( 12 | private val jetpackBuilder: NavOptionsBuilder 13 | ) { 14 | 15 | /** 16 | * @see [NavOptionsBuilder.launchSingleTop] 17 | */ 18 | var launchSingleTop 19 | get() = jetpackBuilder.launchSingleTop 20 | set(value) { 21 | jetpackBuilder.launchSingleTop = value 22 | } 23 | 24 | /** 25 | * @see [NavOptionsBuilder.restoreState] 26 | */ 27 | var restoreState 28 | get() = jetpackBuilder.restoreState 29 | set(value) { 30 | jetpackBuilder.restoreState = value 31 | } 32 | 33 | /** 34 | * @see [NavOptionsBuilder.popUpToRoute] 35 | */ 36 | val popUpToRoute: String? 37 | get() = jetpackBuilder.popUpToRoute 38 | 39 | /** 40 | * Like [NavOptionsBuilder.popUpTo] but accepting a [com.ramcosta.composedestinations.spec.Route] 41 | * or [com.ramcosta.composedestinations.spec.Direction] to pop up to. 42 | * 43 | * @see [NavOptionsBuilder.popUpTo] 44 | */ 45 | fun popUpTo(route: RouteOrDirection, popUpToBuilder: PopUpToBuilder.() -> Unit = {}) { 46 | jetpackBuilder.popUpTo(route.route, popUpToBuilder) 47 | } 48 | } -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/navigation/EmptyDestinationsNavigator.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.navigation 2 | 3 | import androidx.navigation.NavBackStackEntry 4 | import androidx.navigation.NavOptions 5 | import androidx.navigation.Navigator 6 | import com.ramcosta.composedestinations.spec.Direction 7 | import com.ramcosta.composedestinations.spec.RouteOrDirection 8 | 9 | /** 10 | * Empty implementation of [DestinationsNavigator] 11 | * Useful for tests and Composable previews. 12 | */ 13 | object EmptyDestinationsNavigator : DestinationsNavigator { 14 | 15 | override fun navigate(direction: Direction, builder: DestinationsNavOptionsBuilder.() -> Unit) = Unit 16 | 17 | override fun navigate(direction: Direction, navOptions: NavOptions?, navigatorExtras: Navigator.Extras?) = Unit 18 | 19 | override fun navigateUp() = false 20 | 21 | override fun popBackStack() = false 22 | 23 | override fun popBackStack(route: RouteOrDirection, inclusive: Boolean, saveState: Boolean): Boolean = false 24 | 25 | override fun clearBackStack(route: RouteOrDirection): Boolean = false 26 | 27 | override fun getBackStackEntry(route: RouteOrDirection): NavBackStackEntry? = null 28 | 29 | } -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/result/EmptyResultBackNavigator.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.result 2 | 3 | /** 4 | * Empty implementation of [ResultBackNavigator] to 5 | * use in previews and possibly testing. 6 | */ 7 | class EmptyResultBackNavigator : ResultBackNavigator { 8 | 9 | override fun navigateBack(result: R) = Unit 10 | 11 | override fun setResult(result: R) = Unit 12 | 13 | override fun navigateBack() = Unit 14 | } -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/result/EmptyResultRecipient.kt: -------------------------------------------------------------------------------- 1 | @file:SuppressLint("ComposableNaming") 2 | package com.ramcosta.composedestinations.result 3 | 4 | import android.annotation.SuppressLint 5 | import androidx.compose.runtime.Composable 6 | import com.ramcosta.composedestinations.spec.DestinationSpec 7 | 8 | /** 9 | * Empty implementation of [ResultRecipient] to 10 | * use in previews and possibly testing. 11 | */ 12 | class EmptyResultRecipient : ResultRecipient { 13 | 14 | @Composable 15 | override fun onNavResult(listener: (NavResult) -> Unit) = Unit 16 | 17 | @Composable 18 | override fun onNavResult( 19 | deliverResultOn: OpenResultRecipient.DeliverResultOn, 20 | listener: (NavResult) -> Unit 21 | ) = Unit 22 | } -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/result/NavResult.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.result 2 | 3 | /** 4 | * Result sent from [ResultBackNavigator] when its destination is shown 5 | * and then navigates back to the [ResultRecipient] destination. 6 | * 7 | * If the destination related to the [ResultBackNavigator] doesn't put 8 | * any value using [ResultBackNavigator.setResult] or [ResultBackNavigator.navigateBack] 9 | * passing the result, then [Canceled] will be sent on [ResultRecipient.onNavResult], 10 | * otherwise the value set will be wrapped in [Value]. 11 | */ 12 | sealed interface NavResult { 13 | data object Canceled: NavResult 14 | data class Value(val value: R): NavResult 15 | } 16 | 17 | /** 18 | * Returns [NavResult.Value.value] if there is any, or [canceledValue] result 19 | * if there isn't (i.e, it is [NavResult.Canceled]) 20 | */ 21 | inline fun NavResult.getOr(canceledValue: () -> R): R = when (this) { 22 | is NavResult.Canceled -> canceledValue() 23 | is NavResult.Value -> value 24 | } -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/scope/DestinationScopeWithNoDependencies.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.scope 2 | 3 | import androidx.compose.runtime.Immutable 4 | import androidx.navigation.NavBackStackEntry 5 | import androidx.navigation.NavController 6 | import com.ramcosta.composedestinations.navigation.DestinationsNavigator 7 | import com.ramcosta.composedestinations.spec.DestinationSpec 8 | import com.ramcosta.composedestinations.spec.TypedDestinationSpec 9 | 10 | /** 11 | * Scope where a destination screen will be called in. 12 | * 13 | * This is used when we still have no dependencies 14 | */ 15 | @Immutable 16 | interface DestinationScopeWithNoDependencies { 17 | 18 | /** 19 | * [DestinationSpec] related to this scope 20 | */ 21 | val destination: TypedDestinationSpec 22 | 23 | /** 24 | * [NavBackStackEntry] of the current destination 25 | */ 26 | val navBackStackEntry: NavBackStackEntry 27 | 28 | /** 29 | * [NavController] related to the NavHost 30 | */ 31 | val navController: NavController 32 | 33 | /** 34 | * [DestinationsNavigator] useful to navigate from this destination 35 | */ 36 | val destinationsNavigator: DestinationsNavigator 37 | 38 | /** 39 | * Class holding the navigation arguments passed to this destination 40 | * or [Unit] if the destination has no arguments 41 | */ 42 | val navArgs: T 43 | } -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/spec/BaseRoute.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.spec 2 | 3 | /** 4 | * Base class for all [Route]s. 5 | * It ensures correct equality methods to make [ExternalRoute]s 6 | * identity transparent. 7 | */ 8 | abstract class BaseRoute { 9 | 10 | final override fun equals(other: Any?): Boolean { 11 | return if (other is ExternalRoute) { 12 | this === other.original 13 | } else { 14 | this === other 15 | } 16 | } 17 | 18 | final override fun hashCode(): Int { 19 | return super.hashCode() 20 | } 21 | } 22 | 23 | /** 24 | * We want to make external routes essentially identity transparent, 25 | * so we use the identity related methods from [original]. 26 | */ 27 | abstract class ExternalRoute { 28 | 29 | abstract val original: Route 30 | 31 | final override fun equals(other: Any?): Boolean { 32 | return original == other 33 | } 34 | 35 | final override fun hashCode(): Int { 36 | return original.hashCode() 37 | } 38 | final override fun toString(): String { 39 | return original.toString() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/spec/Direction.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.spec 2 | 3 | /** 4 | * Interface for all classes that contain a route 5 | * that is ready to be used in navigation. 6 | * 7 | * Generated [TypedDestinationSpec] and [TypedNavGraphSpec] are [Direction] 8 | * if they don't have any navigation argument. If they do, you can 9 | * call the invoke function passing the arguments to get a [Direction]. 10 | */ 11 | interface Direction: RouteOrDirection { 12 | 13 | /** 14 | * Route with nav arguments filled in. 15 | */ 16 | override val route: String 17 | } 18 | 19 | /** 20 | * Creates a [Direction] for given [route]. 21 | */ 22 | fun Direction(route: String): Direction { 23 | return DirectionImpl(route) 24 | } 25 | 26 | private data class DirectionImpl(override val route: String) : Direction -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/spec/DirectionDestinationSpec.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.spec 2 | 3 | import android.os.Bundle 4 | import androidx.lifecycle.SavedStateHandle 5 | 6 | /** 7 | * [TypedDestinationSpec] that does not contain any navigation arguments. 8 | * It itself is a [Direction] 9 | */ 10 | interface DirectionDestinationSpec: TypedDestinationSpec, Direction { 11 | 12 | override fun invoke(navArgs: Unit): Direction = this 13 | 14 | override fun argsFrom(bundle: Bundle?) = Unit 15 | 16 | override fun argsFrom(savedStateHandle: SavedStateHandle) = Unit 17 | } 18 | 19 | /** 20 | * [ActivityDestinationSpec] that does not contain any navigation arguments. 21 | * It itself is a [Direction] 22 | */ 23 | interface DirectionActivityDestinationSpec: ActivityDestinationSpec, DirectionDestinationSpec { 24 | 25 | override fun argsFrom(savedStateHandle: SavedStateHandle) { 26 | super.argsFrom(savedStateHandle) 27 | } 28 | 29 | override fun argsFrom(bundle: Bundle?) = Unit 30 | } -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/spec/DirectionNavGraphSpec.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.spec 2 | 3 | import android.os.Bundle 4 | import androidx.lifecycle.SavedStateHandle 5 | 6 | /** 7 | * [TypedNavGraphSpec] that does not contain any navigation arguments. 8 | * It itself is a [Direction] 9 | */ 10 | interface DirectionNavGraphSpec: TypedNavGraphSpec, Direction { 11 | 12 | override val baseRoute: String get() = route 13 | 14 | override fun invoke(navArgs: Unit): Direction = this 15 | 16 | operator fun invoke(): Direction = this 17 | 18 | override fun argsFrom(bundle: Bundle?) = Unit 19 | 20 | override fun argsFrom(savedStateHandle: SavedStateHandle) = Unit 21 | } 22 | -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/spec/ModuleDestinationsContainer.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.spec 2 | 3 | /** 4 | * Used by generated code when there's a module that only outputs destinations 5 | * and no nav graphs. 6 | * Generated classes can be used to import all destinations with a single 7 | * [com.ramcosta.composedestinations.annotation.ExternalModuleDestinations] annotation. 8 | */ 9 | interface ModuleDestinationsContainer { 10 | val destinations: List 11 | } 12 | -------------------------------------------------------------------------------- /compose-destinations/src/main/java/com/ramcosta/composedestinations/spec/RouteOrDirection.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.composedestinations.spec 2 | 3 | /** 4 | * [Route] or [Direction] 5 | */ 6 | sealed interface RouteOrDirection { 7 | val route: String 8 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | 21 | android.nonTransitiveRClass=false 22 | android.nonFinalResIds=false 23 | 24 | SONATYPE_AUTOMATIC_RELEASE=true 25 | 26 | ksp.useKSP2=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raamcosta/compose-destinations/34a138134b053bc588554e46bcdf360f8210f3d6/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Aug 23 16:48:14 WEST 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /playground/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /playground/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.kts. 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 -------------------------------------------------------------------------------- /playground/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/OtherActivity.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.compose.foundation.background 6 | import androidx.compose.foundation.layout.Column 7 | import androidx.compose.material.Text 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.graphics.Color 10 | import androidx.compose.ui.platform.ComposeView 11 | import com.ramcosta.composedestinations.annotation.ActivityDestination 12 | import com.ramcosta.samples.playground.commons.SettingsGraph 13 | import com.ramcosta.samples.playground.ui.screens.destinations.OtherActivityDestination 14 | 15 | data class OtherActivityNavArgs( 16 | val otherThing: String, 17 | val color: Color 18 | ) 19 | 20 | @ActivityDestination( 21 | navArgs = OtherActivityNavArgs::class, 22 | label = "other activity" 23 | ) 24 | annotation class OtherActivityDestinantion 25 | 26 | @OtherActivityDestinantion 27 | class OtherActivity: ComponentActivity() { 28 | 29 | override fun onCreate(savedInstanceState: Bundle?) { 30 | super.onCreate(savedInstanceState) 31 | 32 | val args = OtherActivityDestination.argsFrom(intent) 33 | println("OtherActivity args = $args") 34 | 35 | setContentView(ComposeView(this).apply { 36 | setContent { 37 | Column( 38 | modifier = Modifier.background(args.color) 39 | ) { 40 | Text("OTHER ACTIVITY") 41 | Text("ARGS: \n$args") 42 | } 43 | } 44 | }) 45 | } 46 | } -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/commons/DrawerController.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground.commons 2 | 3 | import androidx.compose.material.DrawerState 4 | 5 | interface DrawerController { 6 | suspend fun open() 7 | suspend fun close() 8 | } 9 | 10 | class DrawerControllerImpl( 11 | private val drawerState: DrawerState 12 | ): DrawerController{ 13 | 14 | override suspend fun open() { 15 | drawerState.open() 16 | } 17 | 18 | override suspend fun close() { 19 | drawerState.close() 20 | } 21 | } -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/di/DependencyContainer.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground.di 2 | 3 | import com.ramcosta.samples.playground.ui.screens.profile.GetProfileLikeCountUseCase 4 | 5 | class DependencyContainer { 6 | val getProfileLikeCount get() = GetProfileLikeCountUseCase() 7 | } 8 | -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/ui/screens/GoToProfileConfirmation.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground.ui.screens 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.foundation.layout.Spacer 6 | import androidx.compose.foundation.layout.fillMaxWidth 7 | import androidx.compose.foundation.layout.height 8 | import androidx.compose.foundation.layout.padding 9 | import androidx.compose.material.Button 10 | import androidx.compose.material.MaterialTheme 11 | import androidx.compose.material.Text 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.ui.Alignment 14 | import androidx.compose.ui.Modifier 15 | import androidx.compose.ui.unit.dp 16 | import com.ramcosta.composedestinations.annotation.Destination 17 | import com.ramcosta.composedestinations.annotation.RootGraph 18 | import com.ramcosta.composedestinations.result.ResultBackNavigator 19 | import com.ramcosta.samples.playground.ui.screens.styles.AppDialog 20 | 21 | @Destination(style = AppDialog::class) 22 | @Composable 23 | fun GoToProfileConfirmation( 24 | resultNavigator: ResultBackNavigator 25 | ) { 26 | Column( 27 | modifier = Modifier 28 | .fillMaxWidth() 29 | .background(MaterialTheme.colors.background) 30 | .padding(16.dp), 31 | horizontalAlignment = Alignment.CenterHorizontally 32 | ) { 33 | Text(text = "Are you sure you want to go to Profile Screen?") 34 | Spacer(modifier = Modifier.height(12.dp)) 35 | Button( 36 | onClick = { 37 | resultNavigator.navigateBack(result = true) 38 | } 39 | ) { 40 | Text(text = "Yes, why is this even a dialog?!") 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/ui/screens/greeting/GreetingUiEvents.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground.ui.screens.greeting 2 | 3 | interface GreetingUiEvents { 4 | fun onNewGreetingClicked() 5 | } -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/ui/screens/greeting/GreetingUiState.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground.ui.screens.greeting 2 | 3 | interface GreetingUiState { 4 | 5 | val greeting: String 6 | } -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/ui/screens/greeting/GreetingViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground.ui.screens.greeting 2 | 3 | import android.util.Log 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.mutableStateOf 6 | import androidx.compose.runtime.setValue 7 | import androidx.lifecycle.ViewModel 8 | 9 | class GreetingViewModel: ViewModel(), GreetingUiEvents, GreetingUiState { 10 | 11 | private val greetings = arrayOf( 12 | "Hello", 13 | "What's up", 14 | "Howdy", 15 | "Hi there!", 16 | "Hi folks!", 17 | "Long time, no see", 18 | "Hey", 19 | "How are you" 20 | ) 21 | 22 | private var currentGreetingIndex = 0 23 | 24 | override var greeting: String by mutableStateOf(greetings[currentGreetingIndex]) 25 | 26 | init { 27 | Log.d("GreetingViewModel", "initing---$this") 28 | } 29 | 30 | override fun onNewGreetingClicked() { 31 | Log.d("GreetingViewModel", "onCounterButtonClicked") 32 | if (currentGreetingIndex == greetings.lastIndex) { 33 | currentGreetingIndex = 0 34 | } else { 35 | currentGreetingIndex++ 36 | } 37 | 38 | greeting = greetings[currentGreetingIndex] 39 | } 40 | 41 | override fun onCleared() { 42 | Log.d("GreetingViewModel", "onCleared---$this") 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/ui/screens/profile/GetProfileLikeCountUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground.ui.screens.profile 2 | 3 | import android.util.Log 4 | import kotlinx.coroutines.delay 5 | 6 | class GetProfileLikeCountUseCase { 7 | 8 | suspend operator fun invoke(profileId: Long): Int { 9 | Log.d("GetProfileLikeCount", "Getting like count for profile with id $profileId") 10 | delay(500) //simulate a network or db call 11 | return 10 12 | } 13 | } -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/ui/screens/profile/ProfileUiEvents.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground.ui.screens.profile 2 | 3 | interface ProfileUiEvents { 4 | fun onLikeButtonClick() 5 | } -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/ui/screens/profile/ProfileUiState.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground.ui.screens.profile 2 | 3 | interface ProfileUiState { 4 | val id: Long 5 | val groupName: String 6 | val likeCount: Int 7 | } -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/ui/screens/profile/ProfileViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground.ui.screens.profile 2 | 3 | import android.util.Log 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.mutableStateOf 6 | import androidx.compose.runtime.setValue 7 | import androidx.lifecycle.SavedStateHandle 8 | import androidx.lifecycle.ViewModel 9 | import androidx.lifecycle.viewModelScope 10 | import com.ramcosta.samples.playground.ui.screens.navArgs 11 | import kotlinx.coroutines.launch 12 | 13 | class ProfileViewModel( 14 | getProfileLikeCount: GetProfileLikeCountUseCase, 15 | handle: SavedStateHandle 16 | ) : ViewModel(), ProfileUiEvents, ProfileUiState { 17 | 18 | private val navArgs = handle.navArgs() 19 | 20 | init { 21 | Log.d("ProfileViewModel", "navArgs= $navArgs") 22 | } 23 | 24 | override val groupName: String = navArgs.groupName?.ifEmpty { "\"\"" } ?: "null" 25 | 26 | override val id: Long = navArgs.id 27 | 28 | override var likeCount: Int by mutableStateOf(0) 29 | 30 | init { 31 | viewModelScope.launch { 32 | likeCount = getProfileLikeCount(id) 33 | } 34 | } 35 | 36 | override fun onLikeButtonClick() { 37 | likeCount++ 38 | } 39 | } -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/ui/screens/settings/SettingsViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground.ui.screens.settings 2 | 3 | import androidx.compose.runtime.getValue 4 | import androidx.compose.runtime.mutableStateOf 5 | import androidx.lifecycle.ViewModel 6 | 7 | class SettingsViewModel: ViewModel() { 8 | 9 | private var _isToggleOn = mutableStateOf(false) 10 | val isToggleOn: Boolean by _isToggleOn 11 | 12 | fun toggle() { 13 | _isToggleOn.value = !isToggleOn 14 | } 15 | } -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/ui/screens/styles/AppDialog.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground.ui.screens.styles 2 | 3 | import androidx.compose.ui.window.DialogProperties 4 | import com.ramcosta.composedestinations.spec.DestinationStyle 5 | 6 | object AppDialog : DestinationStyle.Dialog() { 7 | override val properties: DialogProperties 8 | get() = DialogProperties( 9 | dismissOnBackPress = false 10 | ) 11 | } -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/ui/screens/wrappers/DrawerOpeningWrapper.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground.ui.screens.wrappers 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.LaunchedEffect 5 | import com.ramcosta.composedestinations.navigation.require 6 | import com.ramcosta.composedestinations.scope.DestinationScope 7 | import com.ramcosta.composedestinations.wrapper.DestinationWrapper 8 | import com.ramcosta.samples.playground.commons.DrawerController 9 | 10 | object DrawerOpeningWrapper : DestinationWrapper { 11 | 12 | @Composable 13 | override fun DestinationScope.Wrap( 14 | screenContent: @Composable () -> Unit 15 | ) { 16 | val dependencies = buildDependencies() 17 | val drawerController = dependencies.require() 18 | 19 | LaunchedEffect(Unit) { 20 | drawerController.open() 21 | drawerController.close() 22 | drawerController.open() 23 | drawerController.close() 24 | } 25 | 26 | screenContent() 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple200 = Color(0xFFBB86FC) 6 | val Purple500 = Color(0xFF6200EE) 7 | val Purple700 = Color(0xFF3700B3) 8 | val Teal200 = Color(0xFF03DAC5) -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/ui/theme/Shape.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground.ui.theme 2 | 3 | import androidx.compose.foundation.shape.RoundedCornerShape 4 | import androidx.compose.material.Shapes 5 | import androidx.compose.ui.unit.dp 6 | 7 | val Shapes = Shapes( 8 | small = RoundedCornerShape(4.dp), 9 | medium = RoundedCornerShape(4.dp), 10 | large = RoundedCornerShape(0.dp) 11 | ) -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground.ui.theme 2 | 3 | import androidx.compose.foundation.isSystemInDarkTheme 4 | import androidx.compose.material.MaterialTheme 5 | import androidx.compose.material.darkColors 6 | import androidx.compose.material.lightColors 7 | import androidx.compose.runtime.Composable 8 | 9 | private val DarkColorPalette = darkColors( 10 | primary = Purple200, 11 | primaryVariant = Purple700, 12 | secondary = Teal200 13 | ) 14 | 15 | private val LightColorPalette = lightColors( 16 | primary = Purple500, 17 | primaryVariant = Purple700, 18 | secondary = Teal200 19 | 20 | /* Other default colors to override 21 | background = Color.White, 22 | surface = Color.White, 23 | onPrimary = Color.White, 24 | onSecondary = Color.Black, 25 | onBackground = Color.Black, 26 | onSurface = Color.Black, 27 | */ 28 | ) 29 | 30 | @Composable 31 | fun PlaygroundTheme( 32 | darkTheme: Boolean = isSystemInDarkTheme(), 33 | content: @Composable () -> Unit 34 | ) { 35 | val colors = if (darkTheme) { 36 | DarkColorPalette 37 | } else { 38 | LightColorPalette 39 | } 40 | 41 | MaterialTheme( 42 | colors = colors, 43 | typography = Typography, 44 | shapes = Shapes, 45 | content = content 46 | ) 47 | } -------------------------------------------------------------------------------- /playground/app/src/main/java/com/ramcosta/samples/playground/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.ramcosta.samples.playground.ui.theme 2 | 3 | import androidx.compose.material.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | body1 = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp 15 | ) 16 | /* Other default text styles to override 17 | button = TextStyle( 18 | fontFamily = FontFamily.Default, 19 | fontWeight = FontWeight.W500, 20 | fontSize = 14.sp 21 | ), 22 | caption = TextStyle( 23 | fontFamily = FontFamily.Default, 24 | fontWeight = FontWeight.Normal, 25 | fontSize = 12.sp 26 | ) 27 | */ 28 | ) -------------------------------------------------------------------------------- /playground/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /playground/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /playground/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /playground/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raamcosta/compose-destinations/34a138134b053bc588554e46bcdf360f8210f3d6/playground/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /playground/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raamcosta/compose-destinations/34a138134b053bc588554e46bcdf360f8210f3d6/playground/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /playground/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raamcosta/compose-destinations/34a138134b053bc588554e46bcdf360f8210f3d6/playground/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /playground/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raamcosta/compose-destinations/34a138134b053bc588554e46bcdf360f8210f3d6/playground/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /playground/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raamcosta/compose-destinations/34a138134b053bc588554e46bcdf360f8210f3d6/playground/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /playground/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raamcosta/compose-destinations/34a138134b053bc588554e46bcdf360f8210f3d6/playground/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /playground/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raamcosta/compose-destinations/34a138134b053bc588554e46bcdf360f8210f3d6/playground/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /playground/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raamcosta/compose-destinations/34a138134b053bc588554e46bcdf360f8210f3d6/playground/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /playground/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raamcosta/compose-destinations/34a138134b053bc588554e46bcdf360f8210f3d6/playground/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /playground/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raamcosta/compose-destinations/34a138134b053bc588554e46bcdf360f8210f3d6/playground/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /playground/app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /playground/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /playground/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | PlaygroundSample 3 | Greeting Screen 4 | Profile Screen 5 | Settings Screen 6 | Feed Screen 7 | Theme Settings 8 | New Greeting 9 | Go to Profile 10 | Open drawer 11 | Go to Test Screen 12 | Show deep link test notification 13 | -------------------------------------------------------------------------------- /playground/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 21 | 22 | 7 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | 9 | rootProject.name = "ComposeDestinations" 10 | 11 | include(":compose-destinations") 12 | include(":compose-destinations-ksp") 13 | include(":compose-destinations-codegen") 14 | include(":compose-destinations-bottom-sheet") 15 | include(":compose-destinations-wear") 16 | include(":sample") 17 | include(":sample-wear") 18 | include(":playground:app") 19 | include(":playground:core") 20 | include(":playground:featurex") 21 | include(":playground:featurey") 22 | include(":playground:featurey:sub") 23 | include(":playground:featurez") 24 | --------------------------------------------------------------------------------