├── .gitignore
├── LICENSE
├── README.md
├── Tutorial1-1Basics
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── smarttoolfactory
│ │ └── tutorial1_1basics
│ │ └── ExampleInstrumentedTest.kt
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── smarttoolfactory
│ │ └── tutorial1_1basics
│ │ ├── Home.kt
│ │ ├── HomeViewModel.kt
│ │ ├── MainActivity.kt
│ │ ├── MainScreen.kt
│ │ ├── Search.kt
│ │ ├── SearchState.kt
│ │ ├── TutorialNavGraph.kt
│ │ ├── Utils.kt
│ │ ├── chapter1_basics
│ │ ├── Tutorial1_1ColumnRowBoxScreen.kt
│ │ └── Tutorial1_2ClickSurfacesAndClicks.kt
│ │ ├── chapter2_material_widgets
│ │ ├── ConstraintLayoutSample.kt
│ │ ├── LazyColumnItemSwapAnimation.kt
│ │ ├── MotionLayoutDSLSample.kt
│ │ ├── MotionLayoutJSonSample.kt
│ │ ├── Tutorial2_10_1BottomSheet.kt
│ │ ├── Tutorial2_10_2ModalBottomSheetLayout.kt
│ │ ├── Tutorial2_10_3BottomDrawer.kt
│ │ ├── Tutorial2_10_4BottomDrawer2.kt
│ │ ├── Tutorial2_10_5BackdropScaffold.kt
│ │ ├── Tutorial2_11SnackProgressSelection.kt
│ │ ├── Tutorial2_12AlertDialog.kt
│ │ ├── Tutorial2_13SwipeToDismiss.kt
│ │ ├── Tutorial2_14LazyColumnWithCheckBox.kt
│ │ ├── Tutorial2_15ChipAndTextLayout.kt
│ │ ├── Tutorial2_16_1HorizontalPager1.kt
│ │ ├── Tutorial2_16_2HorizontalPager2.kt
│ │ ├── Tutorial2_17_1PullToRefresh1.kt
│ │ ├── Tutorial2_17_2CustomPullToRefresh2.kt
│ │ ├── Tutorial2_1Text.kt
│ │ ├── Tutorial2_2Button.kt
│ │ ├── Tutorial2_3TextField.kt
│ │ ├── Tutorial2_4Image.kt
│ │ ├── Tutorial2_5_1LazyColumn1.kt
│ │ ├── Tutorial2_5_2LazyColumn2.kt
│ │ ├── Tutorial2_5_3LazyRow.kt
│ │ ├── Tutorial2_5_4StickyHeader.kt
│ │ ├── Tutorial2_5_5LazyVerticalGrid.kt
│ │ ├── Tutorial2_5_6LazyVerticalGrid2.kt
│ │ ├── Tutorial2_5_7ListItem.kt
│ │ ├── Tutorial2_5_8LazyListLayoutInfo.kt
│ │ ├── Tutorial2_6_1TopAppBar.kt
│ │ ├── Tutorial2_6_2Tab.kt
│ │ ├── Tutorial2_7BottomNavigation.kt
│ │ ├── Tutorial2_8BottomAppbar.kt
│ │ ├── Tutorial2_9_1SideNavigationScaffold.kt
│ │ ├── Tutorial2_9_2ModalDrawer.kt
│ │ ├── Tutorial2_9_3ModalDrawerScaffold.kt
│ │ └── Tutorial2_9_4ModalDrawerScaffold.kt
│ │ ├── chapter3_layout
│ │ ├── Bubble.kt
│ │ ├── CustomPaddingSamples.kt
│ │ ├── Tutorial3_10CenteringItem.kt
│ │ ├── Tutorial3_11Popup1.kt
│ │ ├── Tutorial3_11Popup2.kt
│ │ ├── Tutorial3_11ToolTip1.kt
│ │ ├── Tutorial3_11Tooltip2.kt
│ │ ├── Tutorial3_11TooltipBox1.kt
│ │ ├── Tutorial3_12LazyListLayoutComposeOffscreen.kt
│ │ ├── Tutorial3_1_1CustomModifier.kt
│ │ ├── Tutorial3_1_2OnGloballyPositionedModifier.kt
│ │ ├── Tutorial3_1_3OffsetGraphicsLayer.kt
│ │ ├── Tutorial3_1_4BoxWithConstraints.kt
│ │ ├── Tutorial3_1_5SizeModifiers.kt
│ │ ├── Tutorial3_1_6ChainSizeModifiers.kt
│ │ ├── Tutorial3_1_7WrapContentModifier.kt
│ │ ├── Tutorial3_1_8LayoutModifier.kt
│ │ ├── Tutorial3_2_0LayoutBasics.kt
│ │ ├── Tutorial3_2_10ConstraintsAndLayout.kt
│ │ ├── Tutorial3_2_1CustomLayout1.kt
│ │ ├── Tutorial3_2_2CustomLayout2.kt
│ │ ├── Tutorial3_2_3Constraints.kt
│ │ ├── Tutorial3_2_4ConstraintsBounds.kt
│ │ ├── Tutorial3_2_5InnerConstraints.kt
│ │ ├── Tutorial3_2_6SiblingConstraints.kt
│ │ ├── Tutorial3_2_7InfiniteConstraints.kt
│ │ ├── Tutorial3_2_8ConstrainAndOffset1.kt
│ │ ├── Tutorial3_2_9ConstrainAndOffset2.kt
│ │ ├── Tutorial3_3_1ScopeAndParentDataModifier.kt
│ │ ├── Tutorial3_3_3OnPlacedModifier1.kt
│ │ ├── Tutorial3_3_4OnPlacedModifier2.kt
│ │ ├── Tutorial3_3_5OnPlacedModifier3.kt
│ │ ├── Tutorial3_4LayoutIdModifier.kt
│ │ ├── Tutorial3_5SubcomposeLayout.kt
│ │ ├── Tutorial3_6_1CustomFlexLayout.kt
│ │ ├── Tutorial3_6_2SubComposeAndFlexLayout.kt
│ │ ├── Tutorial3_7_1CompositionLayoutPhases.kt
│ │ ├── Tutorial3_7_2CompositionLayoutPhases2.kt
│ │ ├── Tutorial3_7_3RememberMeasurePolicy.kt
│ │ ├── Tutorial3_8_1LookAheadLayout.kt
│ │ ├── Tutorial3_9VisbilityPercentage.kt
│ │ └── chat
│ │ │ ├── ChatInput.kt
│ │ │ ├── MessageStatus.kt
│ │ │ ├── QuotedMessage.kt
│ │ │ ├── ReceivedMessageRow.kt
│ │ │ ├── RecipientName.kt
│ │ │ ├── SentMessageRow.kt
│ │ │ └── widget
│ │ │ ├── ChatFlexBoxLayout.kt
│ │ │ └── ResizableColumn.kt
│ │ ├── chapter4_state
│ │ ├── BadgeComponent.kt
│ │ ├── BadgeState.kt
│ │ ├── LogCompositions.kt
│ │ ├── MaterialShadow.kt
│ │ ├── Tutorial4_10_1MovableContentOf1.kt
│ │ ├── Tutorial4_10_2MovableContentOf2.kt
│ │ ├── Tutorial4_10_3MoveableContentOf3.kt
│ │ ├── Tutorial4_11_1LazyListRecomposition1.kt
│ │ ├── Tutorial4_11_2LazyListrecomposition2.kt
│ │ ├── Tutorial4_11_3LazyListRecomposition3.kt
│ │ ├── Tutorial4_11_4LazyListRecomposition4.kt
│ │ ├── Tutorial4_11_5LazyListRecomposition5.kt
│ │ ├── Tutorial4_11_6LazyListRecomposition6.kt
│ │ ├── Tutorial4_12LazyListScrollDirection.kt
│ │ ├── Tutorial4_13AnimatingKeyEquality.kt
│ │ ├── Tutorial4_1_1RememberMutableState.kt
│ │ ├── Tutorial4_1_2SnapshotMutationPolicy.kt
│ │ ├── Tutorial4_2_1Recomposition.kt
│ │ ├── Tutorial4_2_2Recomposition2.kt
│ │ ├── Tutorial4_2_3ScopedRecomposition.kt
│ │ ├── Tutorial4_2_4Stability.kt
│ │ ├── Tutorial4_2_5ImmutableAnnotation.kt
│ │ ├── Tutorial4_2_6StableAnnotation.kt
│ │ ├── Tutorial4_2_7LambdaRecomposition.kt
│ │ ├── Tutorial4_2_8NonRestartableComposable.kt
│ │ ├── Tutorial4_3RememberKeys.kt
│ │ ├── Tutorial4_4CustomRemember.kt
│ │ ├── Tutorial4_5_1SideEffect.kt
│ │ ├── Tutorial4_5_2SideEffect2.kt
│ │ ├── Tutorial4_6ModifierRecomposition.kt
│ │ ├── Tutorial4_7_1ComposePhases1.kt
│ │ ├── Tutorial4_7_2ComposePhases2.kt
│ │ ├── Tutorial4_7_3ComposePhases3.kt
│ │ └── Tutorial4_9_1RememberObserver.kt
│ │ ├── chapter5_gesture
│ │ ├── ImageOffsetAndTouchTranslation.kt
│ │ ├── NestedScrollConnectionAndDispatcherSample.kt
│ │ ├── ToolbarSampleContent.kt
│ │ ├── Tutorial5_10_1DetectColorOnTouchPosition.kt
│ │ ├── Tutorial5_11ZoomableLazyColumn.kt
│ │ ├── Tutorial5_12SwipePagerToStart.kt
│ │ ├── Tutorial5_13_1RotateDragBox.kt
│ │ ├── Tutorial5_13_2RotateZoomDragBox.kt
│ │ ├── Tutorial5_14ImageBoundsRect.kt
│ │ ├── Tutorial5_15DragHorizontalPager.kt
│ │ ├── Tutorial5_16SafeThrottleClick.kt
│ │ ├── Tutorial5_1_1Clickable.kt
│ │ ├── Tutorial5_1_2InteractionSource1.kt
│ │ ├── Tutorial5_1_3InteractionSource2.kt
│ │ ├── Tutorial5_1_4LongClickButton.kt
│ │ ├── Tutorial5_2TapDragGesture.kt
│ │ ├── Tutorial5_3TransformGesture.kt
│ │ ├── Tutorial5_4_1AwaitPointerEventScope.kt
│ │ ├── Tutorial5_4_2AwaitPointertEvenScopeDrag.kt
│ │ ├── Tutorial5_4_3Calculations.kt
│ │ ├── Tutorial5_4_3LongPressCallbacks.kt
│ │ ├── Tutorial5_5_1CombinedGestures1.kt
│ │ ├── Tutorial5_6_1ConsumeChange.kt
│ │ ├── Tutorial5_6_2GesturePropagation1.kt
│ │ ├── Tutorial5_6_3GesturePropagation2.kt
│ │ ├── Tutorial5_6_4TransformPropagation.kt
│ │ ├── Tutorial5_6_5PointerEventPass1.kt
│ │ ├── Tutorial5_6_6PointerEventPass2.kt
│ │ ├── Tutorial5_6_7PointerEventPass3.kt
│ │ ├── Tutorial5_6_8PointerInputPass4.kt
│ │ ├── Tutorial5_6_9PointerEventPass5.kt
│ │ ├── Tutorial5_7_1GestureRipples.kt
│ │ ├── Tutorial5_8_1DragModifier.kt
│ │ ├── Tutorial5_8_2SwipeModifier.kt
│ │ ├── Tutorial5_9_1Scroll.kt
│ │ ├── Tutorial5_9_2NestedScroll1.kt
│ │ ├── Tutorial5_9_3NestedScroll2.kt
│ │ ├── Tutorial5_9_4NestedScroll3.kt
│ │ ├── Tutorial5_9_5DragableNestedScroll.kt
│ │ ├── Tutorial5_9_6CollapsingTopAppBar.kt
│ │ ├── Tutorial5_9_7CollapsingTopAppbar2.kt
│ │ └── gesture
│ │ │ ├── AwaitDragMotionModifier.kt
│ │ │ ├── AwaitPointerMotionEvent.kt
│ │ │ ├── DetectDragGestures.kt
│ │ │ ├── MotionEvent.kt
│ │ │ ├── PointerMotionModifier.kt
│ │ │ ├── TransformGesture.kt
│ │ │ ├── ZoomableImage.kt
│ │ │ ├── ZoomableState.kt
│ │ │ └── Zoomables.kt
│ │ ├── chapter6_graphics
│ │ ├── BlendModes.kt
│ │ ├── ParticlePhysics.kt
│ │ ├── PathUtils.kt
│ │ ├── PieChartLabels.kt
│ │ ├── PolygonLapsDrawable.kt
│ │ ├── RippleAnimation.kt
│ │ ├── Tutorial40_2RenderEffect1.kt
│ │ ├── Tutorial6_10RippleOnCanvas.kt
│ │ ├── Tutorial6_11EraseProgress.kt
│ │ ├── Tutorial6_12RotatedLabel.kt
│ │ ├── Tutorial6_13BorderProgressTimer.kt
│ │ ├── Tutorial6_14PieChart.kt
│ │ ├── Tutorial6_15AnimatedPieChart.kt
│ │ ├── Tutorial6_16CustomSegmentedBorder.kt
│ │ ├── Tutorial6_17AnimatedRainbowBorder.kt
│ │ ├── Tutorial6_18ShimmerEffect.kt
│ │ ├── Tutorial6_19CustomCarousel.kt
│ │ ├── Tutorial6_1_1CanvasBasics.kt
│ │ ├── Tutorial6_1_2CanvasBasics2.kt
│ │ ├── Tutorial6_1_3CanvasPaths.kt
│ │ ├── Tutorial6_1_4CanvasPathOperations.kt
│ │ ├── Tutorial6_1_5CanvasPathSegments.kt
│ │ ├── Tutorial6_1_6CanvasPathEffect.kt
│ │ ├── Tutorial6_1_7PathMeasure.kt
│ │ ├── Tutorial6_1_8CanvasStrokeWidth.kt
│ │ ├── Tutorial6_20PathParserAndSegment.kt
│ │ ├── Tutorial6_21ConstantVelocityAnimation.kt
│ │ ├── Tutorial6_22ArcFillClock.kt
│ │ ├── Tutorial6_23TabAnimation.kt
│ │ ├── Tutorial6_24ProjectionLerping.kt
│ │ ├── Tutorial6_25BeforeAfterLayout.kt
│ │ ├── Tutorial6_26_AnimatedCountdownTimer.kt
│ │ ├── Tutorial6_27rememberVectorPainter.kt
│ │ ├── Tutorial6_28ComplexPathTouchPosition.kt
│ │ ├── Tutorial6_29ToolTipPieChart.kt
│ │ ├── Tutorial6_2BlendMode1.kt
│ │ ├── Tutorial6_2BlendMode2ActualCanvas.kt
│ │ ├── Tutorial6_2BlendMode3Clipping.kt
│ │ ├── Tutorial6_2BlendMode4.kt
│ │ ├── Tutorial6_30LinearInterpolation.kt
│ │ ├── Tutorial6_31EditPolygonPoints1.kt
│ │ ├── Tutorial6_32EdgeFadeEffect.kt
│ │ ├── Tutorial6_33StoppableInfiniteAnimation.kt
│ │ ├── Tutorial6_35MultiColoredLineChart.kt
│ │ ├── Tutorial6_36InnerShadow.kt
│ │ ├── Tutorial6_37CombineShapes.kt
│ │ ├── Tutorial6_38ArcSlider.kt
│ │ ├── Tutorial6_39_1GraphicsLayer1.kt
│ │ ├── Tutorial6_3_1ColorFilter1.kt
│ │ ├── Tutorial6_40_1RenderScript1.kt
│ │ ├── Tutorial6_4_0CanvasTouchGestures.kt
│ │ ├── Tutorial6_4_1DrawWithTouch.kt
│ │ ├── Tutorial6_4_2DrawWithTouch2.kt
│ │ ├── Tutorial6_4_3CanvasTouchPositions.kt
│ │ ├── Tutorial6_5ColorPicker.kt
│ │ ├── Tutorial6_6EditBox.kt
│ │ ├── Tutorial6_7GooeyEffect.kt
│ │ ├── Tutorial6_8_1CutoutShape1.kt
│ │ ├── Tutorial6_9_1NeonEffect.kt
│ │ ├── Ui.kt
│ │ ├── chart
│ │ │ └── PieChart.kt
│ │ ├── colorpicker
│ │ │ ├── ColorPickerWheel.kt
│ │ │ ├── ColorfulSlider.kt
│ │ │ └── SaturationRhombus.kt
│ │ └── editbox
│ │ │ ├── DimensionSubcomposeLayout.kt
│ │ │ ├── ScaleEditBox.kt
│ │ │ ├── TouchRegion.kt
│ │ │ └── Util.kt
│ │ ├── chapter7_theming
│ │ └── Tutorial7_2_1CompositionLocal.kt
│ │ ├── chapter8_semantics
│ │ ├── Tutorial8_1ClickableLabels.kt
│ │ ├── Tutorial8_2CustomMerging.kt
│ │ └── UndoRedoSample.kt
│ │ ├── chapter9_animation
│ │ ├── AnimatedContentSample.kt
│ │ ├── AnimatedVisibilityTransitionStateTest.kt
│ │ ├── Easing.kt
│ │ ├── HorizontalPagerItemDeleteAnimation.kt
│ │ ├── LazyRowSnapAndDeleteAnimation.kt
│ │ ├── MutexAnimatableSample.kt
│ │ ├── ParticleAnimations.kt
│ │ ├── SharedElementTransition1.kt
│ │ ├── SharedElementTransition2.kt
│ │ ├── SharedElementTransition3AnimatedVisibility.kt
│ │ ├── SharedElementTransition3AnimatedVisibilityBlur.kt
│ │ ├── SharedElementTransition4ManuallyManaged.kt
│ │ ├── SharedElementTransition5BoundsTransform.kt
│ │ ├── SharedElementTransition5Clipping.kt
│ │ ├── SharedElementTransition5ResizeMode.kt
│ │ ├── SharedElementTransition5SkipLookAheadSize.kt
│ │ ├── SharedElementTransition6RenderOverlay.kt
│ │ ├── SharedElementTransition7PlaceHolderSize.kt
│ │ ├── SharedElementTransition8Navigation.kt
│ │ ├── SharedElementTransition9Pager.kt
│ │ ├── SharedElementTransition9SheetToScreen.kt
│ │ └── SharedElementTransition9SheetToScreen2.kt
│ │ ├── model
│ │ ├── Place.kt
│ │ ├── Snack.kt
│ │ ├── SuggestionModel.kt
│ │ ├── TopAppbarMenuOverflow.kt
│ │ └── TutorialSectionModel.kt
│ │ ├── tutorial_list
│ │ ├── Tags.kt
│ │ └── TutorialList.kt
│ │ └── ui
│ │ ├── Color.kt
│ │ ├── IconButton.kt
│ │ ├── Shape.kt
│ │ ├── Theme.kt
│ │ ├── Type.kt
│ │ └── components
│ │ ├── CustomChip.kt
│ │ ├── DrawerButton.kt
│ │ ├── FullWidthComponents.kt
│ │ ├── JumpToTopButton.kt
│ │ ├── PlaceCard.kt
│ │ ├── RandomColor.kt
│ │ ├── SnackCard.kt
│ │ ├── StaggeredGrid.kt
│ │ ├── TutorialSectionCard.kt
│ │ └── TutorialTexts.kt
│ └── res
│ ├── drawable-nodpi
│ ├── drawer_bg.jpeg
│ ├── drawer_bg2.jpeg
│ ├── landscape11.jpg
│ ├── placeholder.jpg
│ └── under_construction.jpg
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ ├── avatar_1_raster.png
│ ├── avatar_2_raster.png
│ ├── avatar_3_raster.png
│ ├── avatar_4_raster.png
│ ├── avatar_5_raster.png
│ ├── avatar_6_raster.png
│ ├── composite_dst.png
│ ├── composite_src.png
│ ├── cupcake.jpg
│ ├── dest.png
│ ├── donut.jpeg
│ ├── dots_transparent.png
│ ├── eclair.jpeg
│ ├── froyo.jpeg
│ ├── gingerbread.jpg
│ ├── honeycomb.jpg
│ ├── ic_baseline_deblur_24.xml
│ ├── ic_baseline_location_on_48.xml
│ ├── ic_baseline_star_12.xml
│ ├── ic_eraser_black_24dp.xml
│ ├── ic_launcher_background.xml
│ ├── ic_launcher_foreground.xml
│ ├── image_before_after_shoes_a.jpeg
│ ├── image_before_after_shoes_b.jpeg
│ ├── landscape1.jpg
│ ├── landscape10.jpg
│ ├── landscape2.jpg
│ ├── landscape3.jpg
│ ├── landscape4.jpg
│ ├── landscape5.jpg
│ ├── landscape6.jpg
│ ├── landscape7.jpg
│ ├── landscape8.jpg
│ ├── landscape9.jpg
│ ├── source.png
│ ├── tg_icon.png
│ ├── tip.xml
│ ├── vd_account_active.xml
│ ├── vd_account_outlined.xml
│ ├── vd_activity1_1heart.xml
│ ├── vd_clock_alarm.xml
│ ├── vd_clock_clock.xml
│ ├── vd_clock_stopwatch.xml
│ ├── vd_dashboard_active.xml
│ ├── vd_dashboard_outlined.xml
│ ├── vd_heart_empty.xml
│ ├── vd_heart_filled.xml
│ ├── vd_home_active.xml
│ ├── vd_home_outlined.xml
│ ├── vd_notification_active.xml
│ ├── vd_notification_outlined.xml
│ └── vd_profile.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── values-night
│ └── themes.xml
│ └── values
│ ├── colors.xml
│ ├── strings.xml
│ └── themes.xml
├── Tutorial2-1Unit-Testing
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── smarttoolfactory
│ │ │ └── tutorial2_1unit_testing
│ │ │ ├── MainActivity.kt
│ │ │ └── ui
│ │ │ └── theme
│ │ │ ├── Color.kt
│ │ │ ├── Theme.kt
│ │ │ └── Type.kt
│ └── res
│ │ ├── drawable
│ │ ├── ic_launcher_background.xml
│ │ └── ic_launcher_foreground.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── com
│ └── smarttoolfactory
│ └── tutorial2_1unit_testing
│ ├── coroutines
│ ├── CoroutinesTest1.kt
│ ├── CoroutinesTest2.kt
│ ├── CoroutinesTest3.kt
│ ├── CoroutinesViewModel.kt
│ ├── MainDispatcherExtension.kt
│ ├── MainDispatcherRule.kt
│ └── UserRepository.kt
│ └── mockk
│ ├── CalculationViewModel.kt
│ ├── CalculationViewModelTest.kt
│ ├── CaptureTest.kt
│ ├── Test10VerifyAtLeastAtMostExactly.kt
│ ├── Test11VerificationOrder.kt
│ ├── Test12VerificationConfirmation.kt
│ ├── Test13RecordingExclusions.kt
│ ├── Test14VerificationTimeout.kt
│ ├── Test15PrivateMethodsAndProperties.kt
│ ├── Test15ReturnUnit.kt
│ ├── Test1ExtendWith.kt
│ ├── Test1WithInit.kt
│ ├── Test1WithMockK.kt
│ ├── Test2ObjectMockK.kt
│ ├── Test3RelaxedMockK.kt
│ ├── Test4MockClassEnumConstructor.kt
│ ├── Test5Spy.kt
│ ├── Test6HierarchicalMocking.kt
│ ├── Test6HierarchicalMocking2.kt
│ ├── Test7PartialArgumentMatching.kt
│ ├── Test8ChainedCalls.kt
│ ├── Test9CapturingParams.kt
│ ├── car
│ └── Car.kt
│ └── model_math_application
│ ├── CalculatorService.kt
│ └── MathApplication.kt
├── Tutorial2-2UI-Testing
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── smarttoolfactory
│ │ └── tutorial2_2ui_testing
│ │ ├── ExampleInstrumentedTest.kt
│ │ ├── Test1InspectingTrees.kt
│ │ └── Test2TextField.kt
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── smarttoolfactory
│ │ └── tutorial2_2ui_testing
│ │ ├── MainActivity.kt
│ │ └── ui
│ │ └── theme
│ │ ├── Color.kt
│ │ ├── Theme.kt
│ │ └── Type.kt
│ └── res
│ ├── drawable
│ ├── ic_launcher_background.xml
│ └── ic_launcher_foreground.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.webp
│ └── ic_launcher_round.webp
│ ├── mipmap-mdpi
│ ├── ic_launcher.webp
│ └── ic_launcher_round.webp
│ ├── mipmap-xhdpi
│ ├── ic_launcher.webp
│ └── ic_launcher_round.webp
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.webp
│ └── ic_launcher_round.webp
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.webp
│ └── ic_launcher_round.webp
│ └── values
│ ├── colors.xml
│ ├── strings.xml
│ └── themes.xml
├── Tutorial3-1Navigation
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── smarttoolfactory
│ │ └── tutorial3_1navigation
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── smarttoolfactory
│ │ │ └── tutorial3_1navigation
│ │ │ ├── ExposedSectionMenu.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── NavContoller.kt
│ │ │ ├── NavigateBackWithDrag.kt
│ │ │ ├── NavigationApplication.kt
│ │ │ ├── Routes.kt
│ │ │ ├── Tutorial1TypeSafeNavigation.kt
│ │ │ ├── Tutorial2_1PopUpToAndLaunchSingleTop.kt
│ │ │ ├── Tutorial2_2PopBackStackRoutePatterns.kt
│ │ │ ├── Tutorial2_3PopBackStackWithResult.kt
│ │ │ ├── Tutorial3_1MultipleBackStack.kt
│ │ │ ├── Tutorial3_2BottomNavigationBasics.kt
│ │ │ ├── Tutorial3_3BottomNavigationNestedNavigation.kt
│ │ │ ├── Tutorial4_1Deeplink.kt
│ │ │ ├── Tutorial4_2DeeplinkQueryParams.kt
│ │ │ ├── Tutorial5_1ScopedViewModel.kt
│ │ │ ├── Tutorial5_2MultipleBackStackWithViewModel.kt
│ │ │ ├── Tutorial6_1ConditionalNavigation.kt
│ │ │ ├── Tutorial7_1CustomTypes.kt
│ │ │ ├── Tutorial8_1NavControllerCreateGraph.kt
│ │ │ ├── Tutorial8_2DynamicNavGraphDeeplink.kt
│ │ │ ├── Tutorial9_1DialogNavigation.kt
│ │ │ ├── Util.kt
│ │ │ └── ui
│ │ │ └── theme
│ │ │ ├── Color.kt
│ │ │ ├── Theme.kt
│ │ │ └── Type.kt
│ └── res
│ │ ├── drawable
│ │ ├── ic_launcher_background.xml
│ │ └── ic_launcher_foreground.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── com
│ └── smarttoolfactory
│ └── tutorial3_1navigation
│ └── ExampleUnitTest.kt
├── build.gradle.kts
├── gradle.properties
├── gradle
└── libs.versions.toml
├── gradlew
├── gradlew.bat
├── screenshots
├── intro.gif
├── tutorial-list.jpg
├── tutorial1_1.jpg
├── tutorial1_2.jpg
├── tutorial2_1.jpg
├── tutorial2_10.jpeg
├── tutorial2_10_4.jpeg
├── tutorial2_10_5.jpeg
├── tutorial2_11.gif
├── tutorial2_12.gif
├── tutorial2_2.jpg
├── tutorial2_3.jpg
├── tutorial2_4.gif
├── tutorial2_5.gif
├── tutorial2_6.gif
├── tutorial2_7.jpeg
├── tutorial2_8.jpeg
├── tutorial2_9.jpeg
├── tutorial2_9_2.jpeg
├── tutorial3_10.gif
├── tutorial3_1_1.png
├── tutorial3_1_2.png
├── tutorial3_1_3.gif
├── tutorial3_1_6.png
├── tutorial3_1_7.png
├── tutorial3_1_8.png
├── tutorial3_2_1.png
├── tutorial3_2_10.gif
├── tutorial3_3_1.png
├── tutorial3_4.png
├── tutorial3_5.gif
├── tutorial3_6_1.gif
├── tutorial3_6_2.gif
├── tutorial3_9.gif
├── tutorial4_12.gif
├── tutorial4_1_1.png
├── tutorial4_2_3.png
├── tutorial4_4.gif
├── tutorial4_5_1.png
├── tutorial4_5_2.png
├── tutorial4_7_3.gif
├── tutorial5_10_1.gif
├── tutorial5_11.gif
├── tutorial5_1_1.gif
├── tutorial5_1_2.gif
├── tutorial5_1_3.gif
├── tutorial5_2.gif
├── tutorial5_3.gif
├── tutorial5_4_1.gif
├── tutorial5_4_3.gif
├── tutorial5_6_2.gif
├── tutorial5_6_4.gif
├── tutorial5_9_6.gif
├── tutorial5_9_7.gif
├── tutorial6_11.gif
├── tutorial6_12.gif
├── tutorial6_13.gif
├── tutorial6_14.png
├── tutorial6_15.gif
├── tutorial6_17.gif
├── tutorial6_1_1.gif
├── tutorial6_1_2.gif
├── tutorial6_1_3.gif
├── tutorial6_1_4.gif
├── tutorial6_1_5.gif
├── tutorial6_1_6.gif
├── tutorial6_20.gif
├── tutorial6_21.gif
├── tutorial6_23.gif
├── tutorial6_24.gif
├── tutorial6_25.gif
├── tutorial6_2_1.gif
├── tutorial6_2_3.gif
├── tutorial6_4_2.gif
├── tutorial6_5.gif
├── tutorial6_6.gif
├── tutorial6_7.gif
├── tutorial6_8_1.png
└── tutorial6_9_1.gif
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/*
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | .cxx
10 | local.properties
11 | gradle/wrapper
--------------------------------------------------------------------------------
/Tutorial1-1Basics/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/Tutorial1-1Basics/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/androidTest/java/com/smarttoolfactory/tutorial1_1basics/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.smarttoolfactory.tutorial1_1basics", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
13 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/HomeViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics
2 |
3 |
4 | import androidx.lifecycle.ViewModel
5 | import com.smarttoolfactory.tutorial1_1basics.model.SuggestionModel
6 | import com.smarttoolfactory.tutorial1_1basics.model.TutorialSectionModel
7 |
8 | class HomeViewModel : ViewModel() {
9 |
10 |
11 | val tutorialList = mutableListOf>()
12 |
13 |
14 | fun getTutorials(query: String): List {
15 |
16 | val filteredList = linkedSetOf()
17 |
18 | tutorialList.forEach { list: List ->
19 |
20 | list.forEach { tutorialSectionModel ->
21 |
22 | if (tutorialSectionModel.title.contains(query, ignoreCase = true)) {
23 | filteredList.add(tutorialSectionModel)
24 | }
25 |
26 | if (tutorialSectionModel.description.contains(query, ignoreCase = true)) {
27 | filteredList.add(tutorialSectionModel)
28 | }
29 |
30 | tutorialSectionModel.tags.forEach {
31 | if (it.contains(query, ignoreCase = true)) {
32 | filteredList.add(tutorialSectionModel)
33 | }
34 | }
35 | }
36 | }
37 |
38 | // println("🤖 ViewModel Query: $query, filteredList: ${filteredList.size}")
39 |
40 | return filteredList.toList()
41 | }
42 | }
43 |
44 |
45 | val suggestionList = listOf(
46 | SuggestionModel("Modifier"),
47 | SuggestionModel("Row"),
48 | SuggestionModel("Column"),
49 | SuggestionModel("BottomSheet"),
50 | SuggestionModel("Dialog"),
51 | SuggestionModel("Checkbox"),
52 | SuggestionModel("Layout"),
53 | SuggestionModel("Modifier"),
54 | SuggestionModel("SubcomposeLayout"),
55 | SuggestionModel("Recomposition"),
56 | SuggestionModel("SideEffect"),
57 | SuggestionModel("PointerInput"),
58 | SuggestionModel("AwaitPointerEventScope"),
59 | SuggestionModel("Gesture"),
60 | SuggestionModel("Drag"),
61 | SuggestionModel("Transform"),
62 | SuggestionModel("Canvas"),
63 | SuggestionModel("DrawScope"),
64 | SuggestionModel("Path"),
65 | SuggestionModel("PathEffect"),
66 | SuggestionModel("PathOperation"),
67 | SuggestionModel("Blend Mode"),
68 | )
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics
2 |
3 | import android.os.Bundle
4 | import androidx.activity.compose.setContent
5 | import androidx.appcompat.app.AppCompatActivity
6 | import androidx.compose.animation.ExperimentalAnimationApi
7 | import androidx.compose.foundation.ExperimentalFoundationApi
8 | import androidx.compose.material.ExperimentalMaterialApi
9 | import androidx.compose.ui.ExperimentalComposeUiApi
10 | import androidx.core.view.WindowCompat
11 | import androidx.navigation.NavController
12 | import com.smarttoolfactory.tutorial1_1basics.ui.ComposeTutorialsTheme
13 |
14 | /**
15 | * This is the single and only Activity that contains Composable Tutorial list.
16 | *
17 | * * Tutorial navigation is done via [NavController] and ```composable``` extension function
18 | * for [NavGraphBuilder]
19 | */
20 | @ExperimentalMaterialApi
21 | @ExperimentalAnimationApi
22 | @ExperimentalFoundationApi
23 | @ExperimentalComposeUiApi
24 | class MainActivity : AppCompatActivity() {
25 |
26 | override fun onCreate(savedInstanceState: Bundle?) {
27 | super.onCreate(savedInstanceState)
28 |
29 | WindowCompat.setDecorFitsSystemWindows(window, false)
30 |
31 | setContent {
32 | ComposeTutorialsTheme {
33 | MainScreen()
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/MainScreen.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics
2 |
3 | import androidx.compose.animation.ExperimentalAnimationApi
4 | import androidx.compose.foundation.ExperimentalFoundationApi
5 | import androidx.compose.foundation.layout.PaddingValues
6 | import androidx.compose.foundation.layout.padding
7 | import androidx.compose.material.ExperimentalMaterialApi
8 | import androidx.compose.material.Scaffold
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.ui.ExperimentalComposeUiApi
11 | import androidx.compose.ui.Modifier
12 |
13 | @ExperimentalMaterialApi
14 | @ExperimentalAnimationApi
15 | @ExperimentalComposeUiApi
16 | @ExperimentalFoundationApi
17 | @Composable
18 | fun MainScreen() {
19 | Scaffold { paddingValues: PaddingValues ->
20 | TutorialNavGraph(modifier = Modifier.padding(paddingValues))
21 | }
22 | }
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.Stable
5 | import androidx.compose.ui.platform.LocalInspectionMode
6 |
7 | @Stable
8 | val isInPreview @Composable get() = LocalInspectionMode.current
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/chapter2_material_widgets/MotionLayoutDSLSample.kt:
--------------------------------------------------------------------------------
1 | @file:OptIn(ExperimentalMotionApi::class)
2 |
3 | package com.smarttoolfactory.tutorial1_1basics.chapter2_material_widgets
4 |
5 | import androidx.compose.foundation.layout.fillMaxSize
6 | import androidx.compose.material.Text
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.layout.layoutId
10 | import androidx.compose.ui.tooling.preview.Preview
11 | import androidx.constraintlayout.compose.ExperimentalMotionApi
12 | import androidx.constraintlayout.compose.MotionLayout
13 | import androidx.constraintlayout.compose.MotionScene
14 | import androidx.constraintlayout.compose.OnSwipe
15 | import androidx.constraintlayout.compose.SwipeDirection
16 | import androidx.constraintlayout.compose.SwipeMode
17 | import androidx.constraintlayout.compose.SwipeSide
18 | import androidx.constraintlayout.compose.SwipeTouchUp
19 |
20 | @Preview(showBackground = true)
21 | @Composable
22 | private fun Test() {
23 | MotionLayout(
24 | MotionScene { // this: MotionSceneScope
25 | val textRef = createRefFor("text")
26 | defaultTransition(
27 | from = constraintSet { // this: ConstraintSetScope
28 | constrain(textRef) { // this: ConstrainScope
29 | bottom.linkTo(parent.bottom)
30 | start.linkTo(parent.start)
31 | }
32 | },
33 | to = constraintSet { // this: ConstraintSetScope
34 | constrain(textRef) { // this: ConstrainScope
35 | top.linkTo(parent.top)
36 | end.linkTo(parent.end)
37 | }
38 | }
39 | ) { // this: TransitionScope
40 | onSwipe = OnSwipe(
41 | anchor = textRef,
42 | side = SwipeSide.End,
43 | direction = SwipeDirection.End,
44 | mode = SwipeMode.Spring,
45 | onTouchUp = SwipeTouchUp.AutoComplete
46 | )
47 | }
48 | },
49 | progress = 0f,
50 | Modifier.fillMaxSize()
51 | ) {
52 | Text("Hello, World", Modifier.layoutId("text"))
53 | }
54 | }
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/chapter2_material_widgets/Tutorial2_5_1LazyColumn1.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.chapter2_material_widgets
2 |
3 | import androidx.compose.foundation.layout.Arrangement
4 | import androidx.compose.foundation.layout.PaddingValues
5 | import androidx.compose.foundation.lazy.LazyColumn
6 | import androidx.compose.foundation.lazy.items
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.tooling.preview.Preview
9 | import androidx.compose.ui.unit.dp
10 | import com.smarttoolfactory.tutorial1_1basics.model.Snack
11 | import com.smarttoolfactory.tutorial1_1basics.model.snacks
12 | import com.smarttoolfactory.tutorial1_1basics.ui.components.SnackCard
13 |
14 | @Preview
15 | @Composable
16 | fun Tutorial2_5Screen1() {
17 | TutorialContent()
18 | }
19 |
20 | @Composable
21 | private fun TutorialContent() {
22 | LazyColumn(
23 | contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp),
24 | verticalArrangement = Arrangement.spacedBy(8.dp),
25 | content = {
26 | items(snacks) { item: Snack ->
27 | SnackCard(snack = item)
28 | }
29 | }
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/chapter2_material_widgets/Tutorial2_5_4StickyHeader.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.chapter2_material_widgets
2 |
3 | import androidx.compose.foundation.ExperimentalFoundationApi
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.layout.Arrangement
6 | import androidx.compose.foundation.layout.PaddingValues
7 | import androidx.compose.foundation.layout.fillMaxWidth
8 | import androidx.compose.foundation.layout.padding
9 | import androidx.compose.foundation.lazy.LazyColumn
10 | import androidx.compose.foundation.lazy.items
11 | import androidx.compose.material.Text
12 | import androidx.compose.runtime.Composable
13 | import androidx.compose.ui.Modifier
14 | import androidx.compose.ui.graphics.Color
15 | import androidx.compose.ui.tooling.preview.Preview
16 | import androidx.compose.ui.unit.dp
17 | import androidx.compose.ui.unit.sp
18 | import com.smarttoolfactory.tutorial1_1basics.model.Snack
19 | import com.smarttoolfactory.tutorial1_1basics.model.snacksOrdered
20 | import com.smarttoolfactory.tutorial1_1basics.ui.components.SnackCard
21 |
22 | @ExperimentalFoundationApi
23 | @Preview
24 | @Composable
25 | fun Tutorial2_5Screen4() {
26 | TutorialContent()
27 | }
28 |
29 | @ExperimentalFoundationApi
30 | @Composable
31 | private fun TutorialContent() {
32 | val grouped: Map> = snacksOrdered.groupBy { it.name[0] }
33 |
34 | LazyColumn(
35 | contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp),
36 | verticalArrangement = Arrangement.spacedBy(8.dp),
37 | content = {
38 |
39 | grouped.forEach { (initial: Char, snacks: List) ->
40 |
41 | stickyHeader {
42 | SnackHeader(initial)
43 | }
44 |
45 | items(snacks) { item: Snack ->
46 | SnackCard(snack = item)
47 | }
48 | }
49 | })
50 | }
51 |
52 | @Composable
53 | private fun SnackHeader(initial: Char) {
54 | Text(
55 | text = "$initial",
56 | fontSize = 24.sp,
57 | color = Color(0xff039BE5),
58 | modifier = Modifier
59 | .background(Color(0xffE3F2FD))
60 | .fillMaxWidth()
61 | .padding(horizontal = 12.dp,vertical = 8.dp)
62 | )
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/chapter2_material_widgets/Tutorial2_5_5LazyVerticalGrid.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.foundation.background
2 | import androidx.compose.foundation.layout.PaddingValues
3 | import androidx.compose.foundation.layout.fillMaxSize
4 | import androidx.compose.foundation.lazy.grid.GridCells
5 | import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
6 | import androidx.compose.foundation.lazy.grid.items
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.tooling.preview.Preview
10 | import androidx.compose.ui.unit.dp
11 | import com.smarttoolfactory.tutorial1_1basics.model.Snack
12 | import com.smarttoolfactory.tutorial1_1basics.model.snacks
13 | import com.smarttoolfactory.tutorial1_1basics.ui.backgroundColor
14 | import com.smarttoolfactory.tutorial1_1basics.ui.components.GridSnackCard
15 |
16 | @Preview
17 | @Composable
18 | fun Tutorial2_5Screen5() {
19 | TutorialContent()
20 | }
21 |
22 | @Composable
23 | private fun TutorialContent() {
24 | LazyVerticalGrid(
25 | contentPadding = PaddingValues(12.dp),
26 | modifier = Modifier
27 | .fillMaxSize()
28 | .background(backgroundColor),
29 | columns = GridCells.Fixed(3),
30 | content = {
31 | items(snacks) { snack: Snack ->
32 | GridSnackCard(snack = snack)
33 | }
34 | }
35 | )
36 | }
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/chapter3_layout/chat/MessageStatus.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.chapter3_layout.chat
2 |
3 | import androidx.compose.foundation.layout.Row
4 | import androidx.compose.foundation.layout.padding
5 | import androidx.compose.foundation.layout.size
6 | import androidx.compose.material.ContentAlpha
7 | import androidx.compose.material.Icon
8 | import androidx.compose.material.LocalContentAlpha
9 | import androidx.compose.material.Text
10 | import androidx.compose.material.icons.Icons
11 | import androidx.compose.material.icons.filled.AccessTime
12 | import androidx.compose.material.icons.filled.Done
13 | import androidx.compose.material.icons.filled.DoneAll
14 | import androidx.compose.runtime.Composable
15 | import androidx.compose.runtime.CompositionLocalProvider
16 | import androidx.compose.runtime.remember
17 | import androidx.compose.ui.Alignment
18 | import androidx.compose.ui.Modifier
19 | import androidx.compose.ui.graphics.Color
20 | import androidx.compose.ui.unit.dp
21 | import androidx.compose.ui.unit.sp
22 |
23 | @Composable
24 | fun MessageTimeText(
25 | modifier: Modifier = Modifier,
26 | messageTime: String,
27 | messageStatus: MessageStatus
28 | ) {
29 | val messageStat = remember {
30 | messageStatus
31 | }
32 |
33 | Row(
34 | modifier = modifier,
35 | verticalAlignment = Alignment.CenterVertically
36 | ) {
37 |
38 | CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
39 | Text(
40 | modifier = Modifier
41 | .padding(top = 1.dp, bottom = 1.dp),
42 | text = messageTime,
43 | fontSize = 12.sp
44 | )
45 | }
46 |
47 | Icon(
48 | modifier = Modifier
49 | .size(16.dp, 12.dp)
50 | .padding(start = 4.dp),
51 | imageVector = when (messageStat) {
52 | MessageStatus.PENDING -> {
53 | Icons.Default.AccessTime
54 | }
55 | MessageStatus.RECEIVED -> {
56 | Icons.Default.Done
57 | }
58 | else -> Icons.Default.DoneAll
59 | },
60 | tint = if (messageStat == MessageStatus.READ) Color(0xff0288D1)
61 | else Color(0xff424242),
62 | contentDescription = "messageStatus"
63 | )
64 |
65 | }
66 | }
67 |
68 | enum class MessageStatus {
69 | PENDING, RECEIVED, READ
70 | }
71 |
72 | data class ChatMessage(
73 | val id: Long,
74 | var message: String,
75 | var date: Long
76 | )
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/chapter3_layout/chat/RecipientName.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.chapter3_layout.chat
2 |
3 | import androidx.compose.foundation.clickable
4 | import androidx.compose.foundation.layout.Row
5 | import androidx.compose.foundation.layout.padding
6 | import androidx.compose.material.Text
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Alignment
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.graphics.Color
11 | import androidx.compose.ui.text.font.FontWeight
12 | import androidx.compose.ui.text.style.TextOverflow
13 | import androidx.compose.ui.unit.dp
14 | import androidx.compose.ui.unit.sp
15 | import com.smarttoolfactory.tutorial1_1basics.ui.RecipientAltNameColor
16 |
17 | @Composable
18 | fun RecipientName(
19 | modifier: Modifier = Modifier,
20 | name: String,
21 | isName: Boolean = true,
22 | altName: String? = null,
23 | color: Color = Color.Red,
24 | onClick: ((String) -> Unit)? = null
25 | ) {
26 | Row(
27 | modifier = modifier
28 | .clickable {
29 | onClick?.invoke(name)
30 | }
31 | .padding(start = 4.dp, top = 2.dp, end = 8.dp),
32 | verticalAlignment = Alignment.Top
33 | ) {
34 | Text(
35 | modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp),
36 | text = name,
37 | color = color,
38 | fontSize = 15.sp,
39 | maxLines = 1,
40 | letterSpacing = 1.sp,
41 | fontWeight = FontWeight.Bold,
42 | overflow = TextOverflow.Ellipsis
43 | )
44 | if (!isName && altName != null) {
45 | Text(
46 | modifier = Modifier.padding(vertical = 4.dp),
47 | text = "~$altName",
48 | fontSize = 12.sp,
49 | color = RecipientAltNameColor
50 | )
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/chapter4_state/LogCompositions.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.chapter4_state
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.SideEffect
5 | import androidx.compose.runtime.remember
6 |
7 | class Ref(var value: Int)
8 |
9 | // Note the inline function below which ensures that this function is essentially
10 | // copied at the call site to ensure that its logging only recompositions from the
11 | // original call site.
12 | @Composable
13 | inline fun LogCompositions(msg: String) {
14 | val ref = remember { Ref(0) }
15 | SideEffect { ref.value++ }
16 | println("$msg, recomposition: ${ref.value}")
17 | }
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/chapter4_state/Tutorial4_9_1RememberObserver.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.chapter4_state
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.fillMaxSize
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.padding
7 | import androidx.compose.foundation.rememberScrollState
8 | import androidx.compose.foundation.verticalScroll
9 | import androidx.compose.material.Button
10 | import androidx.compose.material.Text
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.runtime.RememberObserver
13 | import androidx.compose.runtime.getValue
14 | import androidx.compose.runtime.mutableIntStateOf
15 | import androidx.compose.runtime.mutableStateOf
16 | import androidx.compose.runtime.remember
17 | import androidx.compose.runtime.setValue
18 | import androidx.compose.ui.Modifier
19 | import androidx.compose.ui.tooling.preview.Preview
20 | import androidx.compose.ui.unit.dp
21 |
22 | @Preview
23 | @Composable
24 | fun Tutorial4_9_1Screen() {
25 | TutorialContent()
26 | }
27 |
28 | @Preview
29 | @Composable
30 | private fun TutorialContent() {
31 |
32 | var showSample by remember {
33 | mutableStateOf(false)
34 | }
35 | Column(
36 | modifier = Modifier
37 | .fillMaxSize()
38 | .padding(16.dp)
39 | .verticalScroll(rememberScrollState())
40 | ) {
41 |
42 | Button(onClick = { showSample = showSample.not() }) {
43 | Text(text = "Show Composable")
44 | }
45 | if (showSample) {
46 | RememberObserverSample()
47 | }
48 | }
49 | }
50 |
51 | @Composable
52 | private fun RememberObserverSample() {
53 | val sampleUiState = remember {
54 | SampleUiState()
55 | }
56 |
57 | Button(
58 | modifier = Modifier.fillMaxWidth(),
59 | onClick = { sampleUiState.counter++ }
60 | ) {
61 | Text(text = "Increase Counter")
62 | }
63 |
64 | Text(text = "Counter: ${sampleUiState.counter}")
65 | }
66 |
67 |
68 | private class SampleUiState : RememberObserver {
69 |
70 | var counter by mutableIntStateOf(0)
71 |
72 |
73 | override fun onAbandoned() {
74 | println("🔥 onAbandoned")
75 | }
76 |
77 | override fun onForgotten() {
78 | println("🏈 onForgotten")
79 | }
80 |
81 | override fun onRemembered() {
82 | println("🔥 onRemembered")
83 | }
84 |
85 | }
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/chapter5_gesture/Tutorial5_9_2NestedScroll1.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.chapter5_gesture
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.border
5 | import androidx.compose.foundation.layout.Box
6 | import androidx.compose.foundation.layout.Column
7 | import androidx.compose.foundation.layout.fillMaxSize
8 | import androidx.compose.foundation.layout.fillMaxWidth
9 | import androidx.compose.foundation.layout.height
10 | import androidx.compose.foundation.layout.padding
11 | import androidx.compose.foundation.rememberScrollState
12 | import androidx.compose.foundation.verticalScroll
13 | import androidx.compose.material.Text
14 | import androidx.compose.runtime.Composable
15 | import androidx.compose.ui.Alignment
16 | import androidx.compose.ui.Modifier
17 | import androidx.compose.ui.graphics.Brush
18 | import androidx.compose.ui.graphics.Color
19 | import androidx.compose.ui.tooling.preview.Preview
20 | import androidx.compose.ui.unit.dp
21 | import androidx.compose.ui.unit.sp
22 |
23 | @Preview
24 | @Composable
25 | fun Tutorial5_9Screen2() {
26 | TutorialContent()
27 | }
28 |
29 | @Composable
30 | private fun TutorialContent() {
31 | Column(modifier = Modifier.fillMaxSize()) {
32 | DefaultNestedScrollExample()
33 | }
34 | }
35 |
36 | @Composable
37 | private fun DefaultNestedScrollExample() {
38 |
39 | val gradient = Brush.verticalGradient(0f to Color.Gray, 1000f to Color.White)
40 | Box(
41 | modifier = Modifier
42 | .fillMaxWidth()
43 | .background(Color.LightGray)
44 | .verticalScroll(rememberScrollState())
45 | .padding(32.dp),
46 | contentAlignment = Alignment.TopCenter
47 | ) {
48 | Column {
49 | repeat(12) {
50 | Box(
51 | modifier = Modifier
52 | .height(128.dp)
53 | .verticalScroll(rememberScrollState())
54 | ) {
55 | Text(
56 | "Scroll here",
57 | fontSize = 16.sp,
58 | modifier = Modifier
59 | .border(12.dp, Color.DarkGray)
60 | .background(brush = gradient)
61 | .padding(24.dp)
62 | .height(180.dp)
63 | )
64 | }
65 | }
66 | }
67 | }
68 | }
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/chapter5_gesture/gesture/MotionEvent.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.chapter5_gesture.gesture
2 |
3 | enum class MotionEvent {
4 | Idle, Down, Move, Up
5 | }
6 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/chapter6_graphics/Tutorial6_3_1ColorFilter1.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.chapter6_graphics
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.tooling.preview.Preview
5 |
6 | @Preview
7 | @Composable
8 | fun Tutorial6_3Screen1() {
9 | TutorialContent()
10 | }
11 |
12 | @Composable
13 | private fun TutorialContent() {
14 |
15 | }
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/chapter6_graphics/Tutorial6_6EditBox.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.chapter6_graphics
2 |
3 | import androidx.compose.foundation.Image
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.Spacer
7 | import androidx.compose.foundation.layout.fillMaxSize
8 | import androidx.compose.foundation.layout.height
9 | import androidx.compose.foundation.layout.padding
10 | import androidx.compose.foundation.layout.size
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.graphics.Color
14 | import androidx.compose.ui.layout.ContentScale
15 | import androidx.compose.ui.platform.LocalDensity
16 | import androidx.compose.ui.res.painterResource
17 | import androidx.compose.ui.tooling.preview.Preview
18 | import androidx.compose.ui.unit.dp
19 | import com.smarttoolfactory.tutorial1_1basics.R
20 | import com.smarttoolfactory.tutorial1_1basics.chapter6_graphics.editbox.ScaleEditBox
21 |
22 | @Preview
23 | @Composable
24 | fun Tutorial6_6Screen() {
25 | TutorialContent()
26 | }
27 |
28 | @Composable
29 | private fun TutorialContent() {
30 | Column(
31 | modifier = Modifier
32 | .background(Color(0xff424242))
33 | .fillMaxSize()
34 | .padding(8.dp)
35 | ) {
36 | // In this example we use a Composable that scales, translates its scope
37 | EditScaleDemo()
38 | }
39 | }
40 |
41 |
42 | @Composable
43 | private fun EditScaleDemo() {
44 | Column(
45 | modifier = Modifier
46 | .background(Color(0xff424242))
47 | .fillMaxSize()
48 | .padding(8.dp)
49 | ) {
50 |
51 |
52 | Spacer(modifier = Modifier.height(40.dp))
53 |
54 |
55 | val density = LocalDensity.current
56 | val size = (1000 / density.density).dp
57 |
58 | ScaleEditBox(
59 | modifier = Modifier.size(size),
60 | enabled = true
61 | ) {
62 | Image(
63 | modifier=Modifier.fillMaxSize(),
64 | painter = painterResource(id = R.drawable.landscape3),
65 | contentScale = ContentScale.FillBounds,
66 | contentDescription = "",
67 | )
68 | }
69 |
70 | Spacer(modifier = Modifier.height(20.dp))
71 | ScaleEditBox(
72 | handleRadius = 20.dp,
73 | enabled = true
74 | ) {
75 | Image(
76 | painter = painterResource(id = R.drawable.landscape2),
77 | contentScale = ContentScale.FillBounds,
78 | contentDescription = ""
79 | )
80 | }
81 |
82 |
83 | }
84 | }
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/chapter6_graphics/editbox/DimensionSubcomposeLayout.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.chapter6_graphics.editbox
2 |
3 | import androidx.compose.foundation.layout.Box
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.Modifier
6 | import androidx.compose.ui.layout.Placeable
7 | import androidx.compose.ui.layout.SubcomposeLayout
8 | import androidx.compose.ui.unit.IntSize
9 |
10 | /**
11 | * [SubcomposeLayout] layouts children like [Box] and returns [IntSize] of this Composable.
12 | * It doesn't layout mainContent because our [mainContent] and [dependentContent] are
13 | * combined as one Composable. We just measure exact size of initial dimensions
14 | * for a Composable to use it inside for setting size of Rectangles for initial bounds
15 | * for instance.
16 | */
17 | @Composable
18 | internal fun DimensionSubcomposeLayout(
19 | modifier: Modifier = Modifier,
20 | mainContent: @Composable () -> Unit,
21 | dependentContent: @Composable (IntSize) -> Unit
22 | ) {
23 |
24 | SubcomposeLayout(modifier = modifier) { constraints ->
25 |
26 | // Subcompose(compose only a section) main content and get Placeable
27 | val mainPlaceables: List = subcompose(SlotsEnum.Main, mainContent).map {
28 | it.measure(constraints)
29 | }
30 |
31 | // Get max width and height of main component
32 | val maxSize =
33 | mainPlaceables.fold(IntSize.Zero) { currentMax: IntSize, placeable: Placeable ->
34 | IntSize(
35 | width = maxOf(currentMax.width, placeable.width),
36 | height = maxOf(currentMax.height, placeable.height)
37 | )
38 | }
39 |
40 | layout(maxSize.width, maxSize.height) {
41 |
42 | // Get List from subcompose function then get List and place them
43 | subcompose(SlotsEnum.Dependent) {
44 | dependentContent(maxSize)
45 | }.forEach {
46 | it.measure(constraints).placeRelative(0, 0)
47 | }
48 | }
49 | }
50 | }
51 |
52 | enum class SlotsEnum { Main, Dependent }
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/chapter6_graphics/editbox/TouchRegion.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.chapter6_graphics.editbox
2 |
3 | enum class TouchRegion {
4 | TopLeft, TopRight, BottomLeft, BottomRight, Inside, None
5 | }
6 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/chapter6_graphics/editbox/Util.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.chapter6_graphics.editbox
2 |
3 | import androidx.compose.ui.geometry.Offset
4 | import androidx.compose.ui.geometry.Rect
5 | import androidx.compose.ui.graphics.Color
6 | import androidx.compose.ui.graphics.drawscope.DrawScope
7 | import androidx.compose.ui.graphics.drawscope.Stroke
8 | import androidx.compose.ui.unit.dp
9 |
10 | internal fun getTouchRegion(
11 | position: Offset,
12 | rect: Rect,
13 | threshold: Float
14 | ): TouchRegion {
15 |
16 | // Instead of using square check for power of 2 of threshold
17 | val target2 = threshold*threshold
18 | return when {
19 |
20 | inDistanceSquared(
21 | position,
22 | rect.topLeft,
23 | target2
24 | ) -> TouchRegion.TopLeft
25 | inDistanceSquared(
26 | position,
27 | rect.topRight,
28 | target2
29 | ) -> TouchRegion.TopRight
30 | inDistanceSquared(
31 | position,
32 | rect.bottomLeft,
33 | target2
34 | ) -> TouchRegion.BottomLeft
35 | inDistanceSquared(
36 | position,
37 | rect.bottomRight,
38 | target2
39 | ) -> TouchRegion.BottomRight
40 | rect.contains(offset = position) -> TouchRegion.Inside
41 | else -> TouchRegion.None
42 | }
43 | }
44 |
45 | /**
46 | * Check if [target] which is power of 2 of actual value to not use square to make this
47 | * operation cheaper
48 | */
49 | internal fun inDistanceSquared(offset1: Offset, offset2: Offset, target: Float): Boolean {
50 | val x1 = offset1.x
51 | val y1 = offset1.y
52 |
53 | val x2 = offset2.x
54 | val y2 = offset2.y
55 |
56 | val distance = ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
57 | return distance < target
58 | }
59 |
60 | internal fun DrawScope.drawBorderCircle(
61 | radius: Float,
62 | center: Offset
63 | ) {
64 | drawCircle(color = Color.White.copy(alpha = .7f), radius = radius, center = center)
65 | drawCircle(color = Color.White, radius = radius, center = center, style = Stroke(1.dp.toPx()))
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/model/Place.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.model
2 |
3 | import androidx.annotation.DrawableRes
4 | import androidx.compose.runtime.Immutable
5 | import com.smarttoolfactory.tutorial1_1basics.R
6 | import kotlin.random.Random
7 |
8 | @Immutable
9 | data class Place(
10 | val id: Long,
11 | val description: String,
12 | @DrawableRes val imgRes: Int,
13 | val rating: Double = Random.nextDouble(0.0, 10.0),
14 | val price: Int = Random.nextInt(500)
15 | )
16 |
17 | val places = listOf(
18 | Place(1, "Place1", R.drawable.landscape1),
19 | Place(2, "Place2", R.drawable.landscape2),
20 | Place(3, "Place3", R.drawable.landscape3),
21 | Place(4, "Place4", R.drawable.landscape4),
22 | Place(5, "Place5", R.drawable.landscape5),
23 | Place(6, "Place6", R.drawable.landscape6),
24 | Place(7, "Place7", R.drawable.landscape7),
25 | Place(8, "Place8", R.drawable.landscape8),
26 | Place(9, "Place9", R.drawable.landscape9),
27 | Place(10, "Place10", R.drawable.landscape10)
28 | )
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/model/SuggestionModel.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.model
2 |
3 | data class SuggestionModel(val tag: String) {
4 | val id = tag.hashCode()
5 | }
6 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/model/TopAppbarMenuOverflow.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.model
2 |
3 | import androidx.compose.ui.graphics.vector.ImageVector
4 |
5 | fun separateIntoActionAndOverflow(
6 | items: List,
7 | defaultIconSpace: Int
8 | ): Pair, List> {
9 |
10 | var (alwaysCount, neverCount, ifRoomCount) = Triple(0, 0, 0)
11 |
12 | for (item in items) {
13 | when (item.visibility) {
14 | ActionItemMode.ALWAYS_SHOW -> alwaysCount++
15 | ActionItemMode.NEVER_SHOW -> neverCount++
16 | ActionItemMode.IF_ROOM -> ifRoomCount++
17 | }
18 | }
19 |
20 | val needsOverflow = alwaysCount + ifRoomCount > defaultIconSpace || neverCount > 0
21 | val actionIconSpace = defaultIconSpace - (if (needsOverflow) 1 else 0)
22 |
23 | val actionItems = ArrayList()
24 | val overflowItems = ArrayList()
25 |
26 | var ifRoomsToDisplay = actionIconSpace - alwaysCount
27 | for (item in items) {
28 | when (item.visibility) {
29 | ActionItemMode.ALWAYS_SHOW -> {
30 | actionItems.add(item)
31 | }
32 | ActionItemMode.NEVER_SHOW -> {
33 | overflowItems.add(item)
34 | }
35 | ActionItemMode.IF_ROOM -> {
36 | if (ifRoomsToDisplay > 0) {
37 | actionItems.add(item)
38 | ifRoomsToDisplay--
39 | } else {
40 | overflowItems.add(item)
41 | }
42 |
43 | }
44 | }
45 | }
46 | return Pair(actionItems, overflowItems)
47 |
48 | }
49 |
50 | // Kind of equivalent to a menu XML entry, except for the onClick lambda
51 | data class ActionItemSpec(
52 | val name: String,
53 | val icon: ImageVector,
54 | val visibility: ActionItemMode = ActionItemMode.IF_ROOM,
55 | val onClick: () -> Unit,
56 | )
57 |
58 | // Whether to show the action item as an icon or not (or if room)
59 | enum class ActionItemMode {
60 | ALWAYS_SHOW, IF_ROOM, NEVER_SHOW
61 | }
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/model/TutorialSectionModel.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.model
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.graphics.Color
5 | import com.smarttoolfactory.tutorial1_1basics.ui.DefaultListColor
6 |
7 | data class TutorialSectionModel(
8 | val title: String,
9 | val action: @Composable (() -> Unit)? = null,
10 | val description: String,
11 | val tags: List = listOf(),
12 | val tagColor: Color = DefaultListColor,
13 | var expanded: Boolean = false
14 | )
15 |
16 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/ui/Color.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.ui
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)
9 |
10 | val Orange400 = Color(0xffFFA726)
11 | val Blue400 = Color(0xff42A5F5)
12 | val Pink400 = Color(0xffEC407A)
13 | val Green400 = Color(0xff66BB6A)
14 | val Red400 = Color(0xffEF5350)
15 | val Yellow400 = Color(0xffFFEE58)
16 | val Purple400 = Color(0xffAB47BC)
17 | val Brown400 = Color(0xff8D6E63)
18 | val BlueGrey400 = Color(0xff78909C)
19 |
20 | val backgroundColor = Color(0xffECEFF1)
21 |
22 |
23 | val DefaultListColor = Color(0xff00BCD4)
24 | val LayoutListColor = Color(0xffFFEB3B)
25 | val StateListColor = Color(0xffE91E63)
26 | val GestureListColor = Color(0xff8BC34A)
27 | val GraphicsListColor = Color(0xffFF9800)
28 |
29 | val SentMessageColor = Color(0xffE7FFDB)
30 | val SentQuoteColor = Color(0xffDEF6D3)
31 | val ReceivedQuoteColor = Color(0xffECEFF1)
32 | val QuoteTextColor = Color(0xff757575)
33 | val RecipientAltNameColor = Color(0xff757575)
34 |
35 | val gradientColors = listOf(
36 | Color.Red,
37 | Color.Magenta,
38 | Color.Blue,
39 | Color.Cyan,
40 | Color.Green,
41 | Color.Yellow,
42 | Color.Red
43 | )
44 |
45 | val gradientColorsReversed = listOf(
46 | Color.Red,
47 | Color.Yellow,
48 | Color.Green,
49 | Color.Cyan,
50 | Color.Blue,
51 | Color.Magenta,
52 | Color.Red
53 | )
54 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/ui/IconButton.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.ui
2 |
3 | import android.content.res.Configuration
4 | import androidx.compose.foundation.Indication
5 | import androidx.compose.foundation.clickable
6 | import androidx.compose.foundation.interaction.MutableInteractionSource
7 | import androidx.compose.foundation.layout.Box
8 | import androidx.compose.material.ContentAlpha
9 | import androidx.compose.material.Icon
10 | import androidx.compose.material.LocalContentAlpha
11 | import androidx.compose.material.icons.Icons
12 | import androidx.compose.material.icons.filled.CameraAlt
13 | import androidx.compose.material.ripple
14 | import androidx.compose.runtime.Composable
15 | import androidx.compose.runtime.CompositionLocalProvider
16 | import androidx.compose.runtime.remember
17 | import androidx.compose.ui.Alignment
18 | import androidx.compose.ui.Modifier
19 | import androidx.compose.ui.semantics.Role
20 | import androidx.compose.ui.tooling.preview.Devices
21 | import androidx.compose.ui.tooling.preview.Preview
22 | import androidx.compose.ui.unit.dp
23 |
24 | // Default radius of an unbounded ripple in an IconButton
25 | private val RippleRadius = 24.dp
26 |
27 | /**
28 | * Icon Button with adjustable indication option. Indication of standard [IconButton] cannot
29 | * be changed thus making it have bigger radius than necessary in some cases.
30 | */
31 | @Composable
32 | fun IndicatingIconButton(
33 | onClick: () -> Unit,
34 | modifier: Modifier = Modifier,
35 | enabled: Boolean = true,
36 | interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
37 | indication: Indication = ripple(bounded = false, radius = RippleRadius),
38 | content: @Composable () -> Unit,
39 | ) {
40 |
41 | Box(
42 | modifier = modifier
43 | .clickable(
44 | onClick = onClick,
45 | enabled = enabled,
46 | role = Role.Button,
47 | interactionSource = interactionSource,
48 | indication = indication
49 | ),
50 | contentAlignment = Alignment.Center
51 | ) {
52 | val contentAlpha = if (enabled) LocalContentAlpha.current else ContentAlpha.disabled
53 | CompositionLocalProvider(LocalContentAlpha provides contentAlpha, content = content)
54 | }
55 | }
56 |
57 | @Preview
58 | @Preview("dark", uiMode = Configuration.UI_MODE_NIGHT_YES)
59 | @Preview(device = Devices.PIXEL_C)
60 | @Composable
61 | private fun IndicatingIconButtonPreview() {
62 | IndicatingIconButton(onClick = {}) {
63 | Icon(
64 | imageVector = Icons.Filled.CameraAlt,
65 | contentDescription = "camera"
66 | )
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/ui/Shape.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.ui
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 | )
12 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/ui/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.ui
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 | import androidx.compose.runtime.SideEffect
9 | import androidx.compose.ui.graphics.Color
10 | import com.google.accompanist.systemuicontroller.rememberSystemUiController
11 |
12 | private val DarkColorPalette = darkColors(
13 | primary = purple200,
14 | secondary = teal200,
15 | primaryVariant = purple700,
16 | error = Color(0xFFB3261E),
17 | background = Color(0xFF1C1B1F),
18 | surface = Color(0xFF201F24),
19 | onPrimary = Color.Black,
20 | onSecondary = Color.White,
21 | onBackground = Color(0xFFE6E1E5),
22 | onSurface = Color(0xFFE6E1E5),
23 | onError = Color.White
24 | )
25 |
26 | private val LightColorPalette = lightColors(
27 | primary = purple500,
28 | primaryVariant = purple700,
29 | secondary = teal200,
30 | background = Color.White,
31 | surface = Color.White,
32 | onPrimary = Color.White,
33 | onSecondary = Color.Black,
34 | onBackground = Color.Black,
35 | onSurface = Color.Black,
36 | )
37 |
38 | @Composable
39 | fun ComposeTutorialsTheme(
40 | darkTheme: Boolean = isSystemInDarkTheme(),
41 | content: @Composable () -> Unit
42 | ) {
43 | val colors = if (darkTheme) {
44 | DarkColorPalette
45 | } else {
46 | LightColorPalette
47 | }
48 |
49 | val sysUiController = rememberSystemUiController()
50 | SideEffect {
51 | sysUiController.setSystemBarsColor(
52 | color = colors.surface.copy(.5f)
53 | )
54 | }
55 |
56 | MaterialTheme(
57 | colors = colors,
58 | typography = typography,
59 | shapes = shapes,
60 | content = content
61 | )
62 | }
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/ui/Type.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.ui
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 | )
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/ui/components/DrawerButton.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.ui.components
2 |
3 | import androidx.compose.foundation.Image
4 | import androidx.compose.foundation.layout.*
5 | import androidx.compose.material.MaterialTheme
6 | import androidx.compose.material.Surface
7 | import androidx.compose.material.Text
8 | import androidx.compose.material.TextButton
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.ui.Alignment
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.graphics.Color
13 | import androidx.compose.ui.graphics.ColorFilter
14 | import androidx.compose.ui.graphics.vector.ImageVector
15 | import androidx.compose.ui.text.font.FontWeight
16 | import androidx.compose.ui.unit.dp
17 |
18 | /**
19 | * Button for ModalDrawer or BottomDrawer menu items
20 | */
21 | @Composable
22 | fun DrawerButton(
23 | icon: ImageVector,
24 | label: String,
25 | isSelected: Boolean,
26 | action: () -> Unit,
27 | modifier: Modifier = Modifier
28 | ) {
29 | val colors = MaterialTheme.colors
30 | val imageAlpha = if (isSelected) {
31 | 1f
32 | } else {
33 | 0.8f
34 | }
35 | val textIconColor = if (isSelected) {
36 | colors.primary
37 | } else {
38 | colors.onSurface.copy(alpha = 0.9f)
39 | }
40 | val backgroundColor = if (isSelected) {
41 | colors.primary.copy(alpha = 0.12f)
42 | } else {
43 | Color.Transparent
44 | }
45 |
46 | val surfaceModifier = modifier
47 | .padding(start = 8.dp, top = 8.dp, end = 8.dp)
48 | .fillMaxWidth()
49 | Surface(
50 | modifier = surfaceModifier,
51 | color = backgroundColor,
52 | shape = MaterialTheme.shapes.small
53 | ) {
54 | TextButton(
55 | onClick = action,
56 | modifier = Modifier.fillMaxWidth()
57 | ) {
58 | Row(
59 | horizontalArrangement = Arrangement.Start,
60 | verticalAlignment = Alignment.CenterVertically,
61 | modifier = Modifier.fillMaxWidth()
62 | ) {
63 | Image(
64 | imageVector = icon,
65 | contentDescription = null, // decorative
66 | colorFilter = ColorFilter.tint(textIconColor),
67 | alpha = imageAlpha
68 | )
69 | Spacer(Modifier.width(16.dp))
70 | Text(
71 | fontWeight = FontWeight.Bold,
72 | text = label,
73 | style = MaterialTheme.typography.body2,
74 | color = textIconColor
75 | )
76 | }
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/ui/components/RandomColor.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial1_1basics.ui.components
2 |
3 | import androidx.compose.ui.graphics.Color
4 | import kotlin.random.Random
5 |
6 | fun getRandomColor() = Color(
7 | red = Random.nextInt(256),
8 | green = Random.nextInt(256),
9 | blue = Random.nextInt(256),
10 | alpha = 255
11 | )
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable-nodpi/drawer_bg.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable-nodpi/drawer_bg.jpeg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable-nodpi/drawer_bg2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable-nodpi/drawer_bg2.jpeg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable-nodpi/landscape11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable-nodpi/landscape11.jpg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable-nodpi/placeholder.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable-nodpi/placeholder.jpg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable-nodpi/under_construction.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable-nodpi/under_construction.jpg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/avatar_1_raster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/avatar_1_raster.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/avatar_2_raster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/avatar_2_raster.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/avatar_3_raster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/avatar_3_raster.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/avatar_4_raster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/avatar_4_raster.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/avatar_5_raster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/avatar_5_raster.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/avatar_6_raster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/avatar_6_raster.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/composite_dst.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/composite_dst.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/composite_src.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/composite_src.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/cupcake.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/cupcake.jpg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/dest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/dest.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/donut.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/donut.jpeg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/dots_transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/dots_transparent.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/eclair.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/eclair.jpeg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/froyo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/froyo.jpeg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/gingerbread.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/gingerbread.jpg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/honeycomb.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/honeycomb.jpg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/ic_baseline_deblur_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
16 |
19 |
22 |
25 |
28 |
31 |
34 |
37 |
40 |
43 |
46 |
47 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/ic_baseline_location_on_48.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/ic_baseline_star_12.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/ic_eraser_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/image_before_after_shoes_a.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/image_before_after_shoes_a.jpeg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/image_before_after_shoes_b.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/image_before_after_shoes_b.jpeg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/landscape1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/landscape1.jpg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/landscape10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/landscape10.jpg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/landscape2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/landscape2.jpg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/landscape3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/landscape3.jpg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/landscape4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/landscape4.jpg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/landscape5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/landscape5.jpg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/landscape6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/landscape6.jpg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/landscape7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/landscape7.jpg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/landscape8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/landscape8.jpg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/landscape9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/landscape9.jpg
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/source.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/source.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/tg_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/drawable/tg_icon.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/tip.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/vd_account_active.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/vd_account_outlined.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/vd_activity1_1heart.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/vd_clock_stopwatch.xml:
--------------------------------------------------------------------------------
1 |
8 |
12 |
16 |
17 |
22 |
23 |
24 |
25 |
29 |
33 |
38 |
39 |
40 |
44 |
48 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/vd_dashboard_active.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/vd_dashboard_outlined.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/vd_heart_empty.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/vd_heart_filled.xml:
--------------------------------------------------------------------------------
1 |
7 |
13 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/vd_home_active.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/vd_home_outlined.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/vd_notification_active.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/vd_notification_outlined.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/drawable/vd_profile.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
15 |
16 |
21 |
25 |
26 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial1-1Basics/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/Tutorial1-1Basics/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/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
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
13 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/java/com/smarttoolfactory/tutorial2_1unit_testing/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import androidx.compose.foundation.layout.fillMaxSize
7 | import androidx.compose.material3.MaterialTheme
8 | import androidx.compose.material3.Surface
9 | import androidx.compose.material3.Text
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.tooling.preview.Preview
13 | import com.smarttoolfactory.tutorial2_1unit_testing.ui.theme.ComposeTutorialsTheme
14 |
15 | class MainActivity : ComponentActivity() {
16 | override fun onCreate(savedInstanceState: Bundle?) {
17 | super.onCreate(savedInstanceState)
18 | setContent {
19 | ComposeTutorialsTheme {
20 | // A surface container using the 'background' color from the theme
21 | Surface(
22 | modifier = Modifier.fillMaxSize(),
23 | color = MaterialTheme.colorScheme.background
24 | ) {
25 | Greeting("Android")
26 | }
27 | }
28 | }
29 | }
30 | }
31 |
32 | @Composable
33 | fun Greeting(name: String, modifier: Modifier = Modifier) {
34 | Text(
35 | text = "Hello $name!",
36 | modifier = modifier
37 | )
38 | }
39 |
40 | @Preview(showBackground = true)
41 | @Composable
42 | fun GreetingPreview() {
43 | ComposeTutorialsTheme {
44 | Greeting("Android")
45 | }
46 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/java/com/smarttoolfactory/tutorial2_1unit_testing/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Purple80 = Color(0xFFD0BCFF)
6 | val PurpleGrey80 = Color(0xFFCCC2DC)
7 | val Pink80 = Color(0xFFEFB8C8)
8 |
9 | val Purple40 = Color(0xFF6650a4)
10 | val PurpleGrey40 = Color(0xFF625b71)
11 | val Pink40 = Color(0xFF7D5260)
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/java/com/smarttoolfactory/tutorial2_1unit_testing/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.ui.theme
2 |
3 | import android.app.Activity
4 | import android.os.Build
5 | import androidx.compose.foundation.isSystemInDarkTheme
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.material3.darkColorScheme
8 | import androidx.compose.material3.dynamicDarkColorScheme
9 | import androidx.compose.material3.dynamicLightColorScheme
10 | import androidx.compose.material3.lightColorScheme
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.runtime.SideEffect
13 | import androidx.compose.ui.graphics.toArgb
14 | import androidx.compose.ui.platform.LocalContext
15 | import androidx.compose.ui.platform.LocalView
16 | import androidx.core.view.WindowCompat
17 |
18 | private val DarkColorScheme = darkColorScheme(
19 | primary = Purple80,
20 | secondary = PurpleGrey80,
21 | tertiary = Pink80
22 | )
23 |
24 | private val LightColorScheme = lightColorScheme(
25 | primary = Purple40,
26 | secondary = PurpleGrey40,
27 | tertiary = Pink40
28 |
29 | /* Other default colors to override
30 | background = Color(0xFFFFFBFE),
31 | surface = Color(0xFFFFFBFE),
32 | onPrimary = Color.White,
33 | onSecondary = Color.White,
34 | onTertiary = Color.White,
35 | onBackground = Color(0xFF1C1B1F),
36 | onSurface = Color(0xFF1C1B1F),
37 | */
38 | )
39 |
40 | @Composable
41 | fun ComposeTutorialsTheme(
42 | darkTheme: Boolean = isSystemInDarkTheme(),
43 | // Dynamic color is available on Android 12+
44 | dynamicColor: Boolean = true,
45 | content: @Composable () -> Unit
46 | ) {
47 | val colorScheme = when {
48 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
49 | val context = LocalContext.current
50 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
51 | }
52 |
53 | darkTheme -> DarkColorScheme
54 | else -> LightColorScheme
55 | }
56 | val view = LocalView.current
57 | if (!view.isInEditMode) {
58 | SideEffect {
59 | val window = (view.context as Activity).window
60 | window.statusBarColor = colorScheme.primary.toArgb()
61 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
62 | }
63 | }
64 |
65 | MaterialTheme(
66 | colorScheme = colorScheme,
67 | typography = Typography,
68 | content = content
69 | )
70 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/java/com/smarttoolfactory/tutorial2_1unit_testing/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.ui.theme
2 |
3 | import androidx.compose.material3.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 | bodyLarge = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp,
15 | lineHeight = 24.sp,
16 | letterSpacing = 0.5.sp
17 | )
18 | /* Other default text styles to override
19 | titleLarge = TextStyle(
20 | fontFamily = FontFamily.Default,
21 | fontWeight = FontWeight.Normal,
22 | fontSize = 22.sp,
23 | lineHeight = 28.sp,
24 | letterSpacing = 0.sp
25 | ),
26 | labelSmall = TextStyle(
27 | fontFamily = FontFamily.Default,
28 | fontWeight = FontWeight.Medium,
29 | fontSize = 11.sp,
30 | lineHeight = 16.sp,
31 | letterSpacing = 0.5.sp
32 | )
33 | */
34 | )
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-1Unit-Testing/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-1Unit-Testing/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-1Unit-Testing/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-1Unit-Testing/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-1Unit-Testing/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-1Unit-Testing/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-1Unit-Testing/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-1Unit-Testing/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-1Unit-Testing/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-1Unit-Testing/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Tutorial2-1Unit-Testing
3 |
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/coroutines/CoroutinesViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.coroutines
2 |
3 | import androidx.compose.runtime.getValue
4 | import androidx.compose.runtime.mutableStateOf
5 | import androidx.compose.runtime.setValue
6 | import androidx.lifecycle.ViewModel
7 | import androidx.lifecycle.viewModelScope
8 | import kotlinx.coroutines.delay
9 | import kotlinx.coroutines.flow.MutableStateFlow
10 | import kotlinx.coroutines.flow.StateFlow
11 | import kotlinx.coroutines.launch
12 |
13 | class CoroutinesViewModel : ViewModel() {
14 |
15 | var result by mutableStateOf(ResponseResult.Idle)
16 |
17 | private val _message = MutableStateFlow("")
18 | val message: StateFlow get() = _message
19 |
20 | fun loadMessage() {
21 | viewModelScope.launch {
22 | _message.value = "Greetings!"
23 | }
24 | }
25 |
26 | fun loadResultWithDelay() {
27 | viewModelScope.launch {
28 | result = ResponseResult.Loading
29 | delay(100)
30 | result = ResponseResult.Success("Result")
31 | }
32 | }
33 | }
34 |
35 | sealed class ResponseResult {
36 | object Idle : ResponseResult()
37 | object Loading : ResponseResult()
38 | data class Success(val data: String) : ResponseResult()
39 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/coroutines/MainDispatcherExtension.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.coroutines
2 |
3 | import kotlinx.coroutines.Dispatchers
4 | import kotlinx.coroutines.ExperimentalCoroutinesApi
5 | import kotlinx.coroutines.test.TestDispatcher
6 | import kotlinx.coroutines.test.UnconfinedTestDispatcher
7 | import kotlinx.coroutines.test.resetMain
8 | import kotlinx.coroutines.test.setMain
9 | import org.junit.jupiter.api.extension.AfterEachCallback
10 | import org.junit.jupiter.api.extension.BeforeEachCallback
11 | import org.junit.jupiter.api.extension.ExtensionContext
12 |
13 | @OptIn(ExperimentalCoroutinesApi::class)
14 | class MainDispatcherExtension(
15 | val testDispatcher: TestDispatcher = UnconfinedTestDispatcher(),
16 | ) : BeforeEachCallback, AfterEachCallback {
17 |
18 |
19 | override fun beforeEach(context: ExtensionContext?) {
20 | Dispatchers.setMain(testDispatcher)
21 | }
22 |
23 | override fun afterEach(context: ExtensionContext?) {
24 | Dispatchers.resetMain()
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/coroutines/MainDispatcherRule.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.coroutines
2 |
3 | import kotlinx.coroutines.Dispatchers
4 | import kotlinx.coroutines.ExperimentalCoroutinesApi
5 | import kotlinx.coroutines.test.TestDispatcher
6 | import kotlinx.coroutines.test.UnconfinedTestDispatcher
7 | import kotlinx.coroutines.test.resetMain
8 | import kotlinx.coroutines.test.setMain
9 | import org.junit.rules.TestWatcher
10 | import org.junit.runner.Description
11 |
12 | // Reusable JUnit4 TestRule to override the Main dispatcher
13 | @OptIn(ExperimentalCoroutinesApi::class)
14 | class MainDispatcherRule(
15 | val testDispatcher: TestDispatcher = UnconfinedTestDispatcher(),
16 | ) : TestWatcher() {
17 | override fun starting(description: Description) {
18 | Dispatchers.setMain(testDispatcher)
19 | }
20 |
21 | override fun finished(description: Description) {
22 | Dispatchers.resetMain()
23 | }
24 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/coroutines/UserRepository.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.coroutines
2 |
3 | import kotlinx.coroutines.delay
4 |
5 | class UserRepository {
6 | private val users = mutableListOf()
7 |
8 | fun registerUsers(user: String) {
9 | users.add(user)
10 | }
11 |
12 | suspend fun registerUserWithDelay(user: String) {
13 | delay(100)
14 | users.add(user)
15 | }
16 |
17 | fun getAllUsers(): MutableList {
18 | return users
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/CalculationViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import androidx.compose.runtime.getValue
4 | import androidx.compose.runtime.mutableStateOf
5 | import androidx.compose.runtime.setValue
6 | import androidx.lifecycle.ViewModel
7 |
8 | class CalculationViewModel : ViewModel() {
9 | var sum by mutableStateOf(0)
10 | private set
11 |
12 | fun increase() {
13 | sum++
14 | }
15 |
16 | fun sumWith(value: Int) {
17 | sum += value
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/CalculationViewModelTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import com.google.common.truth.Truth
4 | import org.junit.jupiter.api.Test
5 |
6 | class CalculationViewModelTest {
7 |
8 | private val calculationViewModel = CalculationViewModel()
9 |
10 | @Test
11 | fun `increase test`() {
12 | // GIVEN
13 | val initialValue = calculationViewModel.sum
14 | // WHEN
15 | calculationViewModel.increase()
16 | // THEN
17 | Truth.assertThat(calculationViewModel.sum).isEqualTo(initialValue + 1)
18 | }
19 |
20 | @Test
21 | fun `sumWith test`() {
22 | // GIVEN
23 | val initialValue = calculationViewModel.sum
24 | // WHEN
25 | calculationViewModel.sumWith(5)
26 | // THEN
27 | Truth.assertThat(calculationViewModel.sum).isEqualTo(initialValue + 5)
28 | }
29 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/CaptureTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import com.google.common.truth.Truth
4 | import io.mockk.MockKAnnotations
5 | import io.mockk.every
6 | import io.mockk.impl.annotations.InjectMockKs
7 | import io.mockk.impl.annotations.MockK
8 | import io.mockk.slot
9 | import org.junit.jupiter.api.BeforeEach
10 | import org.junit.jupiter.api.Test
11 |
12 | class CaptureTest {
13 |
14 | @InjectMockKs
15 | private lateinit var useCase: UseCase
16 |
17 | @MockK
18 | private lateinit var repository: Repository
19 |
20 | @BeforeEach
21 | fun setUp() {
22 | // 🔥🔥 Initializes mock objects annotated with MockKAnnotations
23 | MockKAnnotations.init(this)
24 | }
25 |
26 |
27 | @Test
28 | fun `Returns captured parameter with slot`() {
29 |
30 | // GIVEN
31 | val size = 5
32 | val slot = slot()
33 | every { repository.getItems(capture(slot)) } returns List(5) {
34 | Item("Row $it")
35 | }
36 | // WHEN
37 | val result = useCase.getItems(size = size)
38 | // THEN
39 | Truth.assertThat(size).isEqualTo(slot.captured)
40 |
41 | }
42 | }
43 |
44 | class UseCase(private val repository: Repository) {
45 | fun getItems(size: Int): List- {
46 | return repository.getItems(size)
47 | }
48 | }
49 |
50 | class Repository {
51 | fun getItems(size: Int) = List(size) {
52 | Item("Row $it")
53 | }
54 | }
55 |
56 | data class Item(val text: String)
57 |
58 |
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/Test10VerifyAtLeastAtMostExactly.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.car.Car
4 | import io.mockk.confirmVerified
5 | import io.mockk.mockk
6 | import io.mockk.verify
7 | import org.junit.jupiter.api.Test
8 |
9 | class Test10VerifyAtLeastAtMostExactly {
10 |
11 | @Test
12 | fun `Verify atLeast, atMost and exactly`() {
13 |
14 | val car = mockk(relaxed = true)
15 |
16 | car.accelerate(fromSpeed = 10, toSpeed = 20)
17 | car.accelerate(fromSpeed = 10, toSpeed = 30)
18 | car.accelerate(fromSpeed = 20, toSpeed = 30)
19 |
20 | // all pass
21 | verify(atLeast = 3) { car.accelerate(allAny(), allAny()) }
22 | verify(atMost = 2) { car.accelerate(fromSpeed = 10, toSpeed = or(20, 30)) }
23 | verify(exactly = 1) { car.accelerate(fromSpeed = 10, toSpeed = 20) }
24 | // // means no calls were performed
25 | verify(exactly = 0) { car.accelerate(fromSpeed = 30, toSpeed = 10) }
26 |
27 | confirmVerified(car)
28 |
29 | }
30 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/Test11VerificationOrder.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import io.mockk.Called
4 | import io.mockk.confirmVerified
5 | import io.mockk.every
6 | import io.mockk.mockk
7 | import io.mockk.slot
8 | import io.mockk.verify
9 | import io.mockk.verifyAll
10 | import io.mockk.verifyOrder
11 | import io.mockk.verifySequence
12 | import org.junit.jupiter.api.Test
13 |
14 |
15 | /**
16 | * verifyAll verifies that all calls happened without checking their order.
17 | * verifySequence verifies that that the exact sequence happened .
18 | * verifyOrder verifies that calls happened in a specific order.
19 | * wasNot Called verifies that the mock or the list of mocks was not called at all.
20 | *
21 | */
22 | class Test11VerificationOrder {
23 |
24 | private val obj = mockk()
25 | private val slot = slot()
26 |
27 | @Test
28 | fun test() {
29 |
30 | every {
31 | obj.sum(any(), capture(slot))
32 | } answers {
33 | // 🔥 firstArg() is the first argument of mock
34 | 1 + firstArg() + slot.captured
35 | }
36 |
37 | obj.sum(1, 2) // returns 4
38 | obj.sum(1, 3) // returns 5
39 | obj.sum(2, 2) // returns 5
40 |
41 | // Verifies that each function with params is called
42 | verifyAll {
43 | obj.sum(1, 3)
44 | obj.sum(1, 2)
45 | obj.sum(2, 2)
46 | }
47 |
48 | // Verifies that all sequence is called, all mocked function calls should be verified
49 | verifySequence {
50 | obj.sum(1, 2)
51 | obj.sum(1, 3)
52 | obj.sum(2, 2)
53 | }
54 |
55 | // Only looks for order of calls, it's not necessary to call every function
56 | verifyOrder {
57 | obj.sum(1, 2)
58 | obj.sum(2, 2)
59 | }
60 |
61 | val obj2 = mockk()
62 | val obj3 = mockk()
63 | verify {
64 | listOf(obj2, obj3) wasNot Called
65 | }
66 |
67 | confirmVerified(obj)
68 |
69 | }
70 |
71 | }
72 |
73 |
74 | class MockedClass {
75 | fun sum(a: Int, b: Int) = a + b
76 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/Test13RecordingExclusions.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.car.Car
4 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.car.Direction
5 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.car.Outcome
6 | import io.mockk.confirmVerified
7 | import io.mockk.every
8 | import io.mockk.excludeRecords
9 | import io.mockk.mockk
10 | import io.mockk.verify
11 | import org.junit.jupiter.api.Test
12 |
13 |
14 | /**
15 | * To exclude some not so important calls from being recorded you can use excludeRecords:
16 | *
17 | * excludeRecords { mock.operation(any(), 5) }
18 | *
19 | * All matching calls will be excluded from recording.
20 | * This may be useful in case you are using exhaustive verification:
21 | * verifyAll, verifySequence or confirmVerified.
22 | */
23 | class Test13RecordingExclusions {
24 |
25 | @Test
26 | fun `Test exclusions`() {
27 |
28 | //Given
29 | val car = mockk()
30 |
31 | every { car.drive(Direction.NORTH) } returns Outcome.OK
32 | every { car.drive(Direction.SOUTH) } returns Outcome.OK
33 |
34 | excludeRecords { car.drive(Direction.SOUTH) }
35 |
36 | // When
37 | car.drive(Direction.NORTH) // returns OK
38 | car.drive(Direction.SOUTH) // returns OK
39 |
40 | // Then
41 | verify {
42 | car.drive(Direction.NORTH)
43 | }
44 |
45 | // car.drive(Direction.SOUTH) was excluded, so confirmation
46 | // is fine with only car.drive(Direction.NORTH)
47 |
48 | confirmVerified(car)
49 |
50 | }
51 |
52 | @Test
53 | fun `Test exclusions2`() {
54 |
55 | //Given
56 | val car = mockk()
57 |
58 | every { car.drive(any()) } returns Outcome.OK
59 |
60 | excludeRecords { car.drive(Direction.SOUTH) }
61 |
62 | // When
63 | car.drive(Direction.NORTH) // returns OK
64 | car.drive(Direction.SOUTH) // returns OK
65 |
66 | // Then
67 | verify {
68 | car.drive(Direction.NORTH)
69 | }
70 |
71 | // car.drive(Direction.SOUTH) was excluded, so confirmation
72 | // is fine with only car.drive(Direction.NORTH)
73 |
74 | confirmVerified(car)
75 |
76 | }
77 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/Test14VerificationTimeout.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import io.mockk.every
4 | import io.mockk.mockk
5 | import io.mockk.verify
6 | import org.junit.jupiter.api.Test
7 |
8 |
9 | /**
10 | * To verify concurrent operations you can use timeout = xxx:
11 | */
12 | class Test14VerificationTimeout {
13 |
14 | @Test
15 | fun `Test timeout`() {
16 |
17 | mockk {
18 |
19 | every { sum(1, 2) } returns 4
20 |
21 | Thread {
22 | Thread.sleep(2000)
23 | sum(1, 2)
24 | }.start()
25 |
26 | verify(timeout = 3000) { sum(1, 2) }
27 | }
28 |
29 | }
30 |
31 | class MockCls {
32 |
33 | fun sum(num1: Int, num2: Int): Int {
34 | return num1 + num2
35 | }
36 |
37 | }
38 |
39 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/Test15PrivateMethodsAndProperties.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import com.google.common.truth.Truth
4 | import io.mockk.clearMocks
5 | import io.mockk.every
6 | import io.mockk.spyk
7 | import org.junit.jupiter.api.AfterEach
8 | import org.junit.jupiter.api.BeforeEach
9 | import org.junit.jupiter.api.Test
10 |
11 | class Test15PrivateMethodsAndProperties {
12 |
13 | private val levelManager = spyk(LevelManager(), recordPrivateCalls = true)
14 |
15 | // private val levelManager: LevelManager = mockk()
16 | private lateinit var player: Player
17 |
18 | @BeforeEach
19 | fun setUp() {
20 | player = Player(levelManager)
21 | }
22 |
23 | @AfterEach
24 | fun tearDown() {
25 | clearMocks(levelManager)
26 | }
27 |
28 | @Test
29 | fun testStatAndLevel() {
30 |
31 | // Given
32 | // every { levelManager getProperty "stat" } returns 20
33 | // every { levelManager getProperty "level" } returns 20
34 |
35 | every { levelManager setProperty "stat" value (20) }
36 | every { levelManager setProperty "level" value (20) }
37 |
38 | // levelManager.level = 20
39 | // levelManager.stat = 20
40 |
41 | // When
42 | val expected = player.isAboveLevelAndStat()
43 |
44 | // Then
45 | Truth.assertThat(expected).isTrue()
46 |
47 | }
48 |
49 | }
50 |
51 | class Player(private val levelManager: LevelManager) {
52 |
53 |
54 | fun isAboveLevelAndStat(): Boolean {
55 | return levelManager.isAboveLevelAndStat()
56 | }
57 |
58 | }
59 |
60 | class LevelManager {
61 |
62 | var stat = 0
63 | var level = 0
64 |
65 | fun isAboveLevelAndStat(): Boolean {
66 | return stat > 10 && level > 10
67 | }
68 |
69 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/Test15ReturnUnit.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import io.mockk.Runs
4 | import io.mockk.every
5 | import io.mockk.just
6 | import io.mockk.mockk
7 | import io.mockk.verify
8 | import org.junit.jupiter.api.Test
9 |
10 | class Test15ReturnUnit {
11 |
12 | class MockedClass {
13 | fun sum(a: Int, b: Int): Unit {
14 | println(a + b)
15 | }
16 | }
17 |
18 | @Test
19 | fun `Test unit function`() {
20 |
21 | val obj = mockk()
22 |
23 | every { obj.sum(any(), any()) } just Runs
24 |
25 | obj.sum(1, 1)
26 | obj.sum(1, 2)
27 | obj.sum(1, 3)
28 |
29 | verify {
30 | obj.sum(1, 1)
31 | obj.sum(1, 2)
32 | obj.sum(1, 3)
33 | }
34 |
35 | }
36 |
37 |
38 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/Test1ExtendWith.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.model_math_application.CalculatorService
4 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.model_math_application.MathApplication
5 | import io.mockk.every
6 | import io.mockk.impl.annotations.InjectMockKs
7 | import io.mockk.impl.annotations.MockK
8 | import io.mockk.junit5.MockKExtension
9 | import io.mockk.verify
10 | import org.junit.jupiter.api.*
11 | import org.junit.jupiter.api.extension.ExtendWith
12 |
13 |
14 | /**
15 | * Methods with @BeforeAll and @AfterAll annotations which are static methods should be inside
16 | * Companion object.
17 | *
18 | * Mocck is either used with @ExtendWith(MockKExtension::class) or
19 | * MockKAnnotations.init(this) is called in method with @BeforeEach annotation
20 | *
21 | */
22 | @ExtendWith(MockKExtension::class)
23 | internal class Test1ExtendWith {
24 |
25 | @InjectMockKs
26 | private lateinit var mathApplication: MathApplication
27 |
28 | @MockK
29 | private lateinit var calcService: CalculatorService
30 |
31 | companion object {
32 |
33 | @BeforeAll
34 | @JvmStatic
35 | fun beforeAllTestCases() {
36 | println("Runs once before all test cases.")
37 | }
38 |
39 | @AfterAll
40 | @JvmStatic
41 | fun afterAllTestCases() {
42 | println("Runs once after all test cases.")
43 | }
44 | }
45 |
46 |
47 | @Test
48 | fun `Add two numbers`() {
49 |
50 | // Given
51 | every { calcService.add(10.0, 20.0) } returns 30.0
52 |
53 | // When
54 | val expected = mathApplication.add(10.0, 20.0)
55 |
56 | // Then
57 | Assertions.assertEquals(expected, 30.0)
58 | // Verify that add method is called with 10.0 and 20.0 parameters
59 | verify(exactly = 1) { calcService.add(10.0, 20.0) }
60 | }
61 |
62 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/Test1WithInit.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.model_math_application.CalculatorService
4 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.model_math_application.MathApplication
5 | import io.mockk.MockKAnnotations.init
6 | import io.mockk.impl.annotations.InjectMockKs
7 | import io.mockk.impl.annotations.MockK
8 | import io.mockk.verify
9 | import org.junit.jupiter.api.Assertions
10 | import org.junit.jupiter.api.BeforeEach
11 | import org.junit.jupiter.api.Test
12 |
13 | class Test1WithInit {
14 |
15 | //@InjectMockKs annotation is used to create and inject the mock object
16 | @InjectMockKs
17 | private lateinit var mathApplication: MathApplication
18 | //@MockK annotation is used to create the mock object to be injected
19 | @MockK
20 | private lateinit var calcService: CalculatorService
21 |
22 | @BeforeEach
23 | fun setUp() {
24 | // 🔥🔥 Initializes mock objects annotated with MockKAnnotations
25 | init(this)
26 | }
27 |
28 |
29 | @Test
30 | fun `Add two numbers`() {
31 |
32 | // Given
33 | // every { calcService.add(10.0, 20.0) } returns 30.0
34 |
35 | // When
36 | val expected = mathApplication.add(10.0, 20.0)
37 |
38 | // Then
39 | Assertions.assertEquals(expected, 30.0)
40 | // Verify that add method is called with 10.0 and 20.0 parameters
41 | verify(exactly = 1) { calcService.add(10.0, 20.0) }
42 | }
43 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/Test1WithMockK.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.model_math_application.CalculatorService
4 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.model_math_application.MathApplication
5 | import io.mockk.every
6 | import io.mockk.mockk
7 | import io.mockk.verify
8 | import org.junit.jupiter.api.Assertions
9 | import org.junit.jupiter.api.BeforeEach
10 | import org.junit.jupiter.api.Test
11 |
12 | class Test1WithMockK {
13 |
14 |
15 | private lateinit var mathApplication: MathApplication
16 | private lateinit var calcService: CalculatorService
17 |
18 | @BeforeEach
19 | fun setUp() {
20 | mathApplication = MathApplication()
21 |
22 | calcService = mockk()
23 |
24 | mathApplication.calcService = calcService
25 | }
26 |
27 |
28 | @Test
29 | fun `Add two numbers`() {
30 |
31 | // Given
32 | every { calcService.add(10.0, 20.0) } returns 30.0
33 |
34 | // When
35 | val expected = mathApplication.add(10.0, 20.0)
36 |
37 | // Then
38 | Assertions.assertEquals(expected, 30.0)
39 | // Verify that add method is called with 10.0 and 20.0 parameters
40 | verify(exactly = 1) { calcService.add(10.0, 20.0) }
41 | }
42 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/Test2ObjectMockK.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import io.mockk.every
4 | import io.mockk.mockkObject
5 | import io.mockk.unmockkAll
6 | import io.mockk.verify
7 | import org.junit.jupiter.api.AfterEach
8 | import org.junit.jupiter.api.Assertions.assertEquals
9 | import org.junit.jupiter.api.BeforeEach
10 | import org.junit.jupiter.api.Test
11 |
12 |
13 | class Test2ObjectMockK {
14 |
15 | @BeforeEach
16 | fun setUp() {
17 | mockkObject(CalculusObject)
18 | mockkObject(MockObj)
19 | every { MockObj.add(1, 2) } returns 55
20 | }
21 |
22 | @Test
23 | fun willUseMockBehaviour() {
24 | assertEquals(55, MockObj.add(1, 2))
25 | }
26 |
27 | @AfterEach
28 | fun afterTests() {
29 | unmockkAll()
30 | // or unmockkObject(MockObj)
31 | }
32 |
33 |
34 | @Test
35 | fun `Add two numbers with mock Object `() {
36 |
37 | // CalculusObject returns real value of it's method
38 | assertEquals(3, CalculusObject.add(1, 2))
39 |
40 | // Mocked method here
41 | every { CalculusObject.add(1, 2) } returns 4
42 |
43 | assertEquals(4, CalculusObject.add(1, 2))
44 |
45 | verify(exactly = 2) { CalculusObject.add(1, 2) }
46 |
47 | }
48 |
49 | }
50 |
51 |
52 | object CalculusObject {
53 |
54 | fun add(input1: Int, input2: Int): Int {
55 | return input1 + input2
56 | }
57 | }
58 |
59 | object MockObj {
60 | fun add(a: Int, b: Int) = a + b
61 | }
62 |
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/Test4MockClassEnumConstructor.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.car.Car
4 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.car.Direction
5 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.car.Outcome
6 | import io.mockk.every
7 | import io.mockk.mockkClass
8 | import io.mockk.mockkObject
9 | import io.mockk.verify
10 | import org.junit.jupiter.api.Assertions.assertEquals
11 | import org.junit.jupiter.api.Nested
12 | import org.junit.jupiter.api.Test
13 |
14 | class Test4MockClassEnumConstructor {
15 |
16 | @Nested
17 | inner class Test4MockClass {
18 |
19 | private val car = mockkClass(Car::class)
20 |
21 | @Test
22 | fun `Class mock test`() {
23 |
24 | // Given
25 | every { car.drive(Direction.NORTH) } returns Outcome.OK
26 |
27 | // When
28 | car.drive(Direction.NORTH) // returns OK
29 |
30 | // Then
31 | verify { car.drive(Direction.NORTH) }
32 | }
33 | }
34 |
35 | /**
36 | * Enums can be mocked using mockkObject
37 | */
38 | @Nested
39 | inner class Test4MockEnum {
40 |
41 | @Test
42 | fun `Enum mock test`() {
43 |
44 | // Given
45 | mockkObject(Enumeration.CONSTANT)
46 | every { Enumeration.CONSTANT.goodInt } returns 42
47 |
48 | // Then
49 | assertEquals(42, Enumeration.CONSTANT.goodInt)
50 | }
51 |
52 | }
53 |
54 | @Nested
55 | inner class Test4MockConstructor
56 |
57 | }
58 |
59 | enum class Enumeration(val goodInt: Int) {
60 | CONSTANT(35),
61 | OTHER_CONSTANT(45);
62 | }
63 |
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/Test5Spy.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import com.google.common.truth.Truth
4 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.car.Car
5 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.car.Direction
6 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.car.Outcome
7 | import io.mockk.confirmVerified
8 | import io.mockk.every
9 | import io.mockk.mockk
10 | import io.mockk.spyk
11 | import io.mockk.verify
12 | import org.junit.jupiter.api.Test
13 |
14 | class Test5Spy {
15 | private val carSpy = spyk(Car()) // or spyk() to call the default constructor
16 | private val carMock = mockk()
17 |
18 | @Test
19 | fun spyTest() {
20 |
21 | // WHEN
22 | val result =
23 | carSpy.drive(Direction.NORTH) // returns whatever the real function of Car returns
24 |
25 | // THEN
26 | Truth.assertThat(result).isEqualTo(Outcome.OK)
27 | verify { carSpy.drive(Direction.NORTH) }
28 | confirmVerified(carSpy)
29 | }
30 |
31 | @Test
32 | fun mockTest() {
33 |
34 | // GIVEN
35 | every {
36 | carMock.drive(any())
37 | } returns Outcome.RECORDED
38 |
39 | // WHEN
40 | val result = carMock.drive(Direction.NORTH) // mocked this to return Outcome.RECORDED
41 |
42 | // THEN
43 | Truth.assertThat(result).isEqualTo(Outcome.RECORDED)
44 | verify { carMock.drive(Direction.NORTH) }
45 | confirmVerified(carMock)
46 | }
47 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/Test6HierarchicalMocking.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import io.mockk.every
4 | import io.mockk.mockk
5 | import org.junit.jupiter.api.Assertions.assertEquals
6 | import org.junit.jupiter.api.Test
7 |
8 | class Test5HierarchicalMocking {
9 |
10 | @Test
11 | fun ` Hierarchical Class mocked only field of property object`() {
12 |
13 | // given
14 | val foo = mockk {
15 | every { bar } returns mockk {
16 | every { nickname } returns "Tomato"
17 | }
18 | }
19 |
20 | // when
21 | val nickname = foo.bar.nickname
22 |
23 | // then
24 | assertEquals("Tomato", nickname)
25 | }
26 |
27 | @Test
28 | fun `given Hierarchical Class when Mocking It then Return Proper Value`() {
29 |
30 | // given
31 | val foo = mockk {
32 | every { name } returns "Karol"
33 | every { bar } returns mockk {
34 | every { nickname } returns "Tomato"
35 |
36 | // 🔥Method of Bar is stubbed here
37 | every { add(1, 2) } returns 5
38 | }
39 | }
40 |
41 | // when
42 | val name = foo.name
43 | val nickname = foo.bar.nickname
44 | val expected = foo.bar.add(1, 2)
45 |
46 | // then
47 | assertEquals("Karol", name)
48 | assertEquals("Tomato", nickname)
49 |
50 | assertEquals(expected, 5)
51 |
52 | }
53 |
54 | }
55 |
56 | class Foo {
57 | lateinit var name: String
58 | lateinit var bar: Bar
59 | }
60 |
61 | class Bar {
62 | lateinit var nickname: String
63 |
64 | fun add(num1: Int, num2: Int) = num1 + num2
65 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/Test6HierarchicalMocking2.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import io.mockk.every
4 | import io.mockk.mockk
5 | import org.junit.jupiter.api.Test
6 |
7 | class Test5HierarchicalMocking2 {
8 |
9 | @Test
10 | fun `test hierarchical structure of Address book`() {
11 |
12 | val addressBook = mockk {
13 |
14 | every { contacts } returns listOf(
15 |
16 | mockk {
17 | every { name } returns "John"
18 | every { telephone } returns "123-456-789"
19 | // 🔥 Fields of Address object is mocked
20 | every { address.city } returns "New-York"
21 | every { address.zip } returns "123-45"
22 | },
23 |
24 | mockk {
25 | every { name } returns "Alex"
26 | every { telephone } returns "789-456-123"
27 | // 🔥 Address object is mocked
28 | every { address } returns mockk {
29 | every { city } returns "Wroclaw"
30 | every { zip } returns "543-21"
31 | }
32 | }
33 |
34 | )
35 | }
36 |
37 | }
38 |
39 | }
40 |
41 | interface AddressBook {
42 | val contacts: List
43 | }
44 |
45 | interface Contact {
46 | val name: String
47 | val telephone: String
48 | val address: Address
49 | }
50 |
51 | interface Address {
52 | val city: String
53 | val zip: String
54 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/Test7PartialArgumentMatching.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk
2 |
3 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.car.Car
4 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.car.Direction
5 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.car.Outcome
6 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.model_math_application.CalculatorService
7 | import com.smarttoolfactory.tutorial2_1unit_testing.mockk.model_math_application.MathApplication
8 | import io.mockk.MockKAnnotations.init
9 | import io.mockk.confirmVerified
10 | import io.mockk.every
11 | import io.mockk.impl.annotations.InjectMockKs
12 | import io.mockk.impl.annotations.MockK
13 | import io.mockk.mockk
14 | import io.mockk.verify
15 | import org.junit.jupiter.api.Assertions
16 | import org.junit.jupiter.api.BeforeEach
17 | import org.junit.jupiter.api.Test
18 |
19 | /**
20 | * You can mix both regular arguments and matchers
21 | */
22 | class Test7PartialArgumentMatching {
23 |
24 | @InjectMockKs
25 | private lateinit var mathApplication: MathApplication
26 | @MockK
27 | private lateinit var calcService: CalculatorService
28 |
29 | @BeforeEach
30 | fun setUp() {
31 | init(this)
32 | }
33 |
34 | @Test
35 | fun `Add two numbers`() {
36 |
37 | // 🔥 Only one parameter of add function is mocked, This does not work with Mockito
38 |
39 | // Given
40 | every { calcService.add(any(), 20.0) } returns 30.0
41 |
42 | // When
43 | val expected = mathApplication.add(10.0, 20.0)
44 |
45 | // Then
46 | Assertions.assertEquals(expected, 30.0)
47 | // Verify that add method is called with 10.0 and 20.0 parameters
48 | verify(exactly = 1) { calcService.add(10.0, 20.0) }
49 | }
50 |
51 |
52 | /**
53 | * more(): matches if the value is more than the provided value via the compareTo function
54 | *
55 | *
56 | *
57 | */
58 | @Test
59 | fun `Partial Mocking with car`() {
60 |
61 | // Given
62 | val car = mockk()
63 |
64 | every {
65 | car.recordTelemetry(
66 | speed = more(50),
67 | direction = Direction.NORTH, // here eq() is used
68 | lat = any(),
69 | long = any()
70 | )
71 | } returns Outcome.RECORDED
72 |
73 | // When
74 | car.recordTelemetry(60, Direction.NORTH, 51.1377382, 17.0257142)
75 |
76 | // Then
77 | verify { car.recordTelemetry(60, Direction.NORTH, 51.1377382, 17.0257142) }
78 | confirmVerified(car)
79 |
80 | }
81 |
82 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/car/Car.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk.car
2 |
3 | class Car {
4 |
5 |
6 | fun drive(direction: Direction): Outcome {
7 |
8 | return when (direction) {
9 | Direction.NORTH -> Outcome.OK
10 | Direction.EAST -> Outcome.RECORDED
11 | Direction.SOUTH -> Outcome.RECORDED
12 | Direction.WEST -> Outcome.RECORDED
13 | }
14 | }
15 |
16 | fun recordTelemetry(
17 | speed: Int = 0,
18 | direction: Direction = Direction.NORTH,
19 | lat: Double = 0.0,
20 | long: Double = 0.0
21 | ) = Outcome.RECORDED
22 |
23 | fun door(doorType: DoorType): Car = apply {}
24 |
25 | fun windowState(): WindowState {
26 | return WindowState.UP
27 | }
28 |
29 | fun accelerate(fromSpeed: Int, toSpeed: Int) {
30 |
31 | }
32 |
33 |
34 | }
35 |
36 |
37 | enum class Direction(azimuth: Int) {
38 |
39 | NORTH(360),
40 | EAST(90),
41 | SOUTH(180),
42 | WEST(270)
43 |
44 | }
45 |
46 | enum class Outcome {
47 | OK,
48 | RECORDED
49 | }
50 |
51 | enum class DoorType {
52 | FRONT_LEFT,
53 | FRONT_RIGHT,
54 | BACK_LEFT,
55 | BACK_RIGHT
56 |
57 | }
58 |
59 | enum class WindowState {
60 | UP,
61 | DOWN
62 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/model_math_application/CalculatorService.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk.model_math_application
2 |
3 | interface CalculatorService {
4 | fun add(input1: Double, input2: Double): Double
5 | fun subtract(input1: Double, input2: Double): Double
6 | fun multiply(input1: Double, input2: Double): Double
7 | fun divide(input1: Double, input2: Double): Double
8 |
9 | fun print(text: String)
10 |
11 | fun log(text: String): String
12 | }
--------------------------------------------------------------------------------
/Tutorial2-1Unit-Testing/src/test/java/com/smarttoolfactory/tutorial2_1unit_testing/mockk/model_math_application/MathApplication.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_1unit_testing.mockk.model_math_application
2 |
3 | class MathApplication {
4 |
5 | var calcService: CalculatorService? = null
6 |
7 |
8 | fun add(input1: Double, input2: Double): Double {
9 | return calcService!!.add(input1, input2)
10 | }
11 |
12 |
13 | fun subtract(input1: Double, input2: Double): Double {
14 | return calcService!!.subtract(input1, input2)
15 | }
16 |
17 | fun multiply(input1: Double, input2: Double): Double {
18 | return calcService!!.multiply(input1, input2)
19 | }
20 |
21 | fun divide(input1: Double, input2: Double): Double {
22 | return calcService!!.divide(input1, input2)
23 | }
24 |
25 | fun print(text: String) {
26 | calcService!!.print(text)
27 | }
28 |
29 | fun log(text: String) = calcService!!.log(text)
30 | }
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("org.jetbrains.kotlin.android")
4 | alias(libs.plugins.compose)
5 | }
6 |
7 | android {
8 | namespace = "com.smarttoolfactory.tutorial2_2ui_testing"
9 | compileSdk = 35
10 |
11 | defaultConfig {
12 | applicationId = "com.smarttoolfactory.tutorial2_2ui_testing"
13 | minSdk = 24
14 | targetSdk = 35
15 | versionCode = 1
16 | versionName = "1.0"
17 |
18 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
19 | vectorDrawables {
20 | useSupportLibrary = true
21 | }
22 | }
23 |
24 | buildTypes {
25 | release {
26 | isMinifyEnabled = false
27 | proguardFiles(
28 | getDefaultProguardFile("proguard-android-optimize.txt"),
29 | "proguard-rules.pro"
30 | )
31 | }
32 | }
33 | compileOptions {
34 | sourceCompatibility = JavaVersion.VERSION_1_8
35 | targetCompatibility = JavaVersion.VERSION_1_8
36 | }
37 | kotlinOptions {
38 | jvmTarget = "1.8"
39 | }
40 | buildFeatures {
41 | compose = true
42 | }
43 | packaging {
44 | resources {
45 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
46 | }
47 | }
48 | }
49 |
50 | composeCompiler {
51 | // Configure compose compiler options if required
52 | enableStrongSkippingMode = true
53 | }
54 |
55 |
56 | dependencies {
57 |
58 | implementation(libs.androidx.core.ktx)
59 | implementation(libs.androidx.lifecycle.runtime.compose)
60 | implementation(libs.androidx.activity.compose)
61 | implementation(platform(libs.androidx.compose.bom))
62 | implementation(libs.ui)
63 | implementation(libs.androidx.ui.graphics)
64 | implementation(libs.androidx.ui.tooling.preview)
65 | implementation(libs.material3)
66 | testImplementation(libs.junit)
67 | androidTestImplementation(libs.androidx.test.ext.junit)
68 | androidTestImplementation(libs.androidx.test.espresso.core)
69 | androidTestImplementation(platform(libs.androidx.compose.bom))
70 | androidTestImplementation(libs.ui.test.junit4)
71 | debugImplementation(libs.ui.tooling)
72 | debugImplementation(libs.ui.test.manifest)
73 | }
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/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
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/androidTest/java/com/smarttoolfactory/tutorial2_2ui_testing/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_2ui_testing
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.*
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.smarttoolfactory.tutorial2_2ui_testing", appContext.packageName)
21 | }
22 | }
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/java/com/smarttoolfactory/tutorial2_2ui_testing/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_2ui_testing
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import androidx.compose.foundation.layout.fillMaxSize
7 | import androidx.compose.material3.MaterialTheme
8 | import androidx.compose.material3.Surface
9 | import androidx.compose.material3.Text
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.tooling.preview.Preview
13 | import com.smarttoolfactory.tutorial2_2ui_testing.ui.theme.ComposeTutorialsTheme
14 |
15 | class MainActivity : ComponentActivity() {
16 | override fun onCreate(savedInstanceState: Bundle?) {
17 | super.onCreate(savedInstanceState)
18 | setContent {
19 | ComposeTutorialsTheme {
20 | // A surface container using the 'background' color from the theme
21 | Surface(
22 | modifier = Modifier.fillMaxSize(),
23 | color = MaterialTheme.colorScheme.background
24 | ) {
25 | Greeting("Android")
26 | }
27 | }
28 | }
29 | }
30 | }
31 |
32 | @Composable
33 | fun Greeting(name: String, modifier: Modifier = Modifier) {
34 | Text(
35 | text = "Hello $name!",
36 | modifier = modifier
37 | )
38 | }
39 |
40 | @Preview(showBackground = true)
41 | @Composable
42 | fun GreetingPreview() {
43 | ComposeTutorialsTheme {
44 | Greeting("Android")
45 | }
46 | }
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/java/com/smarttoolfactory/tutorial2_2ui_testing/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_2ui_testing.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Purple80 = Color(0xFFD0BCFF)
6 | val PurpleGrey80 = Color(0xFFCCC2DC)
7 | val Pink80 = Color(0xFFEFB8C8)
8 |
9 | val Purple40 = Color(0xFF6650a4)
10 | val PurpleGrey40 = Color(0xFF625b71)
11 | val Pink40 = Color(0xFF7D5260)
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/java/com/smarttoolfactory/tutorial2_2ui_testing/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_2ui_testing.ui.theme
2 |
3 | import android.app.Activity
4 | import android.os.Build
5 | import androidx.compose.foundation.isSystemInDarkTheme
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.material3.darkColorScheme
8 | import androidx.compose.material3.dynamicDarkColorScheme
9 | import androidx.compose.material3.dynamicLightColorScheme
10 | import androidx.compose.material3.lightColorScheme
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.runtime.SideEffect
13 | import androidx.compose.ui.graphics.toArgb
14 | import androidx.compose.ui.platform.LocalContext
15 | import androidx.compose.ui.platform.LocalView
16 | import androidx.core.view.WindowCompat
17 |
18 | private val DarkColorScheme = darkColorScheme(
19 | primary = Purple80,
20 | secondary = PurpleGrey80,
21 | tertiary = Pink80
22 | )
23 |
24 | private val LightColorScheme = lightColorScheme(
25 | primary = Purple40,
26 | secondary = PurpleGrey40,
27 | tertiary = Pink40
28 |
29 | /* Other default colors to override
30 | background = Color(0xFFFFFBFE),
31 | surface = Color(0xFFFFFBFE),
32 | onPrimary = Color.White,
33 | onSecondary = Color.White,
34 | onTertiary = Color.White,
35 | onBackground = Color(0xFF1C1B1F),
36 | onSurface = Color(0xFF1C1B1F),
37 | */
38 | )
39 |
40 | @Composable
41 | fun ComposeTutorialsTheme(
42 | darkTheme: Boolean = isSystemInDarkTheme(),
43 | // Dynamic color is available on Android 12+
44 | dynamicColor: Boolean = true,
45 | content: @Composable () -> Unit
46 | ) {
47 | val colorScheme = when {
48 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
49 | val context = LocalContext.current
50 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
51 | }
52 |
53 | darkTheme -> DarkColorScheme
54 | else -> LightColorScheme
55 | }
56 | val view = LocalView.current
57 | if (!view.isInEditMode) {
58 | SideEffect {
59 | val window = (view.context as Activity).window
60 | window.statusBarColor = colorScheme.primary.toArgb()
61 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
62 | }
63 | }
64 |
65 | MaterialTheme(
66 | colorScheme = colorScheme,
67 | typography = Typography,
68 | content = content
69 | )
70 | }
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/java/com/smarttoolfactory/tutorial2_2ui_testing/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial2_2ui_testing.ui.theme
2 |
3 | import androidx.compose.material3.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 | bodyLarge = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp,
15 | lineHeight = 24.sp,
16 | letterSpacing = 0.5.sp
17 | )
18 | /* Other default text styles to override
19 | titleLarge = TextStyle(
20 | fontFamily = FontFamily.Default,
21 | fontWeight = FontWeight.Normal,
22 | fontSize = 22.sp,
23 | lineHeight = 28.sp,
24 | letterSpacing = 0.sp
25 | ),
26 | labelSmall = TextStyle(
27 | fontFamily = FontFamily.Default,
28 | fontWeight = FontWeight.Medium,
29 | fontSize = 11.sp,
30 | lineHeight = 16.sp,
31 | letterSpacing = 0.5.sp
32 | )
33 | */
34 | )
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-2UI-Testing/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-2UI-Testing/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-2UI-Testing/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-2UI-Testing/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-2UI-Testing/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-2UI-Testing/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-2UI-Testing/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-2UI-Testing/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-2UI-Testing/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial2-2UI-Testing/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Tutorial2–2UI-Testing
3 |
--------------------------------------------------------------------------------
/Tutorial2-2UI-Testing/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.application)
3 | alias(libs.plugins.kotlin.android)
4 | alias(libs.plugins.compose)
5 | alias(libs.plugins.kotlin.parcelize)
6 | alias(libs.plugins.jetbrains.kotlin.serialization)
7 | alias(libs.plugins.hilt)
8 | alias(libs.plugins.ksp)
9 | id("kotlin-parcelize")
10 | }
11 |
12 | android {
13 | namespace = "com.smarttoolfactory.tutorial3_1navigation"
14 | compileSdk = libs.versions.compileSdk.get().toInt()
15 |
16 | defaultConfig {
17 | applicationId = "com.smarttoolfactory.tutorial3_1navigation"
18 | minSdk = libs.versions.minSdk.get().toInt()
19 | targetSdk = libs.versions.targetSdk.get().toInt()
20 | versionCode = 1
21 | versionName = "1.0"
22 |
23 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
24 | }
25 |
26 | buildTypes {
27 | release {
28 | isMinifyEnabled = false
29 | proguardFiles(
30 | getDefaultProguardFile("proguard-android-optimize.txt"),
31 | "proguard-rules.pro"
32 | )
33 | }
34 | }
35 | compileOptions {
36 | sourceCompatibility = JavaVersion.VERSION_17
37 | targetCompatibility = JavaVersion.VERSION_17
38 | }
39 |
40 | kotlinOptions { jvmTarget = "17" }
41 |
42 | buildFeatures {
43 | compose = true
44 | }
45 | }
46 |
47 | dependencies {
48 | implementation(libs.androidx.core.ktx)
49 | implementation(libs.androidx.lifecycle.runtime)
50 | implementation(libs.androidx.activity.compose)
51 | implementation(platform(libs.androidx.compose.bom))
52 | implementation(libs.androidx.compose.ui)
53 | implementation(libs.androidx.compose.ui.graphics)
54 | implementation(libs.androidx.compose.ui.tooling.preview)
55 | implementation(libs.androidx.compose.material)
56 | implementation(libs.androidx.compose.material3)
57 | implementation(libs.androidx.navigation.compose)
58 | implementation(libs.androidx.hilt.navigation.compose)
59 | implementation(libs.hilt.android)
60 | implementation(libs.kotlinx.serialization.json)
61 |
62 | ksp(libs.hilt.compiler)
63 |
64 | testImplementation(libs.junit)
65 | androidTestImplementation(libs.androidx.test.ext.junit)
66 | androidTestImplementation(libs.androidx.test.espresso.core)
67 | androidTestImplementation(platform(libs.androidx.compose.bom))
68 | androidTestImplementation(libs.androidx.compose.ui.test.junit4)
69 | debugImplementation(libs.androidx.compose.ui.tooling)
70 | debugImplementation(libs.androidx.compose.ui.test.manifest)
71 | }
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/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
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/androidTest/java/com/smarttoolfactory/tutorial3_1navigation/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial3_1navigation
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.*
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.smarttoolfactory.tutorial3_1navigation", appContext.packageName)
21 | }
22 | }
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/java/com/smarttoolfactory/tutorial3_1navigation/ExposedSectionMenu.kt:
--------------------------------------------------------------------------------
1 | @file:OptIn(ExperimentalMaterialApi::class)
2 |
3 | package com.smarttoolfactory.tutorial3_1navigation
4 |
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.padding
7 | import androidx.compose.material.DropdownMenuItem
8 | import androidx.compose.material.ExperimentalMaterialApi
9 | import androidx.compose.material.ExposedDropdownMenuBox
10 | import androidx.compose.material.ExposedDropdownMenuDefaults
11 | import androidx.compose.material.Text
12 | import androidx.compose.material.TextField
13 | import androidx.compose.runtime.Composable
14 | import androidx.compose.runtime.getValue
15 | import androidx.compose.runtime.mutableStateOf
16 | import androidx.compose.runtime.saveable.rememberSaveable
17 | import androidx.compose.runtime.setValue
18 | import androidx.compose.ui.Modifier
19 | import androidx.compose.ui.unit.dp
20 |
21 | @Composable
22 | fun ExposedSelectionMenu(
23 | title: String,
24 | index: Int,
25 | options: List,
26 | onSelected: (Int) -> Unit,
27 | ) {
28 |
29 | var expanded by rememberSaveable { mutableStateOf(false) }
30 | var selectedOptionText by rememberSaveable { mutableStateOf(options[index]) }
31 |
32 | ExposedDropdownMenuBox(
33 | modifier = Modifier
34 | .fillMaxWidth()
35 | .padding(vertical = 4.dp),
36 | expanded = expanded,
37 | onExpandedChange = {
38 | expanded = it
39 | }
40 | ) {
41 |
42 | TextField(
43 | modifier = Modifier.fillMaxWidth(),
44 | readOnly = true,
45 | value = selectedOptionText,
46 | onValueChange = { },
47 | label = { Text(title) },
48 | trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
49 | colors = ExposedDropdownMenuDefaults.textFieldColors()
50 | )
51 | ExposedDropdownMenu(
52 | modifier = Modifier.fillMaxWidth(),
53 | expanded = expanded,
54 | onDismissRequest = {
55 | expanded = false
56 |
57 | }
58 | ) {
59 | options.forEachIndexed { index: Int, selectionOption: String ->
60 | DropdownMenuItem(
61 | modifier = Modifier.fillMaxWidth(),
62 | content = {
63 | Text(text = selectionOption)
64 | },
65 | onClick = {
66 | selectedOptionText = selectionOption
67 | expanded = false
68 | onSelected(index)
69 | }
70 | )
71 | }
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/java/com/smarttoolfactory/tutorial3_1navigation/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial3_1navigation
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import androidx.activity.enableEdgeToEdge
7 | import androidx.compose.foundation.layout.consumeWindowInsets
8 | import androidx.compose.foundation.layout.padding
9 | import androidx.compose.material3.Scaffold
10 | import androidx.compose.material3.Surface
11 | import androidx.compose.ui.Modifier
12 | import com.smarttoolfactory.tutorial3_1navigation.ui.theme.ComposeTutorialsTheme
13 | import dagger.hilt.android.AndroidEntryPoint
14 |
15 | @AndroidEntryPoint
16 | class MainActivity : ComponentActivity() {
17 | override fun onCreate(savedInstanceState: Bundle?) {
18 | super.onCreate(savedInstanceState)
19 | enableEdgeToEdge()
20 | setContent {
21 |
22 | ComposeTutorialsTheme {
23 | Scaffold { innerPadding ->
24 | Surface(
25 | modifier = Modifier
26 | .padding(innerPadding)
27 | .consumeWindowInsets(innerPadding)
28 | ) {
29 | // Tutorial1Screen()
30 | // Tutorial2_1Screen()
31 | // Tutorial3_1Screen()
32 | // Tutorial3_2Screen()
33 | // Tutorial4_1Screen()
34 | // Tutorial4_2Screen()
35 | // Tutorial5_1Screen()
36 | // Tutorial5_2Screen()
37 | // Tutorial6_1Screen()
38 | // Tutorial7_1Screen()
39 | // Tutorial8_1Screen()
40 | Tutorial9_1Screen()
41 | }
42 | }
43 | }
44 | }
45 | }
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/java/com/smarttoolfactory/tutorial3_1navigation/NavContoller.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial3_1navigation
2 |
3 | import androidx.lifecycle.Lifecycle
4 | import androidx.navigation.NavBackStackEntry
5 | import androidx.navigation.NavDestination
6 | import androidx.navigation.NavGraph
7 |
8 |
9 | /**
10 | * If the lifecycle is not resumed it means this NavBackStackEntry already processed a nav event.
11 | *
12 | * This is used to de-duplicate navigation events.
13 | */
14 | internal fun NavBackStackEntry.lifecycleIsResumed() =
15 | this.lifecycle.currentState == Lifecycle.State.RESUMED
16 |
17 | private val NavGraph.startDestination: NavDestination?
18 | get() = findNode(startDestinationId)
19 |
20 | /**
21 | * Copied from similar function in NavigationUI.kt
22 | *
23 | * https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.kt
24 | */
25 | internal tailrec fun findStartDestination(graph: NavDestination): NavDestination {
26 | return if (graph is NavGraph) findStartDestination(graph.startDestination!!) else graph
27 | }
28 |
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/java/com/smarttoolfactory/tutorial3_1navigation/NavigationApplication.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial3_1navigation
2 |
3 | import android.app.Application
4 | import dagger.hilt.android.HiltAndroidApp
5 |
6 | @HiltAndroidApp
7 | class NavigationApplication : Application()
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/java/com/smarttoolfactory/tutorial3_1navigation/Tutorial8_2DynamicNavGraphDeeplink.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial3_1navigation
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.tooling.preview.Preview
5 |
6 | // TODO
7 | @Preview
8 | @Composable
9 | fun Tutorial8_2Screen() {
10 | MainContainer()
11 | }
12 |
13 | @Composable
14 | private fun MainContainer() {
15 |
16 | }
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/java/com/smarttoolfactory/tutorial3_1navigation/Util.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial3_1navigation
2 |
3 | import android.app.Notification
4 | import android.app.NotificationChannel
5 | import android.app.NotificationManager
6 | import android.app.PendingIntent
7 | import android.app.TaskStackBuilder
8 | import android.content.Context
9 | import android.content.Intent
10 | import android.os.Build
11 | import androidx.core.app.NotificationCompat
12 | import androidx.core.content.getSystemService
13 | import androidx.core.net.toUri
14 |
15 | const val id = "exampleId"
16 |
17 | fun showNotification(context: Context, uriString: String) {
18 |
19 | val deepLinkIntent = Intent(
20 | Intent.ACTION_VIEW,
21 | uriString.toUri(),
22 | context,
23 | MainActivity::class.java
24 | )
25 |
26 | val deepLinkPendingIntent: PendingIntent? = TaskStackBuilder
27 | .create(context).run {
28 | addNextIntentWithParentStack(deepLinkIntent)
29 | getPendingIntent(0, PendingIntent.FLAG_IMMUTABLE)
30 | }
31 |
32 |
33 | val notification: Notification = NotificationCompat.Builder(
34 | context,
35 | "channelId"
36 | )
37 | .setSmallIcon(R.drawable.ic_launcher_foreground)
38 | .setContentTitle("Go to app")
39 | .setContentText("Click to open Profile")
40 | .setPriority(NotificationCompat.PRIORITY_MAX)
41 | .setAutoCancel(true)
42 | .setContentIntent(deepLinkPendingIntent)
43 | .build()
44 |
45 | val notificationManager = context.getSystemService() as NotificationManager?
46 |
47 | notificationManager?.run {
48 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
49 | this.createNotificationChannel(
50 | NotificationChannel("channelId", "name", NotificationManager.IMPORTANCE_DEFAULT)
51 | )
52 | }
53 | this.notify(1, notification)
54 | }
55 |
56 | }
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/java/com/smarttoolfactory/tutorial3_1navigation/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial3_1navigation.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Purple80 = Color(0xFFD0BCFF)
6 | val PurpleGrey80 = Color(0xFFCCC2DC)
7 | val Pink80 = Color(0xFFEFB8C8)
8 |
9 | val Purple40 = Color(0xFF6650a4)
10 | val PurpleGrey40 = Color(0xFF625b71)
11 | val Pink40 = Color(0xFF7D5260)
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/java/com/smarttoolfactory/tutorial3_1navigation/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial3_1navigation.ui.theme
2 |
3 | import android.os.Build
4 | import androidx.compose.foundation.isSystemInDarkTheme
5 | import androidx.compose.material3.MaterialTheme
6 | import androidx.compose.material3.darkColorScheme
7 | import androidx.compose.material3.dynamicDarkColorScheme
8 | import androidx.compose.material3.dynamicLightColorScheme
9 | import androidx.compose.material3.lightColorScheme
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.platform.LocalContext
12 |
13 | private val DarkColorScheme = darkColorScheme(
14 | primary = Purple80,
15 | secondary = PurpleGrey80,
16 | tertiary = Pink80
17 | )
18 |
19 | private val LightColorScheme = lightColorScheme(
20 | primary = Purple40,
21 | secondary = PurpleGrey40,
22 | tertiary = Pink40
23 |
24 | /* Other default colors to override
25 | background = Color(0xFFFFFBFE),
26 | surface = Color(0xFFFFFBFE),
27 | onPrimary = Color.White,
28 | onSecondary = Color.White,
29 | onTertiary = Color.White,
30 | onBackground = Color(0xFF1C1B1F),
31 | onSurface = Color(0xFF1C1B1F),
32 | */
33 | )
34 |
35 | @Composable
36 | fun ComposeTutorialsTheme(
37 | darkTheme: Boolean = isSystemInDarkTheme(),
38 | // Dynamic color is available on Android 12+
39 | dynamicColor: Boolean = true,
40 | content: @Composable () -> Unit,
41 | ) {
42 | val colorScheme = when {
43 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
44 | val context = LocalContext.current
45 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
46 | }
47 |
48 | darkTheme -> DarkColorScheme
49 | else -> LightColorScheme
50 | }
51 |
52 | MaterialTheme(
53 | colorScheme = colorScheme,
54 | typography = Typography,
55 | content = content
56 | )
57 | }
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/java/com/smarttoolfactory/tutorial3_1navigation/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial3_1navigation.ui.theme
2 |
3 | import androidx.compose.material3.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 | bodyLarge = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp,
15 | lineHeight = 24.sp,
16 | letterSpacing = 0.5.sp
17 | )
18 | /* Other default text styles to override
19 | titleLarge = TextStyle(
20 | fontFamily = FontFamily.Default,
21 | fontWeight = FontWeight.Normal,
22 | fontSize = 22.sp,
23 | lineHeight = 28.sp,
24 | letterSpacing = 0.sp
25 | ),
26 | labelSmall = TextStyle(
27 | fontFamily = FontFamily.Default,
28 | fontWeight = FontWeight.Medium,
29 | fontSize = 11.sp,
30 | lineHeight = 16.sp,
31 | letterSpacing = 0.5.sp
32 | )
33 | */
34 | )
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial3-1Navigation/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial3-1Navigation/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial3-1Navigation/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial3-1Navigation/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial3-1Navigation/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial3-1Navigation/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial3-1Navigation/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial3-1Navigation/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial3-1Navigation/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/Tutorial3-1Navigation/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Tutorial3-1Navigation
3 |
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Tutorial3-1Navigation/src/test/java/com/smarttoolfactory/tutorial3_1navigation/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.tutorial3_1navigation
2 |
3 | import org.junit.Assert.*
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 | }
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.gradle.versions)
3 | alias(libs.plugins.version.catalog.update)
4 | alias(libs.plugins.android.application) apply false
5 | alias(libs.plugins.kotlin.android) apply false
6 | alias(libs.plugins.kotlin.parcelize) apply false
7 | alias(libs.plugins.compose) apply false
8 | alias(libs.plugins.jetbrains.kotlin.serialization) apply false
9 | alias(libs.plugins.ksp) apply false
10 | alias(libs.plugins.hilt) apply false
11 | }
12 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | android.defaults.buildfeatures.buildconfig=true
21 | android.nonTransitiveRClass=false
22 | android.nonFinalResIds=false
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/screenshots/intro.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/intro.gif
--------------------------------------------------------------------------------
/screenshots/tutorial-list.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial-list.jpg
--------------------------------------------------------------------------------
/screenshots/tutorial1_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial1_1.jpg
--------------------------------------------------------------------------------
/screenshots/tutorial1_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial1_2.jpg
--------------------------------------------------------------------------------
/screenshots/tutorial2_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial2_1.jpg
--------------------------------------------------------------------------------
/screenshots/tutorial2_10.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial2_10.jpeg
--------------------------------------------------------------------------------
/screenshots/tutorial2_10_4.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial2_10_4.jpeg
--------------------------------------------------------------------------------
/screenshots/tutorial2_10_5.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial2_10_5.jpeg
--------------------------------------------------------------------------------
/screenshots/tutorial2_11.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial2_11.gif
--------------------------------------------------------------------------------
/screenshots/tutorial2_12.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial2_12.gif
--------------------------------------------------------------------------------
/screenshots/tutorial2_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial2_2.jpg
--------------------------------------------------------------------------------
/screenshots/tutorial2_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial2_3.jpg
--------------------------------------------------------------------------------
/screenshots/tutorial2_4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial2_4.gif
--------------------------------------------------------------------------------
/screenshots/tutorial2_5.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial2_5.gif
--------------------------------------------------------------------------------
/screenshots/tutorial2_6.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial2_6.gif
--------------------------------------------------------------------------------
/screenshots/tutorial2_7.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial2_7.jpeg
--------------------------------------------------------------------------------
/screenshots/tutorial2_8.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial2_8.jpeg
--------------------------------------------------------------------------------
/screenshots/tutorial2_9.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial2_9.jpeg
--------------------------------------------------------------------------------
/screenshots/tutorial2_9_2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial2_9_2.jpeg
--------------------------------------------------------------------------------
/screenshots/tutorial3_10.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial3_10.gif
--------------------------------------------------------------------------------
/screenshots/tutorial3_1_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial3_1_1.png
--------------------------------------------------------------------------------
/screenshots/tutorial3_1_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial3_1_2.png
--------------------------------------------------------------------------------
/screenshots/tutorial3_1_3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial3_1_3.gif
--------------------------------------------------------------------------------
/screenshots/tutorial3_1_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial3_1_6.png
--------------------------------------------------------------------------------
/screenshots/tutorial3_1_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial3_1_7.png
--------------------------------------------------------------------------------
/screenshots/tutorial3_1_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial3_1_8.png
--------------------------------------------------------------------------------
/screenshots/tutorial3_2_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial3_2_1.png
--------------------------------------------------------------------------------
/screenshots/tutorial3_2_10.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial3_2_10.gif
--------------------------------------------------------------------------------
/screenshots/tutorial3_3_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial3_3_1.png
--------------------------------------------------------------------------------
/screenshots/tutorial3_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial3_4.png
--------------------------------------------------------------------------------
/screenshots/tutorial3_5.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial3_5.gif
--------------------------------------------------------------------------------
/screenshots/tutorial3_6_1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial3_6_1.gif
--------------------------------------------------------------------------------
/screenshots/tutorial3_6_2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial3_6_2.gif
--------------------------------------------------------------------------------
/screenshots/tutorial3_9.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial3_9.gif
--------------------------------------------------------------------------------
/screenshots/tutorial4_12.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial4_12.gif
--------------------------------------------------------------------------------
/screenshots/tutorial4_1_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial4_1_1.png
--------------------------------------------------------------------------------
/screenshots/tutorial4_2_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial4_2_3.png
--------------------------------------------------------------------------------
/screenshots/tutorial4_4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial4_4.gif
--------------------------------------------------------------------------------
/screenshots/tutorial4_5_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial4_5_1.png
--------------------------------------------------------------------------------
/screenshots/tutorial4_5_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial4_5_2.png
--------------------------------------------------------------------------------
/screenshots/tutorial4_7_3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial4_7_3.gif
--------------------------------------------------------------------------------
/screenshots/tutorial5_10_1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial5_10_1.gif
--------------------------------------------------------------------------------
/screenshots/tutorial5_11.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial5_11.gif
--------------------------------------------------------------------------------
/screenshots/tutorial5_1_1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial5_1_1.gif
--------------------------------------------------------------------------------
/screenshots/tutorial5_1_2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial5_1_2.gif
--------------------------------------------------------------------------------
/screenshots/tutorial5_1_3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial5_1_3.gif
--------------------------------------------------------------------------------
/screenshots/tutorial5_2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial5_2.gif
--------------------------------------------------------------------------------
/screenshots/tutorial5_3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial5_3.gif
--------------------------------------------------------------------------------
/screenshots/tutorial5_4_1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial5_4_1.gif
--------------------------------------------------------------------------------
/screenshots/tutorial5_4_3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial5_4_3.gif
--------------------------------------------------------------------------------
/screenshots/tutorial5_6_2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial5_6_2.gif
--------------------------------------------------------------------------------
/screenshots/tutorial5_6_4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial5_6_4.gif
--------------------------------------------------------------------------------
/screenshots/tutorial5_9_6.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial5_9_6.gif
--------------------------------------------------------------------------------
/screenshots/tutorial5_9_7.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial5_9_7.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_11.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_11.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_12.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_12.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_13.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_13.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_14.png
--------------------------------------------------------------------------------
/screenshots/tutorial6_15.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_15.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_17.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_17.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_1_1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_1_1.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_1_2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_1_2.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_1_3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_1_3.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_1_4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_1_4.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_1_5.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_1_5.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_1_6.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_1_6.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_20.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_20.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_21.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_21.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_23.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_23.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_24.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_24.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_25.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_25.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_2_1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_2_1.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_2_3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_2_3.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_4_2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_4_2.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_5.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_5.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_6.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_6.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_7.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_7.gif
--------------------------------------------------------------------------------
/screenshots/tutorial6_8_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_8_1.png
--------------------------------------------------------------------------------
/screenshots/tutorial6_9_1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/Jetpack-Compose-Tutorials/29f84457195aa2adb237065c96ea89aee3e3a75a/screenshots/tutorial6_9_1.gif
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | maven { url 'https://jitpack.io'}
14 | }
15 | }
16 | rootProject.name = "Compose Tutorials"
17 | include ':Tutorial1-1Basics'
18 | include ':Tutorial2-1Unit-Testing'
19 | include ':Tutorial2-2UI-Testing'
20 | include ':Tutorial3-1Navigation'
21 |
--------------------------------------------------------------------------------