├── .config
└── detekt.yml
├── .github
├── CODEOWNERS
└── workflows
│ └── compile-and-check.yml
├── .gitignore
├── .idea
├── .gitignore
├── .name
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── compiler.xml
├── gradle.xml
├── misc.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── xmartlabs
│ │ └── typednavigation
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── xmartlabs
│ │ │ └── typednavigation
│ │ │ ├── App.kt
│ │ │ ├── MainActivity.kt
│ │ │ └── ui
│ │ │ ├── hiltExapmle
│ │ │ ├── HiltExample.kt
│ │ │ └── HiltExampleViewModel.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
│ └── java
│ └── com
│ └── xmartlabs
│ └── typednavigation
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── jitpack.yml
├── settings.gradle
└── typednavigation
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
├── androidTest
└── java
│ └── com
│ └── xmartlabs
│ └── typednavigation
│ └── ExampleInstrumentedTest.kt
├── main
├── AndroidManifest.xml
└── java
│ └── com
│ └── xmartlabs
│ └── typednavigation
│ ├── NavGraphBuilderExtensions.kt
│ ├── NullableTypes.kt
│ ├── TypedNavigation.kt
│ ├── TypedNavigationInterface.kt
│ └── TypedNavigationInterfaceExtensions.kt
└── test
└── java
└── com
└── xmartlabs
└── typednavigation
├── TestRouter.kt
└── UnitTest.kt
/.config/detekt.yml:
--------------------------------------------------------------------------------
1 | build:
2 | maxIssues: 0
3 | excludeCorrectable: false
4 | weights:
5 | # complexity: 2
6 | # LongParameterList: 1
7 | # style: 1
8 | # comments: 1
9 |
10 | config:
11 | validation: true
12 | warningsAsErrors: false
13 | # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]'
14 | excludes: ''
15 |
16 | processors:
17 | active: true
18 | exclude:
19 | - 'DetektProgressListener'
20 | # - 'KtFileCountProcessor'
21 | # - 'PackageCountProcessor'
22 | # - 'ClassCountProcessor'
23 | # - 'FunctionCountProcessor'
24 | # - 'PropertyCountProcessor'
25 | # - 'ProjectComplexityProcessor'
26 | # - 'ProjectCognitiveComplexityProcessor'
27 | # - 'ProjectLLOCProcessor'
28 | # - 'ProjectCLOCProcessor'
29 | # - 'ProjectLOCProcessor'
30 | # - 'ProjectSLOCProcessor'
31 | # - 'LicenseHeaderLoaderExtension'
32 |
33 | console-reports:
34 | active: true
35 | exclude:
36 | - 'ProjectStatisticsReport'
37 | - 'ComplexityReport'
38 | - 'NotificationReport'
39 | # - 'FindingsReport'
40 | - 'FileBasedFindingsReport'
41 | - 'LiteFindingsReport'
42 |
43 | output-reports:
44 | active: true
45 | exclude:
46 | # - 'TxtOutputReport'
47 | # - 'XmlOutputReport'
48 | # - 'HtmlOutputReport'
49 |
50 | comments:
51 | active: true
52 | AbsentOrWrongFileLicense:
53 | active: false
54 | licenseTemplateFile: 'license.template'
55 | licenseTemplateIsRegex: false
56 | CommentOverPrivateFunction:
57 | active: false
58 | CommentOverPrivateProperty:
59 | active: false
60 | DeprecatedBlockTag:
61 | active: false
62 | EndOfSentenceFormat:
63 | active: false
64 | endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)'
65 | UndocumentedPublicClass:
66 | active: false
67 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
68 | searchInNestedClass: true
69 | searchInInnerClass: true
70 | searchInInnerObject: true
71 | searchInInnerInterface: true
72 | UndocumentedPublicFunction:
73 | active: false
74 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
75 | UndocumentedPublicProperty:
76 | active: false
77 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
78 |
79 | complexity:
80 | active: true
81 | ComplexCondition:
82 | active: true
83 | threshold: 4
84 | ComplexInterface:
85 | active: false
86 | threshold: 10
87 | includeStaticDeclarations: false
88 | includePrivateDeclarations: false
89 | ComplexMethod:
90 | active: true
91 | threshold: 15
92 | ignoreSingleWhenExpression: false
93 | ignoreSimpleWhenEntries: false
94 | ignoreNestingFunctions: false
95 | nestingFunctions:
96 | - 'also'
97 | - 'apply'
98 | - 'forEach'
99 | - 'isNotNull'
100 | - 'ifNull'
101 | - 'let'
102 | - 'run'
103 | - 'use'
104 | - 'with'
105 | LabeledExpression:
106 | active: false
107 | ignoredLabels: []
108 | LargeClass:
109 | active: true
110 | threshold: 600
111 | LongMethod:
112 | active: true
113 | threshold: 60
114 | LongParameterList:
115 | active: true
116 | functionThreshold: 6
117 | constructorThreshold: 7
118 | ignoreDefaultParameters: false
119 | ignoreDataClasses: true
120 | # ignoreAnnotatedParameter: []
121 | MethodOverloading:
122 | active: false
123 | threshold: 6
124 | NamedArguments:
125 | active: false
126 | threshold: 3
127 | NestedBlockDepth:
128 | active: true
129 | threshold: 4
130 | ReplaceSafeCallChainWithRun:
131 | active: false
132 | StringLiteralDuplication:
133 | active: false
134 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
135 | threshold: 3
136 | ignoreAnnotation: true
137 | excludeStringsWithLessThan5Characters: true
138 | ignoreStringsRegex: '$^'
139 | TooManyFunctions:
140 | active: true
141 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
142 | thresholdInFiles: 100
143 | thresholdInClasses: 11
144 | thresholdInInterfaces: 11
145 | thresholdInObjects: 11
146 | thresholdInEnums: 11
147 | ignoreDeprecated: false
148 | ignorePrivate: false
149 | ignoreOverridden: false
150 |
151 | coroutines:
152 | active: true
153 | GlobalCoroutineUsage:
154 | active: false
155 | RedundantSuspendModifier:
156 | active: false
157 | SleepInsteadOfDelay:
158 | active: false
159 | SuspendFunWithFlowReturnType:
160 | active: false
161 |
162 | empty-blocks:
163 | active: true
164 | EmptyCatchBlock:
165 | active: true
166 | allowedExceptionNameRegex: '_|(ignore|expected).*'
167 | EmptyClassBlock:
168 | active: true
169 | EmptyDefaultConstructor:
170 | active: true
171 | EmptyDoWhileBlock:
172 | active: true
173 | EmptyElseBlock:
174 | active: true
175 | EmptyFinallyBlock:
176 | active: true
177 | EmptyForBlock:
178 | active: true
179 | EmptyFunctionBlock:
180 | active: true
181 | ignoreOverridden: false
182 | EmptyIfBlock:
183 | active: true
184 | EmptyInitBlock:
185 | active: true
186 | EmptyKtFile:
187 | active: true
188 | EmptySecondaryConstructor:
189 | active: true
190 | EmptyTryBlock:
191 | active: true
192 | EmptyWhenBlock:
193 | active: true
194 | EmptyWhileBlock:
195 | active: true
196 |
197 | exceptions:
198 | active: true
199 | ExceptionRaisedInUnexpectedLocation:
200 | active: true
201 | methodNames:
202 | - 'equals'
203 | - 'finalize'
204 | - 'hashCode'
205 | - 'toString'
206 | InstanceOfCheckForException:
207 | active: false
208 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
209 | NotImplementedDeclaration:
210 | active: false
211 | ObjectExtendsThrowable:
212 | active: false
213 | PrintStackTrace:
214 | active: true
215 | RethrowCaughtException:
216 | active: true
217 | ReturnFromFinally:
218 | active: true
219 | ignoreLabeled: false
220 | SwallowedException:
221 | active: true
222 | ignoredExceptionTypes:
223 | - 'InterruptedException'
224 | - 'MalformedURLException'
225 | - 'NumberFormatException'
226 | - 'ParseException'
227 | allowedExceptionNameRegex: '_|(ignore|expected).*'
228 | ThrowingExceptionFromFinally:
229 | active: true
230 | ThrowingExceptionInMain:
231 | active: false
232 | ThrowingExceptionsWithoutMessageOrCause:
233 | active: true
234 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
235 | exceptions:
236 | - 'ArrayIndexOutOfBoundsException'
237 | - 'Exception'
238 | - 'IllegalArgumentException'
239 | - 'IllegalMonitorStateException'
240 | - 'IllegalStateException'
241 | - 'IndexOutOfBoundsException'
242 | - 'NullPointerException'
243 | - 'RuntimeException'
244 | - 'Throwable'
245 | ThrowingNewInstanceOfSameException:
246 | active: true
247 | TooGenericExceptionCaught:
248 | active: true
249 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
250 | exceptionNames:
251 | - 'ArrayIndexOutOfBoundsException'
252 | - 'Error'
253 | - 'Exception'
254 | - 'IllegalMonitorStateException'
255 | - 'IndexOutOfBoundsException'
256 | - 'NullPointerException'
257 | - 'RuntimeException'
258 | - 'Throwable'
259 | allowedExceptionNameRegex: '_|(ignore|expected).*'
260 | TooGenericExceptionThrown:
261 | active: true
262 | exceptionNames:
263 | - 'Error'
264 | - 'Exception'
265 | - 'RuntimeException'
266 | - 'Throwable'
267 |
268 | formatting:
269 | active: true
270 | android: false
271 | autoCorrect: true
272 | AnnotationOnSeparateLine:
273 | active: false
274 | autoCorrect: true
275 | AnnotationSpacing:
276 | active: false
277 | autoCorrect: true
278 | ArgumentListWrapping:
279 | active: false
280 | autoCorrect: true
281 | indentSize: 4
282 | maxLineLength: 120
283 | ChainWrapping:
284 | active: true
285 | autoCorrect: true
286 | CommentSpacing:
287 | active: true
288 | autoCorrect: true
289 | EnumEntryNameCase:
290 | active: false
291 | autoCorrect: true
292 | Filename:
293 | active: true
294 | FinalNewline:
295 | active: true
296 | autoCorrect: true
297 | insertFinalNewLine: true
298 | ImportOrdering:
299 | active: false
300 | autoCorrect: true
301 | layout: '*,java.**,javax.**,kotlin.**,^'
302 | Indentation:
303 | active: false
304 | autoCorrect: true
305 | indentSize: 4
306 | continuationIndentSize: 4
307 | MaximumLineLength:
308 | active: true
309 | maxLineLength: 120
310 | ignoreBackTickedIdentifier: false
311 | ModifierOrdering:
312 | active: true
313 | autoCorrect: true
314 | MultiLineIfElse:
315 | active: true
316 | autoCorrect: true
317 | NoBlankLineBeforeRbrace:
318 | active: true
319 | autoCorrect: true
320 | NoConsecutiveBlankLines:
321 | active: true
322 | autoCorrect: true
323 | NoEmptyClassBody:
324 | active: true
325 | autoCorrect: true
326 | NoEmptyFirstLineInMethodBlock:
327 | active: false
328 | autoCorrect: true
329 | NoLineBreakAfterElse:
330 | active: true
331 | autoCorrect: true
332 | NoLineBreakBeforeAssignment:
333 | active: true
334 | autoCorrect: true
335 | NoMultipleSpaces:
336 | active: true
337 | autoCorrect: true
338 | NoSemicolons:
339 | active: true
340 | autoCorrect: true
341 | NoTrailingSpaces:
342 | active: true
343 | autoCorrect: true
344 | NoUnitReturn:
345 | active: true
346 | autoCorrect: true
347 | NoUnusedImports:
348 | active: true
349 | autoCorrect: true
350 | NoWildcardImports:
351 | active: true
352 | PackageName:
353 | active: true
354 | autoCorrect: true
355 | ParameterListWrapping:
356 | active: true
357 | autoCorrect: true
358 | indentSize: 4
359 | maxLineLength: 120
360 | SpacingAroundAngleBrackets:
361 | active: false
362 | autoCorrect: true
363 | SpacingAroundColon:
364 | active: true
365 | autoCorrect: true
366 | SpacingAroundComma:
367 | active: true
368 | autoCorrect: true
369 | SpacingAroundCurly:
370 | active: true
371 | autoCorrect: true
372 | SpacingAroundDot:
373 | active: true
374 | autoCorrect: true
375 | SpacingAroundDoubleColon:
376 | active: false
377 | autoCorrect: true
378 | SpacingAroundKeyword:
379 | active: true
380 | autoCorrect: true
381 | SpacingAroundOperators:
382 | active: true
383 | autoCorrect: true
384 | SpacingAroundParens:
385 | active: true
386 | autoCorrect: true
387 | SpacingAroundRangeOperator:
388 | active: true
389 | autoCorrect: true
390 | SpacingAroundUnaryOperator:
391 | active: false
392 | autoCorrect: true
393 | SpacingBetweenDeclarationsWithAnnotations:
394 | active: false
395 | autoCorrect: true
396 | SpacingBetweenDeclarationsWithComments:
397 | active: false
398 | autoCorrect: true
399 | StringTemplate:
400 | active: true
401 | autoCorrect: true
402 |
403 | naming:
404 | active: true
405 | BooleanPropertyNaming:
406 | active: false
407 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
408 | allowedPattern: '^(is|has|are)'
409 | ClassNaming:
410 | active: true
411 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
412 | classPattern: '[A-Z][a-zA-Z0-9]*'
413 | ConstructorParameterNaming:
414 | active: true
415 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
416 | parameterPattern: '[a-z][A-Za-z0-9]*'
417 | privateParameterPattern: '[a-z][A-Za-z0-9]*'
418 | excludeClassPattern: '$^'
419 | ignoreOverridden: true
420 | EnumNaming:
421 | active: true
422 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
423 | enumEntryPattern: '[A-Z][_a-zA-Z0-9]*'
424 | ForbiddenClassName:
425 | active: false
426 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
427 | forbiddenName: []
428 | FunctionMaxLength:
429 | active: false
430 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
431 | maximumFunctionNameLength: 30
432 | FunctionMinLength:
433 | active: false
434 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
435 | minimumFunctionNameLength: 3
436 | FunctionNaming:
437 | active: true
438 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
439 | functionPattern: '([a-z][a-zA-Z0-9]*)|(`.*`)'
440 | excludeClassPattern: '$^'
441 | ignoreOverridden: true
442 | FunctionParameterNaming:
443 | active: true
444 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
445 | parameterPattern: '[a-z][A-Za-z0-9]*'
446 | excludeClassPattern: '$^'
447 | ignoreOverridden: true
448 | InvalidPackageDeclaration:
449 | active: false
450 | excludes: ['**/*.kts']
451 | rootPackage: ''
452 | MatchingDeclarationName:
453 | active: true
454 | mustBeFirst: true
455 | MemberNameEqualsClassName:
456 | active: true
457 | ignoreOverridden: true
458 | NoNameShadowing:
459 | active: false
460 | NonBooleanPropertyPrefixedWithIs:
461 | active: false
462 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
463 | ObjectPropertyNaming:
464 | active: true
465 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
466 | constantPattern: '[A-Za-z][_A-Za-z0-9]*'
467 | propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
468 | privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
469 | PackageNaming:
470 | active: true
471 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
472 | packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*'
473 | TopLevelPropertyNaming:
474 | active: true
475 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
476 | constantPattern: '[A-Z][_A-Z0-9]*'
477 | propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
478 | privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
479 | VariableMaxLength:
480 | active: false
481 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
482 | maximumVariableNameLength: 64
483 | VariableMinLength:
484 | active: false
485 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
486 | minimumVariableNameLength: 1
487 | VariableNaming:
488 | active: true
489 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
490 | variablePattern: '[a-z][A-Za-z0-9]*'
491 | privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
492 | excludeClassPattern: '$^'
493 | ignoreOverridden: true
494 |
495 | performance:
496 | active: true
497 | ArrayPrimitive:
498 | active: true
499 | ForEachOnRange:
500 | active: true
501 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
502 | SpreadOperator:
503 | active: true
504 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
505 | UnnecessaryTemporaryInstantiation:
506 | active: true
507 |
508 | potential-bugs:
509 | active: true
510 | AvoidReferentialEquality:
511 | active: false
512 | forbiddenTypePatterns:
513 | - 'kotlin.String'
514 | CastToNullableType:
515 | active: false
516 | Deprecation:
517 | active: false
518 | DontDowncastCollectionTypes:
519 | active: false
520 | DoubleMutabilityForCollection:
521 | active: false
522 | DuplicateCaseInWhenExpression:
523 | active: true
524 | EqualsAlwaysReturnsTrueOrFalse:
525 | active: true
526 | EqualsWithHashCodeExist:
527 | active: true
528 | ExitOutsideMain:
529 | active: false
530 | ExplicitGarbageCollectionCall:
531 | active: true
532 | HasPlatformType:
533 | active: false
534 | IgnoredReturnValue:
535 | active: false
536 | restrictToAnnotatedMethods: true
537 | returnValueAnnotations:
538 | - '*.CheckResult'
539 | - '*.CheckReturnValue'
540 | ignoreReturnValueAnnotations:
541 | - '*.CanIgnoreReturnValue'
542 | ImplicitDefaultLocale:
543 | active: true
544 | ImplicitUnitReturnType:
545 | active: false
546 | allowExplicitReturnType: true
547 | InvalidRange:
548 | active: true
549 | IteratorHasNextCallsNextMethod:
550 | active: true
551 | IteratorNotThrowingNoSuchElementException:
552 | active: true
553 | LateinitUsage:
554 | active: false
555 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
556 | ignoreOnClassesPattern: ''
557 | MapGetWithNotNullAssertionOperator:
558 | active: false
559 | MissingWhenCase:
560 | active: true
561 | allowElseExpression: true
562 | NullableToStringCall:
563 | active: false
564 | RedundantElseInWhen:
565 | active: true
566 | UnconditionalJumpStatementInLoop:
567 | active: false
568 | UnnecessaryNotNullOperator:
569 | active: true
570 | UnnecessarySafeCall:
571 | active: true
572 | UnreachableCatchBlock:
573 | active: false
574 | UnreachableCode:
575 | active: true
576 | UnsafeCallOnNullableType:
577 | active: true
578 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
579 | UnsafeCast:
580 | active: true
581 | UnusedUnaryOperator:
582 | active: false
583 | UselessPostfixExpression:
584 | active: false
585 | WrongEqualsTypeParameter:
586 | active: true
587 |
588 | style:
589 | active: true
590 | ClassOrdering:
591 | active: false
592 | CollapsibleIfStatements:
593 | active: false
594 | DataClassContainsFunctions:
595 | active: false
596 | conversionFunctionPrefix: 'to'
597 | DataClassShouldBeImmutable:
598 | active: false
599 | DestructuringDeclarationWithTooManyEntries:
600 | active: false
601 | maxDestructuringEntries: 3
602 | EqualsNullCall:
603 | active: true
604 | EqualsOnSignatureLine:
605 | active: false
606 | ExplicitCollectionElementAccessMethod:
607 | active: false
608 | ExplicitItLambdaParameter:
609 | active: false
610 | ExpressionBodySyntax:
611 | active: false
612 | includeLineWrapping: false
613 | ForbiddenComment:
614 | active: true
615 | values:
616 | - 'FIXME:'
617 | - 'STOPSHIP:'
618 | - 'TODO:'
619 | allowedPatterns: ''
620 | ForbiddenImport:
621 | active: false
622 | imports: []
623 | forbiddenPatterns: ''
624 | ForbiddenMethodCall:
625 | active: false
626 | methods:
627 | - 'kotlin.io.print'
628 | - 'kotlin.io.println'
629 | ForbiddenPublicDataClass:
630 | active: true
631 | excludes: ['**']
632 | ignorePackages:
633 | - '*.internal'
634 | - '*.internal.*'
635 | ForbiddenVoid:
636 | active: false
637 | ignoreOverridden: false
638 | ignoreUsageInGenerics: false
639 | FunctionOnlyReturningConstant:
640 | active: true
641 | ignoreOverridableFunction: true
642 | ignoreActualFunction: true
643 | excludedFunctions: ''
644 | LibraryCodeMustSpecifyReturnType:
645 | active: true
646 | excludes: ['**']
647 | LibraryEntitiesShouldNotBePublic:
648 | active: true
649 | excludes: ['**']
650 | LoopWithTooManyJumpStatements:
651 | active: true
652 | maxJumpCount: 1
653 | MagicNumber:
654 | active: true
655 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
656 | ignoreNumbers:
657 | - '-1'
658 | - '0'
659 | - '1'
660 | - '2'
661 | ignoreHashCodeFunction: true
662 | ignorePropertyDeclaration: false
663 | ignoreLocalVariableDeclaration: false
664 | ignoreConstantDeclaration: true
665 | ignoreCompanionObjectPropertyDeclaration: true
666 | ignoreAnnotation: false
667 | ignoreNamedArgument: true
668 | ignoreEnums: false
669 | ignoreRanges: false
670 | ignoreExtensionFunctions: true
671 | MandatoryBracesIfStatements:
672 | active: false
673 | MandatoryBracesLoops:
674 | active: false
675 | MaxLineLength:
676 | active: true
677 | maxLineLength: 120
678 | excludePackageStatements: true
679 | excludeImportStatements: true
680 | excludeCommentStatements: false
681 | MayBeConst:
682 | active: true
683 | ModifierOrder:
684 | active: true
685 | MultilineLambdaItParameter:
686 | active: false
687 | NestedClassesVisibility:
688 | active: true
689 | NewLineAtEndOfFile:
690 | active: true
691 | NoTabs:
692 | active: false
693 | ObjectLiteralToLambda:
694 | active: false
695 | OptionalAbstractKeyword:
696 | active: true
697 | OptionalUnit:
698 | active: false
699 | OptionalWhenBraces:
700 | active: false
701 | PreferToOverPairSyntax:
702 | active: false
703 | ProtectedMemberInFinalClass:
704 | active: true
705 | RedundantExplicitType:
706 | active: false
707 | RedundantHigherOrderMapUsage:
708 | active: false
709 | RedundantVisibilityModifierRule:
710 | active: false
711 | ReturnCount:
712 | active: true
713 | max: 2
714 | excludedFunctions: 'equals'
715 | excludeLabeled: false
716 | excludeReturnFromLambda: true
717 | excludeGuardClauses: false
718 | SafeCast:
719 | active: true
720 | SerialVersionUIDInSerializableClass:
721 | active: true
722 | SpacingBetweenPackageAndImports:
723 | active: false
724 | ThrowsCount:
725 | active: true
726 | max: 2
727 | excludeGuardClauses: false
728 | TrailingWhitespace:
729 | active: false
730 | # UnderscoresInNumericLiterals:
731 | # active: false
732 | # acceptableLength: 4
733 | UnnecessaryAbstractClass:
734 | active: true
735 | UnnecessaryAnnotationUseSiteTarget:
736 | active: false
737 | UnnecessaryApply:
738 | active: true
739 | UnnecessaryFilter:
740 | active: false
741 | UnnecessaryInheritance:
742 | active: true
743 | UnnecessaryLet:
744 | active: false
745 | UnnecessaryParentheses:
746 | active: false
747 | UntilInsteadOfRangeTo:
748 | active: false
749 | UnusedImports:
750 | active: false
751 | UnusedPrivateClass:
752 | active: true
753 | UnusedPrivateMember:
754 | active: true
755 | allowedNames: '(_|ignored|expected|serialVersionUID)'
756 | UseArrayLiteralsInAnnotations:
757 | active: false
758 | UseCheckNotNull:
759 | active: false
760 | UseCheckOrError:
761 | active: false
762 | UseDataClass:
763 | active: false
764 | allowVars: false
765 | UseEmptyCounterpart:
766 | active: false
767 | UseIfEmptyOrIfBlank:
768 | active: false
769 | UseIfInsteadOfWhen:
770 | active: false
771 | UseIsNullOrEmpty:
772 | active: false
773 | UseOrEmpty:
774 | active: false
775 | UseRequire:
776 | active: false
777 | UseRequireNotNull:
778 | active: false
779 | UselessCallOnNotNull:
780 | active: true
781 | UtilityClassWithPublicConstructor:
782 | active: true
783 | VarCouldBeVal:
784 | active: true
785 | WildcardImport:
786 | active: true
787 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
788 | excludeImports:
789 | - 'java.util.*'
790 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @xmartlabs/android-open-source
--------------------------------------------------------------------------------
/.github/workflows/compile-and-check.yml:
--------------------------------------------------------------------------------
1 | name: Run lints and build
2 | on:
3 | pull_request:
4 | push:
5 | branches:
6 | - main
7 | jobs:
8 | detekt_and_build:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v2
12 | - name: Cache files
13 | uses: actions/cache@v2
14 | with:
15 | path: |
16 | ~/.gradle/caches
17 | ~/.gradle/wrapper
18 | ~/.android/build-cache
19 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle-wrapper.properties') }}
20 | restore-keys: |
21 | ${{ runner.os }}-gradle-
22 | - name: Gradle Initialization
23 | run: ./gradlew
24 | - name: Detekt
25 | run: ./gradlew detekt
26 | - name: Upload Detekt report
27 | uses: actions/upload-artifact@v2
28 | if: failure()
29 | with:
30 | name: Detekt report
31 | path: build/reports/detekt/detekt.html
32 | - name: Build
33 | run: ./gradlew :typednavigation:build
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | TypedNavigation
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | xmlns:android
21 |
22 | ^$
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | xmlns:.*
32 |
33 | ^$
34 |
35 |
36 | BY_NAME
37 |
38 |
39 |
40 |
41 |
42 |
43 | .*:id
44 |
45 | http://schemas.android.com/apk/res/android
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | .*:name
55 |
56 | http://schemas.android.com/apk/res/android
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | name
66 |
67 | ^$
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | style
77 |
78 | ^$
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | .*
88 |
89 | ^$
90 |
91 |
92 | BY_NAME
93 |
94 |
95 |
96 |
97 |
98 |
99 | .*
100 |
101 | http://schemas.android.com/apk/res/android
102 |
103 |
104 | ANDROID_ATTRIBUTE_ORDER
105 |
106 |
107 |
108 |
109 |
110 |
111 | .*
112 |
113 | .*
114 |
115 |
116 | BY_NAME
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
20 |
21 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TypedNavigation
2 |
3 | [](https://github.com/xmartlabs/TypedNavigation/actions/workflows/compile-and-check.yml)
4 | [](https://jitpack.io/#xmartlabs/TypedNavigation)
5 |
6 | A lightweight library to help you navigate in compose with well typed functions.
7 |
8 | ## Installation:
9 |
10 | You can add this library to your project by just adding the following code to your root `build.gradle`
11 |
12 | ```groovy
13 | allprojects {
14 | repositories {
15 | // ...
16 | maven { url 'https://jitpack.io' }
17 | }
18 | }
19 | ```
20 |
21 | Then import the library in your app `build.gradle` file.
22 |
23 | ```groovy
24 | implementation 'com.github.xmartlabs:TypedNavigation:0.0.4'
25 | ```
26 |
27 | ## Usage:
28 |
29 | You just have to define your screens and the arguments they receive:
30 |
31 | ```kotlin
32 | object Router {
33 | val default = TypedNavigation.E("default")
34 | val sample = TypedNavigation.A3("sample", NavType.StringType, NavType.StringType, NavType.StringType)
35 | }
36 | ```
37 |
38 | And after that the library will provide you with the following functions:
39 |
40 | To add your screen to the `NavHost`:
41 |
42 | ```kotlin
43 | setContent {
44 | val navigationController: NavHostController = rememberNavController()
45 | NavHost(navController = navigationController, startDestination = Router.default.url) {
46 | composable(Router.default) {
47 | Default(navigationController = navigationController)
48 | }
49 | composable(Router.example) { a: String?, b: String?, c: String? ->
50 | Example(a, b, c)
51 | }
52 | }
53 | }
54 | ```
55 |
56 | To navigate from one screen to another:
57 |
58 | ```kotlin
59 | navigationController.navigate(Router.example.route("a", "b", "c"))
60 | ```
61 |
62 | Add deep linking to your screen by setting up the correct path to the url:
63 |
64 | ```kotlin
65 | val example =
66 | TypedNavigation.A3("example", NavType.StringType, NavType.StringType, NavType.StringType,
67 | listOf { a1, a2, a3 -> // a1, a2 and a3 contains the keys for the attributes previously defined
68 | "www.example.com/$a1/$a2/$a3"
69 | }
70 | )
71 | ```
72 |
73 | ### Use it with hilt ViewModel
74 | You can access attributes stored in the `SavedStateHandle` by using `withAttributes`
75 |
76 | ```kotlin
77 | @HiltViewModel
78 | class HiltExampleViewModel @Inject constructor(
79 | savedStateHandle: SavedStateHandle
80 | ) : ViewModel() {
81 | data class ScreenState(val name: String? = null, val age: Int? = null, val knowsHilt: Boolean? = null)
82 |
83 | val stateFlow: MutableStateFlow = MutableStateFlow(ScreenState())
84 |
85 | init {
86 | Router.hiltExample.withAttributes(savedStateHandle) { name, age, knowsHilt ->
87 | viewModelScope.launch {
88 | stateFlow.emit(ScreenState(name!!, age, knowsHilt))
89 | }
90 | }
91 | }
92 | }
93 | ```
94 |
95 | For more examples you can check out our example app.
96 |
97 | ## About
98 |
99 | Made with ❤️ by [XMARTLABS](http://xmartlabs.com)
100 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | id 'kotlin-parcelize'
5 | id 'kotlin-kapt'
6 | id 'dagger.hilt.android.plugin'
7 | }
8 |
9 | android {
10 | compileSdk 31
11 |
12 | defaultConfig {
13 | applicationId "com.xmartlabs.typednavigation"
14 | minSdk 21
15 | targetSdk 31
16 | versionCode 1
17 | versionName "1.0"
18 |
19 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
20 | vectorDrawables {
21 | useSupportLibrary true
22 | }
23 | }
24 |
25 | buildTypes {
26 | release {
27 | minifyEnabled false
28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
29 | }
30 | }
31 | compileOptions {
32 | sourceCompatibility JavaVersion.VERSION_1_8
33 | targetCompatibility JavaVersion.VERSION_1_8
34 | }
35 | kotlinOptions {
36 | jvmTarget = '1.8'
37 | useIR = true
38 | languageVersion = "1.5"
39 | }
40 | buildFeatures {
41 | compose true
42 | }
43 | composeOptions {
44 | kotlinCompilerExtensionVersion compose_version
45 | kotlinCompilerVersion '1.5.31'
46 | }
47 | packagingOptions {
48 | resources {
49 | excludes += '/META-INF/{AL2.0,LGPL2.1}'
50 | }
51 | }
52 | }
53 |
54 | dependencies {
55 |
56 | implementation "androidx.compose.material:material:$compose_version"
57 | implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
58 | implementation "androidx.compose.ui:ui:$compose_version"
59 | implementation "com.google.dagger:hilt-android:2.38.1"
60 | implementation 'androidx.activity:activity-compose:1.4.0'
61 | implementation 'androidx.appcompat:appcompat:1.4.1'
62 | implementation 'androidx.core:core-ktx:1.7.0'
63 | implementation 'androidx.hilt:hilt-navigation-compose:1.0.0-rc01'
64 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
65 | implementation 'androidx.navigation:navigation-compose:2.4.0-rc01'
66 | implementation 'com.google.android.material:material:1.5.0'
67 | implementation project(":typednavigation")
68 |
69 | kapt "com.google.dagger:hilt-compiler:2.38.1"
70 |
71 | testImplementation 'junit:junit:4.+'
72 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
73 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
74 | androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
75 | debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
76 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/xmartlabs/typednavigation/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("com.xmartlabs.typednavigation", appContext.packageName)
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xmartlabs/typednavigation/App.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation
2 |
3 | import android.app.Application
4 | import dagger.hilt.android.HiltAndroidApp
5 |
6 | @HiltAndroidApp
7 | class App : Application() {
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xmartlabs/typednavigation/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation
2 |
3 | import android.os.Bundle
4 | import androidx.activity.compose.setContent
5 | import androidx.appcompat.app.AppCompatActivity
6 | import androidx.compose.foundation.layout.Arrangement
7 | import androidx.compose.foundation.layout.Box
8 | import androidx.compose.foundation.layout.Row
9 | import androidx.compose.foundation.layout.fillMaxSize
10 | import androidx.compose.foundation.layout.padding
11 | import androidx.compose.material.Button
12 | import androidx.compose.material.Text
13 | import androidx.compose.runtime.Composable
14 | import androidx.compose.ui.Alignment
15 | import androidx.compose.ui.Modifier
16 | import androidx.compose.ui.unit.dp
17 | import androidx.hilt.navigation.compose.hiltViewModel
18 | import androidx.navigation.NavHostController
19 | import androidx.navigation.NavType
20 | import androidx.navigation.compose.NavHost
21 | import androidx.navigation.compose.rememberNavController
22 | import com.xmartlabs.typednavigation.ui.hiltExapmle.HiltExample
23 | import com.xmartlabs.typednavigation.ui.hiltExapmle.HiltExampleViewModel
24 | import com.xmartlabs.typednavigation.ui.theme.TypedNavigationTheme
25 | import dagger.hilt.android.AndroidEntryPoint
26 |
27 | @AndroidEntryPoint
28 | class MainActivity : AppCompatActivity() {
29 | override fun onCreate(savedInstanceState: Bundle?) {
30 | super.onCreate(savedInstanceState)
31 | setContent {
32 | val navigationController: NavHostController = rememberNavController()
33 | NavHost(navController = navigationController, startDestination = Router.default.url) {
34 | composable(Router.default) {
35 | Default(navigationController = navigationController)
36 | }
37 | composable(Router.home) { a1, a2 ->
38 | Home(a1, a2)
39 | }
40 | composable(Router.example) { a, b, c ->
41 | Example(a, b, c)
42 | }
43 | composable(Router.hiltExample) {
44 | val hiltExampleViewModel = hiltViewModel()
45 | HiltExample(hiltExampleViewModel)
46 | }
47 | }
48 | }
49 | }
50 | }
51 |
52 | @Composable
53 | fun Default(navigationController: NavHostController) = Box(
54 | contentAlignment = Alignment.Center,
55 | modifier = Modifier.fillMaxSize()
56 | ) {
57 | Row(
58 | verticalAlignment = Alignment.CenterVertically,
59 | horizontalArrangement = Arrangement.SpaceEvenly
60 | ) {
61 | Button(
62 | onClick = { navigationController.navigate(Router.home.route("example", 5)) },
63 | modifier = Modifier.padding(end = 10.dp)
64 | ) {
65 | Text(text = "Home")
66 | }
67 | Button(
68 | onClick = { navigationController.navigate(Router.example.route("a", "b", "c")) },
69 | modifier = Modifier.padding(end = 10.dp)
70 | ) {
71 | Text(text = "Example")
72 | }
73 | Button(onClick = { navigationController.navigate(Router.hiltExample.route(null, null, false)) }) {
74 | Text(text = "Hilt Example")
75 | }
76 | }
77 | }
78 |
79 | @Composable
80 | fun Home(a1: String?, a2: Int) = Box(
81 | contentAlignment = Alignment.Center,
82 | modifier = Modifier.fillMaxSize()
83 | ) {
84 | Text(text = "Hello $a1 $a2!")
85 | }
86 |
87 | @Composable
88 | fun Example(a1: String?, a2: String?, a3: String?) = Box(
89 | contentAlignment = Alignment.Center,
90 | modifier = Modifier.fillMaxSize()
91 | ) {
92 | TypedNavigationTheme {
93 | Text(text = "Hello $a1 $a2 $a3!")
94 | }
95 | }
96 |
97 | object Router {
98 | val default = TypedNavigation.E("default")
99 | val home = TypedNavigation.A2("home", NavType.StringType, NavType.IntType)
100 | val example =
101 | TypedNavigation.A3("example", NavType.StringType, NavType.StringType, NavType.StringType, listOf { a1, a2, a3 ->
102 | "www.example.com/$a1/$a2/$a3"
103 | }
104 | )
105 | val hiltExample =
106 | TypedNavigation.A3("hiltExample", NavType.StringType, NavType.NullableBool, NavType.BoolType)
107 | }
108 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xmartlabs/typednavigation/ui/hiltExapmle/HiltExample.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation.ui.hiltExapmle
2 |
3 | import androidx.compose.material.Text
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.runtime.collectAsState
6 | import androidx.compose.runtime.getValue
7 | import androidx.lifecycle.viewmodel.compose.viewModel
8 |
9 | @Composable
10 | fun HiltExample(
11 | viewModel: HiltExampleViewModel
12 | ) {
13 | val state by viewModel.stateFlow.collectAsState()
14 | Text(text = "Hello ${state.name} your age is ${state.age} and you ${if (state.knowsHilt == true) "know" else "don't know"} hilt")
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xmartlabs/typednavigation/ui/hiltExapmle/HiltExampleViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation.ui.hiltExapmle
2 |
3 | import androidx.lifecycle.SavedStateHandle
4 | import androidx.lifecycle.ViewModel
5 | import androidx.lifecycle.viewModelScope
6 | import com.xmartlabs.typednavigation.Router
7 | import com.xmartlabs.typednavigation.withAttributes
8 | import dagger.hilt.android.lifecycle.HiltViewModel
9 | import kotlinx.coroutines.flow.MutableStateFlow
10 | import kotlinx.coroutines.launch
11 | import javax.inject.Inject
12 |
13 | @HiltViewModel
14 | class HiltExampleViewModel @Inject constructor(
15 | savedStateHandle: SavedStateHandle
16 | ) : ViewModel() {
17 | data class ScreenState(val name: String? = null, val age: Boolean? = null, val knowsHilt: Boolean? = null)
18 |
19 | val stateFlow: MutableStateFlow = MutableStateFlow(ScreenState())
20 |
21 | init {
22 | Router.hiltExample.withAttributes(savedStateHandle) { name, age, knowsHilt ->
23 | viewModelScope.launch {
24 | stateFlow.emit(ScreenState(name!!, age, knowsHilt))
25 | }
26 | }
27 | }
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xmartlabs/typednavigation/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation.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)
--------------------------------------------------------------------------------
/app/src/main/java/com/xmartlabs/typednavigation/ui/theme/Shape.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation.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 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/xmartlabs/typednavigation/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation.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 TypedNavigationTheme(
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 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/xmartlabs/typednavigation/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation.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 | )
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmartlabs/TypedNavigation/cafc9db817cdf773a5a3b89058532eb2605cba8f/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmartlabs/TypedNavigation/cafc9db817cdf773a5a3b89058532eb2605cba8f/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmartlabs/TypedNavigation/cafc9db817cdf773a5a3b89058532eb2605cba8f/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmartlabs/TypedNavigation/cafc9db817cdf773a5a3b89058532eb2605cba8f/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmartlabs/TypedNavigation/cafc9db817cdf773a5a3b89058532eb2605cba8f/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmartlabs/TypedNavigation/cafc9db817cdf773a5a3b89058532eb2605cba8f/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmartlabs/TypedNavigation/cafc9db817cdf773a5a3b89058532eb2605cba8f/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmartlabs/TypedNavigation/cafc9db817cdf773a5a3b89058532eb2605cba8f/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmartlabs/TypedNavigation/cafc9db817cdf773a5a3b89058532eb2605cba8f/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmartlabs/TypedNavigation/cafc9db817cdf773a5a3b89058532eb2605cba8f/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | TypedNavigation
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/test/java/com/xmartlabs/typednavigation/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | ext {
4 | compose_version = '1.0.5'
5 | }
6 | repositories {
7 | google()
8 | mavenCentral()
9 | }
10 | dependencies {
11 | classpath "com.android.tools.build:gradle:7.0.4"
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31"
13 | classpath("com.google.dagger:hilt-android-gradle-plugin:2.38.1")
14 |
15 | // NOTE: Do not place your application dependencies here; they belong
16 | // in the individual module build.gradle files
17 | }
18 | }
19 |
20 | plugins {
21 | id "io.gitlab.arturbosch.detekt" version "1.18.1"
22 | }
23 |
24 | detekt {
25 | buildUponDefaultConfig = true
26 | allRules = false
27 | config = files("$projectDir/.config/detekt.yml")
28 | source = files(
29 | "typednavigation/src/main"
30 | )
31 | reports {
32 | html.enabled = true
33 | xml.enabled = true
34 | txt.enabled = true
35 | sarif.enabled = true
36 | }
37 | }
38 |
39 | task clean(type: Delete) {
40 | delete rootProject.buildDir
41 | }
--------------------------------------------------------------------------------
/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 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmartlabs/TypedNavigation/cafc9db817cdf773a5a3b89058532eb2605cba8f/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Oct 01 19:41:11 UYT 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | jdk:
2 | - openjdk11
3 | install:
4 | - ./gradlew :typednavigation:build :typednavigation:publishToMavenLocal
5 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | dependencyResolutionManagement {
2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | rootProject.name = "TypedNavigation"
9 | include ':app'
10 | include ':typednavigation'
11 |
--------------------------------------------------------------------------------
/typednavigation/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/typednavigation/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'kotlin-android'
4 | id 'maven-publish'
5 | }
6 |
7 | task androidSourcesJar(type: Jar) {
8 | classifier 'sources'
9 | from android.sourceSets.main.java.srcDirs
10 | }
11 |
12 | project.afterEvaluate {
13 | publishing {
14 | publications {
15 | release(MavenPublication) {
16 | from components.release
17 | }
18 | }
19 | }
20 | }
21 |
22 | android {
23 | compileSdk 31
24 |
25 | defaultConfig {
26 | minSdk 21
27 | targetSdk 31
28 | versionCode 1
29 | versionName "0.0.4"
30 |
31 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
32 | consumerProguardFiles "consumer-rules.pro"
33 | }
34 |
35 | buildTypes {
36 | release {
37 | minifyEnabled false
38 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
39 | }
40 | }
41 | compileOptions {
42 | sourceCompatibility JavaVersion.VERSION_1_8
43 | targetCompatibility JavaVersion.VERSION_1_8
44 | }
45 |
46 | kotlin.explicitApi = 'strict'
47 |
48 | kotlinOptions {
49 | jvmTarget = '1.8'
50 | useIR = true
51 | languageVersion = "1.5"
52 | freeCompilerArgs += [
53 | '-Xexplicit-api=strict',
54 | ]
55 | }
56 |
57 | composeOptions {
58 | kotlinCompilerVersion "1.5.31"
59 | kotlinCompilerExtensionVersion compose_version
60 | }
61 |
62 | buildFeatures {
63 | compose true
64 | }
65 | }
66 |
67 | dependencies {
68 |
69 | implementation 'androidx.core:core-ktx:1.7.0'
70 | implementation 'androidx.appcompat:appcompat:1.4.1'
71 | implementation 'com.google.android.material:material:1.5.0'
72 | implementation 'androidx.navigation:navigation-compose:2.4.1'
73 | implementation "androidx.compose.compiler:compiler:$compose_version"
74 | implementation "androidx.compose.runtime:runtime:$compose_version"
75 | testImplementation 'junit:junit:4.+'
76 |
77 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
78 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
79 | }
80 |
--------------------------------------------------------------------------------
/typednavigation/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xmartlabs/TypedNavigation/cafc9db817cdf773a5a3b89058532eb2605cba8f/typednavigation/consumer-rules.pro
--------------------------------------------------------------------------------
/typednavigation/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/typednavigation/src/androidTest/java/com/xmartlabs/typednavigation/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation
2 |
3 | import androidx.compose.ui.test.junit4.createComposeRule
4 | import androidx.navigation.NavHostController
5 | import androidx.navigation.compose.NavHost
6 | import androidx.navigation.compose.rememberNavController
7 | import androidx.test.ext.junit.runners.AndroidJUnit4
8 | import org.junit.Rule
9 | import org.junit.Test
10 | import org.junit.runner.RunWith
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * See [testing documentation](http://d.android.com/tools/testing).
16 | */
17 | @RunWith(AndroidJUnit4::class)
18 | public class ExampleInstrumentedTest {}
--------------------------------------------------------------------------------
/typednavigation/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/typednavigation/src/main/java/com/xmartlabs/typednavigation/NavGraphBuilderExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.navigation.NavGraphBuilder
5 | import androidx.navigation.compose.composable
6 |
7 | public fun NavGraphBuilder.composable(
8 | screen: TypedNavigationInterface,
9 | function: @Composable () -> Unit
10 | ) {
11 | composable(
12 | screen.url,
13 | screen.navArguments,
14 | screen.deepLinks
15 | ) {
16 | function()
17 | }
18 | }
19 |
20 | public fun NavGraphBuilder.composable(
21 | screen: TypedNavigationInterface0,
22 | function: @Composable () -> Unit
23 | ) {
24 | composable(
25 | screen.url,
26 | screen.navArguments,
27 | screen.deepLinks
28 | ) {
29 | function()
30 | }
31 | }
32 |
33 | public fun NavGraphBuilder.composable(
34 | screen: TypedNavigationInterface1,
35 | function: @Composable (A) -> Unit
36 | ) {
37 | composable(
38 | screen.url,
39 | screen.navArguments,
40 | screen.deepLinks
41 | ) {
42 | val arg1 = it.arguments?.get("a1") as A
43 | function(arg1)
44 | }
45 | }
46 |
47 | public fun NavGraphBuilder.composable(
48 | screen: TypedNavigationInterface2,
49 | function: @Composable (A1, A2) -> Unit
50 | ) {
51 | composable(
52 | screen.url,
53 | screen.navArguments,
54 | screen.deepLinks
55 | ) {
56 | val arg1 = it.arguments?.get("a1") as A1
57 | val arg2 = it.arguments?.get("a2") as A2
58 | function(arg1, arg2)
59 | }
60 | }
61 |
62 | public fun NavGraphBuilder.composable(
63 | screen: TypedNavigationInterface3,
64 | function: @Composable (A1, A2, A3) -> Unit
65 | ) {
66 | composable(
67 | screen.url,
68 | screen.navArguments,
69 | screen.deepLinks
70 | ) {
71 | val arg1 = it.arguments?.get("a1") as A1
72 | val arg2 = it.arguments?.get("a2") as A2
73 | val arg3 = it.arguments?.get("a3") as A3
74 | function(arg1, arg2, arg3)
75 | }
76 | }
77 |
78 | public fun NavGraphBuilder.composable(
79 | screen: TypedNavigationInterface4,
80 | function: @Composable (A1, A2, A3, A4) -> Unit
81 | ) {
82 | composable(
83 | screen.url,
84 | screen.navArguments,
85 | screen.deepLinks
86 | ) {
87 | val arg1 = it.arguments?.get("a1") as A1
88 | val arg2 = it.arguments?.get("a2") as A2
89 | val arg3 = it.arguments?.get("a3") as A3
90 | val arg4 = it.arguments?.get("a4") as A4
91 | function(arg1, arg2, arg3, arg4)
92 | }
93 | }
94 |
95 | public fun NavGraphBuilder.composable(
96 | screen: TypedNavigationInterface5,
97 | function: @Composable (A1, A2, A3, A4, A5) -> Unit
98 | ) {
99 | composable(
100 | screen.url,
101 | screen.navArguments,
102 | screen.deepLinks
103 | ) {
104 | val arg1 = it.arguments?.get("a1") as A1
105 | val arg2 = it.arguments?.get("a2") as A2
106 | val arg3 = it.arguments?.get("a3") as A3
107 | val arg4 = it.arguments?.get("a4") as A4
108 | val arg5 = it.arguments?.get("a5") as A5
109 | function(arg1, arg2, arg3, arg4, arg5)
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/typednavigation/src/main/java/com/xmartlabs/typednavigation/NullableTypes.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation
2 |
3 | import android.os.Bundle
4 | import androidx.navigation.NavType
5 |
6 | @Suppress("MagicNumber")
7 | public val NavType.Companion.NullableInt: NavType
8 | get() = object : NavType(isNullableAllowed = true) {
9 | override val name: String
10 | get() = "integer?"
11 |
12 | override fun get(bundle: Bundle, key: String): Int? =
13 | if (bundle.containsKey(key)) bundle.getInt(key) else null
14 |
15 |
16 | override fun put(bundle: Bundle, key: String, value: Int?) = value?.let {
17 | bundle.putInt(key, value)
18 | } ?: Unit
19 |
20 | override fun parseValue(value: String): Int? {
21 | return if (value.startsWith("0x")) {
22 | value.substring(2).toIntOrNull(16)
23 | } else {
24 | value.toIntOrNull()
25 | }
26 | }
27 | }
28 |
29 | @Suppress("MagicNumber")
30 | public val NavType.Companion.NullableLong: NavType
31 | get() = object : NavType(true) {
32 | override val name: String
33 | get() = "long?"
34 |
35 | override fun put(bundle: Bundle, key: String, value: Long?) = value?.let {
36 | bundle.putLong(key, it)
37 | } ?: Unit
38 |
39 | override fun get(bundle: Bundle, key: String): Long? =
40 | if (bundle.containsKey(key)) bundle.getLong(key) else null
41 |
42 | override fun parseValue(value: String): Long? {
43 | // At runtime the L suffix is optional, contrary to the Safe Args plugin.
44 | // This is in order to be able to parse long numbers passed as deep link URL
45 | // parameters
46 | var localValue = value
47 | if (value.endsWith("L")) {
48 | localValue = localValue.substring(0, value.length - 1)
49 | }
50 | return if (value.startsWith("0x")) {
51 | localValue.substring(2).toLongOrNull(16)
52 | } else {
53 | localValue.toLongOrNull()
54 | }
55 | }
56 | }
57 |
58 | public val NavType.Companion.NullableBool: NavType
59 | get() = object : NavType(isNullableAllowed = true) {
60 | override val name: String
61 | get() = "boolean?"
62 |
63 | override fun get(bundle: Bundle, key: String): Boolean? =
64 | if (bundle.containsKey(key)) bundle.getBoolean(key) else null
65 |
66 |
67 | override fun put(bundle: Bundle, key: String, value: Boolean?) = value?.let {
68 | bundle.putBoolean(key, value)
69 | } ?: Unit
70 |
71 | override fun parseValue(value: String): Boolean? {
72 | return when (value) {
73 | "true" -> true
74 | "false" -> false
75 | else -> null
76 | }
77 | }
78 | }
79 |
80 | public val NavType.Companion.NullableFloat: NavType
81 | get() = object : NavType(true) {
82 | override val name: String
83 | get() = "float?"
84 |
85 | override fun put(bundle: Bundle, key: String, value: Float?) = value?.let {
86 | bundle.putFloat(key, it)
87 | } ?: Unit
88 |
89 | override fun get(bundle: Bundle, key: String): Float? =
90 | if (bundle.containsKey(key)) bundle.getFloat(key) else null
91 |
92 | override fun parseValue(value: String): Float {
93 | return value.toFloat()
94 | }
95 | }
96 |
97 | public val NavType.Companion.NotNullString: NavType
98 | get() = object : NavType(false) {
99 | override val name: String
100 | get() = "String?"
101 |
102 | override fun put(bundle: Bundle, key: String, value: String) = bundle.putString(key, value)
103 |
104 |
105 | override fun get(bundle: Bundle, key: String): String = bundle.getString(key)!!
106 |
107 | override fun parseValue(value: String): String = value
108 | }
109 |
--------------------------------------------------------------------------------
/typednavigation/src/main/java/com/xmartlabs/typednavigation/TypedNavigation.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation
2 |
3 | import androidx.navigation.NamedNavArgument
4 | import androidx.navigation.NavDeepLink
5 | import androidx.navigation.NavType
6 | import androidx.navigation.navArgument
7 | import androidx.navigation.navDeepLink
8 |
9 |
10 | /** Sealed class that implements [TypedNavigationInterface] */
11 | public sealed class TypedNavigation {
12 | /** Implementation for the [TypedNavigationInterface0]
13 | * @property deepLinksBuilder is a list of functions that when executed provide the uriPattern for the deep linking
14 | * */
15 | public data class E(
16 | override val name: String,
17 | private val deepLinksBuilder: List<() -> String> = listOf()
18 | ) : TypedNavigation(), TypedNavigationInterface0 {
19 | override val url: String = "$name/"
20 | override val navArguments: List = listOf()
21 | override val deepLinks: List = deepLinksBuilder.map {
22 | navDeepLink {
23 | uriPattern = it.invoke()
24 | }
25 | }
26 |
27 | override fun route(): String = "$name/"
28 | }
29 |
30 | /** Implementation for the [TypedNavigationInterface1]
31 | * @property deepLinksBuilder is a list of functions that when executed provide the uriPattern for the deep linking
32 | * */
33 | public data class A1(
34 | override val name: String,
35 | override val t1: NavType,
36 | private val deepLinksBuilder: List<(String) -> String> = listOf()
37 | ) : TypedNavigation(), TypedNavigationInterface1 {
38 | override val url: String = "$name/{a1}"
39 | override val navArguments: List = listOf(
40 | t1.toNavArgument("a1"),
41 | )
42 | override val deepLinks: List = deepLinksBuilder.map { func ->
43 | navDeepLink {
44 | uriPattern = func.invoke("{a1}")
45 | }
46 | }
47 |
48 | override fun route(arg1: A): String = "$name/${arg1.toString()}"
49 | }
50 |
51 | /** Implementation for the [TypedNavigationInterface2]
52 | * @property deepLinksBuilder is a list of functions that when executed provide the uriPattern for the deep linking
53 | * */
54 | public data class A2(
55 | override val name: String,
56 | override val t1: NavType,
57 | override val t2: NavType,
58 | private val deepLinksBuilder: List<(String, String) -> String> = listOf()
59 | ) : TypedNavigation(), TypedNavigationInterface2 {
60 | override val url: String = "$name/{a1}/{a2}"
61 | override val deepLinks: List = deepLinksBuilder.map { func ->
62 | navDeepLink {
63 | uriPattern = func.invoke("{a1}", "{a2}")
64 | }
65 | }
66 |
67 |
68 | override val navArguments: List = listOf(
69 | t1.toNavArgument("a1"),
70 | t2.toNavArgument("a2"),
71 | )
72 |
73 | override fun route(arg1: A1, arg2: A2): String =
74 | "$name/${arg1.toString()}/${arg2.toString()}"
75 | }
76 |
77 | /** Implementation for the [TypedNavigationInterface3]
78 | * @property deepLinksBuilder is a list of functions that when executed provide the uriPattern for the deep linking
79 | * */
80 | public data class A3(
81 | override val name: String,
82 | override val t1: NavType,
83 | override val t2: NavType,
84 | override val t3: NavType,
85 | private val deepLinksBuilder: List<(String, String, String) -> String> = listOf()
86 | ) : TypedNavigation(), TypedNavigationInterface3 {
87 | override val url: String = "$name/{a1}/{a2}/{a3}"
88 | override val navArguments: List = listOf(
89 | t1.toNavArgument("a1"),
90 | t2.toNavArgument("a2"),
91 | t3.toNavArgument("a3"),
92 | )
93 | override val deepLinks: List = deepLinksBuilder.map { func ->
94 | navDeepLink {
95 | uriPattern = func.invoke("{a1}", "{a2}", "{a3}")
96 | }
97 | }
98 |
99 | override fun route(arg1: A1, arg2: A2, arg3: A3): String =
100 | "$name/${arg1.toString()}/${arg2.toString()}/${arg3.toString()}"
101 | }
102 |
103 | /** Implementation for the [TypedNavigationInterface4]
104 | * @property deepLinksBuilder is a list of functions that when executed provide the uriPattern for the deep linking
105 | * */
106 | public data class A4(
107 | override val name: String,
108 | override val t1: NavType,
109 | override val t2: NavType,
110 | override val t3: NavType,
111 | override val t4: NavType,
112 | private val deepLinksBuilder: List<(String, String, String, String) -> String> = listOf()
113 | ) : TypedNavigation(), TypedNavigationInterface4 {
114 | override val url: String = "$name/{a1}/{a2}/{a3}/{a4}"
115 | override val navArguments: List = listOf(
116 | t1.toNavArgument("a1"),
117 | t2.toNavArgument("a2"),
118 | t3.toNavArgument("a3"),
119 | t4.toNavArgument("a4"),
120 | )
121 | override val deepLinks: List = deepLinksBuilder.map { func ->
122 | navDeepLink {
123 | uriPattern = func.invoke("{a1}", "{a2}", "{a3}", "{a4}")
124 | }
125 | }
126 |
127 | override fun route(arg1: A1, arg2: A2, arg3: A3, arg4: A4): String =
128 | "$name/${arg1.toString()}/${arg2.toString()}/${arg3.toString()}/${arg4.toString()}"
129 | }
130 |
131 | /**Implementation for the [TypedNavigationInterface5]
132 | * @property deepLinksBuilder is a list of functions that when executed provide the uriPattern for the deep linking
133 | * */
134 | public data class A5(
135 | override val name: String,
136 | override val t1: NavType,
137 | override val t2: NavType,
138 | override val t3: NavType,
139 | override val t4: NavType,
140 | override val t5: NavType,
141 | private val deepLinksBuilder: List<(String, String, String, String, String) -> String> = listOf()
142 | ) : TypedNavigation(), TypedNavigationInterface5 {
143 | override val url: String = "$name/{a1}/{a2}/{a3}/{a4}/{a5}"
144 |
145 | override val navArguments: List = listOf(
146 | t1.toNavArgument("a1"),
147 | t2.toNavArgument("a2"),
148 | t3.toNavArgument("a3"),
149 | t4.toNavArgument("a4"),
150 | t5.toNavArgument("a5"),
151 | )
152 |
153 | override val deepLinks: List = deepLinksBuilder.map { func ->
154 | navDeepLink {
155 | uriPattern = func.invoke("{a1}", "{a2}", "{a3}", "{a4}", "{a5}")
156 | }
157 | }
158 |
159 | override fun route(arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5): String =
160 | "$name/${arg1.toString()}/${arg2.toString()}/${arg3.toString()}/${arg4.toString()}/${arg5.toString()}"
161 | }
162 | }
163 |
164 | private fun NavType<*>.toNavArgument(name: String) = navArgument(name) {
165 | type = this@toNavArgument
166 | nullable = isNullableAllowed
167 | }
168 |
--------------------------------------------------------------------------------
/typednavigation/src/main/java/com/xmartlabs/typednavigation/TypedNavigationInterface.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation
2 |
3 | import androidx.navigation.NamedNavArgument
4 | import androidx.navigation.NavDeepLink
5 | import androidx.navigation.NavType
6 |
7 | /**
8 | An interface that implements the basic attributes needed for navigation
9 | *
10 | * @property name the name of the destination screen
11 | * @property url the url string for the navigation
12 | * @property navArguments the list of [NamedNavArgument] for the destination screen
13 | * @property deepLinks the list of [NavDeepLink] for the destination screen
14 | */
15 | public sealed interface TypedNavigationInterface {
16 | public val name: String
17 | public val url: String
18 | public val navArguments: List
19 | public val deepLinks: List
20 | }
21 |
22 | /** Defines the navigation without parameters
23 | *
24 | */
25 | public interface TypedNavigationInterface0 : TypedNavigationInterface {
26 |
27 | /** Returns the route of the destination */
28 | public fun route(): String
29 | }
30 |
31 | /** An interface that implements navigation with 1 parameter
32 | *
33 | * @property t1 the [NavType] for the argument
34 | *
35 | */
36 | public interface TypedNavigationInterface1 : TypedNavigationInterface {
37 | public val t1: NavType
38 |
39 | /** Get the [String] route for the destination
40 | * @param arg1 the argument corresponding to the [NavType] of [t1]
41 | *
42 | * @return the [String] containing the route of the destination
43 | */
44 | public fun route(arg1: A): String
45 | }
46 |
47 | /** An interface that implements navigation with 2 parameters
48 | *
49 | * @property t1 the [NavType] for the first argument
50 | * @property t2 the [NavType] for the second argument
51 | *
52 | */
53 | public interface TypedNavigationInterface2 : TypedNavigationInterface {
54 | public val t1: NavType
55 | public val t2: NavType
56 |
57 | /** Get the [String] route for the destination
58 | * @param arg1 the argument corresponding to the [NavType] of [t1]
59 | * @param arg2 the argument corresponding to the [NavType] of [t2]
60 | *
61 | * @return the [String] containing the route of the destination
62 | */
63 | public fun route(arg1: A1, arg2: A2): String
64 | }
65 |
66 | /** An interface that implements navigation with 3 parameters
67 | *
68 | * @property t1 the [NavType] for the first argument
69 | * @property t2 the [NavType] for the second argument
70 | * @property t3 the [NavType] for the third argument
71 | *
72 | */
73 | public interface TypedNavigationInterface3 : TypedNavigationInterface {
74 | public val t1: NavType
75 | public val t2: NavType
76 | public val t3: NavType
77 |
78 | /** Get the [String] route for the destination
79 | * @param arg1 the argument corresponding to the [NavType] of [t1]
80 | * @param arg2 the argument corresponding to the [NavType] of [t2]
81 | * @param arg3 the argument corresponding to the [NavType] of [t3]
82 | *
83 | * @return the [String] containing the route of the destination
84 | */
85 | public fun route(arg1: A1, arg2: A2, arg3: A3): String
86 | }
87 |
88 | /** An interface that implements navigation with 4 parameters
89 | *
90 | * @property t1 the [NavType] for the first argument
91 | * @property t2 the [NavType] for the second argument
92 | * @property t3 the [NavType] for the third argument
93 | * @property t4 the [NavType] for the fourth argument
94 | *
95 | */
96 | public interface TypedNavigationInterface4 : TypedNavigationInterface {
97 | public val t1: NavType
98 | public val t2: NavType
99 | public val t3: NavType
100 | public val t4: NavType
101 |
102 |
103 | /** Get the [String] route for the destination
104 | * @param arg1 the argument corresponding to the [NavType] of [t1]
105 | * @param arg2 the argument corresponding to the [NavType] of [t2]
106 | * @param arg3 the argument corresponding to the [NavType] of [t3]
107 | * @param arg4 the argument corresponding to the [NavType] of [t4]
108 | *
109 | * @return the [String] containing the route of the destination
110 | */
111 | public fun route(arg1: A1, arg2: A2, arg3: A3, arg4: A4): String
112 | }
113 |
114 | /** An interface that implements navigation with 5 parameters
115 | *
116 | * @property t1 the [NavType] for the first argument
117 | * @property t2 the [NavType] for the second argument
118 | * @property t3 the [NavType] for the third argument
119 | * @property t4 the [NavType] for the fourth argument
120 | * @property t5 the [NavType] for the fifth argument
121 | *
122 | */
123 | public interface TypedNavigationInterface5 : TypedNavigationInterface {
124 | public val t1: NavType
125 | public val t2: NavType
126 | public val t3: NavType
127 | public val t4: NavType
128 | public val t5: NavType
129 |
130 | /** Get the [String] route for the destination
131 | * @param arg1 the argument corresponding to the [NavType] of [t1]
132 | * @param arg2 the argument corresponding to the [NavType] of [t2]
133 | * @param arg3 the argument corresponding to the [NavType] of [t3]
134 | * @param arg4 the argument corresponding to the [NavType] of [t4]
135 | * @param arg5 the argument corresponding to the [NavType] of [t5]
136 | *
137 | * @return the [String] containing the route of the destination
138 | */
139 | public fun route(arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5): String
140 | }
141 |
--------------------------------------------------------------------------------
/typednavigation/src/main/java/com/xmartlabs/typednavigation/TypedNavigationInterfaceExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation
2 |
3 | import androidx.lifecycle.SavedStateHandle
4 |
5 | /**
6 | Executes a function with the attributes defined in the [TypedNavigationInterface1]
7 | *
8 | * @property savedStateHandle the [SavedStateHandle] to extract the arguments
9 | * @property function the function to execute
10 | *
11 | * @return an execution of the given function
12 | */
13 | public fun TypedNavigationInterface1.withAttributes(
14 | savedStateHandle: SavedStateHandle,
15 | function: (A) -> Unit
16 | ) {
17 | val arg1 = savedStateHandle.get("a1") as A
18 | function(arg1)
19 | }
20 |
21 | /**
22 | Executes a function with the attributes defined in the [TypedNavigationInterface2]
23 | *
24 | * @property savedStateHandle the [SavedStateHandle] to extract the arguments
25 | * @property function the function to execute
26 | *
27 | * @return an execution of the given function
28 | */
29 | public fun TypedNavigationInterface2.withAttributes(
30 | savedStateHandle: SavedStateHandle,
31 | function: (A1, A2) -> Unit
32 | ) {
33 | val arg1 = savedStateHandle.get("a1") as A1
34 | val arg2 = savedStateHandle.get("a2") as A2
35 | function(arg1, arg2)
36 | }
37 |
38 | /**
39 | Executes a function with the attributes defined in the [TypedNavigationInterface3]
40 | *
41 | * @property savedStateHandle the [SavedStateHandle] to extract the arguments
42 | * @property function the function to execute
43 | *
44 | * @return an execution of the given function
45 | */
46 | public fun TypedNavigationInterface3.withAttributes(
47 | savedStateHandle: SavedStateHandle,
48 | function: (A1, A2, A3) -> Unit
49 | ) {
50 | val arg1 = savedStateHandle.get("a1") as A1
51 | val arg2 = savedStateHandle.get("a2") as A2
52 | val arg3 = savedStateHandle.get("a3") as A3
53 | function(arg1, arg2, arg3)
54 | }
55 |
56 | /**
57 | Executes a function with the attributes defined in the [TypedNavigationInterface4]
58 | *
59 | * @property savedStateHandle the [SavedStateHandle] to extract the arguments
60 | * @property function the function to execute
61 | *
62 | * @return an execution of the given function
63 | */
64 | public fun TypedNavigationInterface4.withAttributes(
65 | savedStateHandle: SavedStateHandle,
66 | function: (A1, A2, A3, A4) -> Unit
67 | ) {
68 | val arg1 = savedStateHandle.get("a1") as A1
69 | val arg2 = savedStateHandle.get("a2") as A2
70 | val arg3 = savedStateHandle.get("a3") as A3
71 | val arg4 = savedStateHandle.get("a4") as A4
72 | function(arg1, arg2, arg3, arg4)
73 | }
74 |
75 | /**
76 | Executes a function with the attributes defined in the [TypedNavigationInterface5]
77 | *
78 | * @property savedStateHandle the [SavedStateHandle] to extract the arguments
79 | * @property function the function to execute
80 | *
81 | * @return an execution of the given function
82 | */
83 | public fun TypedNavigationInterface5.withAttributes(
84 | savedStateHandle: SavedStateHandle,
85 | function: (A1, A2, A3, A4, A5) -> Unit
86 | ) {
87 | val arg1 = savedStateHandle.get("a1") as A1
88 | val arg2 = savedStateHandle.get("a2") as A2
89 | val arg3 = savedStateHandle.get("a3") as A3
90 | val arg4 = savedStateHandle.get("a4") as A4
91 | val arg5 = savedStateHandle.get("a5") as A5
92 | function(arg1, arg2, arg3, arg4, arg5)
93 | }
94 |
--------------------------------------------------------------------------------
/typednavigation/src/test/java/com/xmartlabs/typednavigation/TestRouter.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation
2 |
3 | import androidx.navigation.NavType
4 |
5 | public object TestRouter {
6 | public val a0: TypedNavigation.E = TypedNavigation.E("example")
7 | public val a1: TypedNavigation.A1 = TypedNavigation.A1(
8 | "example1", NavType.NotNullString
9 | )
10 | public val a2: TypedNavigation.A2 =
11 | TypedNavigation.A2(
12 | "example2",
13 | NavType.StringType,
14 | NavType.StringType
15 | )
16 | public val a3: TypedNavigation.A3 =
17 | TypedNavigation.A3("example3", NavType.StringType, NavType.NullableBool, NavType.BoolType)
18 | public val a4: TypedNavigation.A4 =
19 | TypedNavigation.A4(
20 | "example4",
21 | NavType.StringType,
22 | NavType.NullableBool,
23 | NavType.BoolType,
24 | NavType.StringType
25 | )
26 | public val a5: TypedNavigation.A5 =
27 | TypedNavigation.A5(
28 | "example5",
29 | NavType.StringType,
30 | NavType.NullableBool,
31 | NavType.BoolType,
32 | NavType.BoolType,
33 | NavType.StringType
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/typednavigation/src/test/java/com/xmartlabs/typednavigation/UnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.xmartlabs.typednavigation
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | public class UnitTest {
12 | @Test
13 | public fun `urls are correct`() {
14 | assertEquals(TestRouter.a0.url, "${TestRouter.a0.name}/")
15 | assertEquals(TestRouter.a1.url, "${TestRouter.a1.name}/{a1}")
16 | assertEquals(TestRouter.a2.url, "${TestRouter.a2.name}/{a1}/{a2}")
17 | assertEquals(TestRouter.a3.url, "${TestRouter.a3.name}/{a1}/{a2}/{a3}")
18 | assertEquals(TestRouter.a4.url, "${TestRouter.a4.name}/{a1}/{a2}/{a3}/{a4}")
19 | assertEquals(TestRouter.a5.url, "${TestRouter.a5.name}/{a1}/{a2}/{a3}/{a4}/{a5}")
20 | }
21 |
22 | @Test
23 | public fun `routes are correct`() {
24 | val arg1 = "test1"
25 | val arg2 = "test2"
26 | val boolarg1 = true
27 | val boolarg2 = false
28 | assertEquals(TestRouter.a0.route(), "${TestRouter.a0.name}/")
29 | assertEquals(TestRouter.a1.route(arg1), "${TestRouter.a1.name}/$arg1")
30 | assertEquals(TestRouter.a2.route(arg1, arg2), "${TestRouter.a2.name}/$arg1/$arg2")
31 | assertEquals(TestRouter.a3.route(arg1, boolarg1, boolarg2), "${TestRouter.a3.name}/$arg1/$boolarg1/$boolarg2")
32 | assertEquals(
33 | TestRouter.a4.route(arg1, null, boolarg1, arg2),
34 | "${TestRouter.a4.name}/$arg1/null/$boolarg1/$arg2"
35 | )
36 | assertEquals(
37 | TestRouter.a5.route(arg1, null, boolarg1, boolarg2, arg1),
38 | "${TestRouter.a5.name}/$arg1/null/$boolarg1/$boolarg2/$arg1"
39 | )
40 | }
41 | }
--------------------------------------------------------------------------------