├── .github └── FUNDING.yml ├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── deploymentTargetSelector.xml ├── git_toolbox_blame.xml ├── gradle.xml ├── kotlinc.xml ├── migrations.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── CONTRIBUTING.md ├── LICENCE ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── cavin │ │ └── designpatterns │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── cavin │ │ │ └── designpatterns │ │ │ ├── MainActivity.kt │ │ │ ├── patterns │ │ │ ├── abstract_factory │ │ │ │ ├── ThemeComponentFactory.kt │ │ │ │ ├── ThemeComponents.kt │ │ │ │ └── ThemeInterfaces.kt │ │ │ ├── adapter │ │ │ │ ├── WeatherData.kt │ │ │ │ ├── WeatherDataAdapter.kt │ │ │ │ └── WeatherPresentation.kt │ │ │ ├── bridge │ │ │ │ ├── Video.kt │ │ │ │ ├── VideoImpl.kt │ │ │ │ ├── VideoProcessor.kt │ │ │ │ └── VideoProcessorImpl.kt │ │ │ ├── chain_of_responsibility │ │ │ │ ├── ButtonInteractionHandler.kt │ │ │ │ ├── FormInteractionHandler.kt │ │ │ │ └── InteractionHandler.kt │ │ │ ├── command │ │ │ │ ├── TextCommand.kt │ │ │ │ ├── TextEditorController.kt │ │ │ │ └── UpdateTextCommand.kt │ │ │ ├── composite │ │ │ │ ├── CartItem.kt │ │ │ │ ├── Category.kt │ │ │ │ └── Product.kt │ │ │ ├── decorator │ │ │ │ ├── BaseCard.kt │ │ │ │ ├── BorderDecorator.kt │ │ │ │ ├── DecoratedCard.kt │ │ │ │ ├── PaddingDecorator.kt │ │ │ │ └── ShadowDecorator.kt │ │ │ ├── facade │ │ │ │ ├── AnalyticsManager.kt │ │ │ │ ├── AudioPlayer.kt │ │ │ │ ├── MusicPlayerFacade.kt │ │ │ │ └── NotificationManager.kt │ │ │ ├── factory │ │ │ │ ├── CardFactoryProvider.kt │ │ │ │ ├── NewsCardFactory.kt │ │ │ │ ├── NewsCardFactoryImpl.kt │ │ │ │ └── NewsItem.kt │ │ │ ├── flyweight │ │ │ │ ├── FlyWeight.kt │ │ │ │ ├── IconFactory.kt │ │ │ │ └── IconFlyWeight.kt │ │ │ ├── iterator │ │ │ │ ├── ConcreteImage.kt │ │ │ │ ├── ConcreteText.kt │ │ │ │ ├── UiComponent.kt │ │ │ │ └── WidgetParser.kt │ │ │ ├── prototype │ │ │ │ └── AppDocument.kt │ │ │ ├── proxy │ │ │ │ ├── WeatherService.kt │ │ │ │ ├── WeatherServiceImpl.kt │ │ │ │ └── WeatherServiceProxy.kt │ │ │ └── singleton │ │ │ │ ├── Singleton.kt │ │ │ │ └── ThemeConfig.kt │ │ │ ├── ui │ │ │ └── theme │ │ │ │ ├── Color.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ │ └── views │ │ │ ├── AbstractFactoryView.kt │ │ │ ├── AdapterView.kt │ │ │ ├── BridgeView.kt │ │ │ ├── ChainOfResponsibilityView.kt │ │ │ ├── CommandView.kt │ │ │ ├── CompositeView.kt │ │ │ ├── DecoratorView.kt │ │ │ ├── FacadeScreen.kt │ │ │ ├── FactoryView.kt │ │ │ ├── FlyWeightView.kt │ │ │ ├── InterpreterView.kt │ │ │ ├── ProtoTypeView.kt │ │ │ ├── ProxyView.kt │ │ │ └── SingletonView.kt │ └── res │ │ ├── drawable │ │ ├── ic_launcher_background.xml │ │ ├── ic_launcher_foreground.xml │ │ └── logo.png │ │ ├── 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 │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── cavin │ └── designpatterns │ └── ExampleUnitTest.kt ├── banner └── jetpack-compose-design-pattern.png ├── build.gradle.kts ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── readme.md └── settings.gradle.kts /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [cavin-macwan] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: cavin.macwan 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Jetpack Compose Design Patterns -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetSelector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/git_toolbox_blame.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Pull Request Process 9 | 10 | 1. Ensure there's no grammatical error in the readme file that you've written 11 | 2. Make sure to add the example of the Design pattern in the patterns folder and the usecase in the views folder 12 | 13 | ### Our Pledge 14 | 15 | In the interest of fostering an open and welcoming environment, we as 16 | contributors and maintainers pledge to making participation in our project and 17 | our community a harassment-free experience for everyone, regardless of age, body 18 | size, disability, ethnicity, gender identity and expression, level of experience, 19 | nationality, personal appearance, race, religion, or sexual identity and 20 | orientation. 21 | 22 | ### Our Standards 23 | 24 | Examples of behavior that contributes to creating a positive environment 25 | include: 26 | 27 | * Using welcoming and inclusive language 28 | * Being respectful of differing viewpoints and experiences 29 | * Gracefully accepting constructive criticism 30 | * Focusing on what is best for the community 31 | * Showing empathy towards other community members 32 | 33 | Examples of unacceptable behavior by participants include: 34 | 35 | * The use of sexualized language or imagery and unwelcome sexual attention or 36 | advances 37 | * Trolling, insulting/derogatory comments, and personal or political attacks 38 | * Public or private harassment 39 | * Publishing others' private information, such as a physical or electronic 40 | address, without explicit permission 41 | * Other conduct which could reasonably be considered inappropriate in a 42 | professional setting 43 | 44 | ### Our Responsibilities 45 | 46 | Project maintainers are responsible for clarifying the standards of acceptable 47 | behavior and are expected to take appropriate and fair corrective action in 48 | response to any instances of unacceptable behavior. 49 | 50 | Project maintainers have the right and responsibility to remove, edit, or 51 | reject comments, commits, code, wiki edits, issues, and other contributions 52 | that are not aligned to this Code of Conduct, or to ban temporarily or 53 | permanently any contributor for other behaviors that they deem inappropriate, 54 | threatening, offensive, or harmful. -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Mert Dogan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.android.application) 3 | alias(libs.plugins.kotlin.android) 4 | alias(libs.plugins.kotlin.compose) 5 | } 6 | 7 | android { 8 | namespace = "com.cavin.designpatterns" 9 | compileSdk = 35 10 | 11 | defaultConfig { 12 | applicationId = "com.cavin.designpatterns" 13 | minSdk = 24 14 | targetSdk = 35 15 | versionCode = 1 16 | versionName = "1.0" 17 | 18 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 19 | } 20 | 21 | buildTypes { 22 | release { 23 | isMinifyEnabled = false 24 | proguardFiles( 25 | getDefaultProguardFile("proguard-android-optimize.txt"), 26 | "proguard-rules.pro" 27 | ) 28 | } 29 | } 30 | compileOptions { 31 | sourceCompatibility = JavaVersion.VERSION_11 32 | targetCompatibility = JavaVersion.VERSION_11 33 | } 34 | kotlinOptions { 35 | jvmTarget = "11" 36 | } 37 | buildFeatures { 38 | compose = true 39 | } 40 | } 41 | 42 | dependencies { 43 | 44 | implementation(libs.androidx.core.ktx) 45 | implementation(libs.androidx.lifecycle.runtime.ktx) 46 | implementation(libs.androidx.activity.compose) 47 | implementation(platform(libs.androidx.compose.bom)) 48 | implementation(libs.androidx.ui) 49 | implementation(libs.androidx.ui.graphics) 50 | implementation(libs.androidx.ui.tooling.preview) 51 | implementation(libs.coil.compose) 52 | implementation(libs.coil.network.ktor3) 53 | implementation(libs.ktor.client.android) 54 | implementation(libs.androidx.material3) 55 | testImplementation(libs.junit) 56 | androidTestImplementation(libs.androidx.junit) 57 | androidTestImplementation(libs.androidx.espresso.core) 58 | androidTestImplementation(platform(libs.androidx.compose.bom)) 59 | androidTestImplementation(libs.androidx.ui.test.junit4) 60 | debugImplementation(libs.androidx.ui.tooling) 61 | debugImplementation(libs.androidx.ui.test.manifest) 62 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/cavin/designpatterns/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns 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.cavin.designpatterns", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 17 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.activity.enableEdgeToEdge 7 | import com.cavin.designpatterns.ui.theme.JetpackComposeDesignPatternsTheme 8 | import com.cavin.designpatterns.views.AbstractFactoryView 9 | import com.cavin.designpatterns.views.AdaptorView 10 | import com.cavin.designpatterns.views.BridgeView 11 | import com.cavin.designpatterns.views.ChainOfResponsibilityView 12 | import com.cavin.designpatterns.views.CommandPatternView 13 | import com.cavin.designpatterns.views.CompositeView 14 | import com.cavin.designpatterns.views.DecoratorPatternExample 15 | import com.cavin.designpatterns.views.FacadeView 16 | import com.cavin.designpatterns.views.FlyWeightView 17 | import com.cavin.designpatterns.views.InterpreterView 18 | import com.cavin.designpatterns.views.ProtoTypeView 19 | import com.cavin.designpatterns.views.ProxyView 20 | import com.cavin.designpatterns.views.SingletonView 21 | 22 | class MainActivity : ComponentActivity() { 23 | override fun onCreate(savedInstanceState: Bundle?) { 24 | super.onCreate(savedInstanceState) 25 | enableEdgeToEdge() 26 | setContent { 27 | JetpackComposeDesignPatternsTheme { 28 | CommandPatternView() 29 | } 30 | } 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/abstract_factory/ThemeComponentFactory.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.abstract_factory 2 | 3 | interface ThemeComponentsFactory { 4 | fun createButton(): ThemeButton 5 | fun createCard(): ThemeCard 6 | } 7 | 8 | class LightThemeFactory : ThemeComponentsFactory { 9 | override fun createButton(): ThemeButton = LightThemeButton() 10 | override fun createCard(): ThemeCard = LightThemeCard() 11 | } 12 | 13 | class DarkThemeFactory : ThemeComponentsFactory { 14 | override fun createButton(): ThemeButton = DarkThemeButton() 15 | override fun createCard(): ThemeCard = DarkThemeCard() 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/abstract_factory/ThemeComponents.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.abstract_factory 2 | 3 | import androidx.compose.foundation.layout.padding 4 | import androidx.compose.foundation.layout.size 5 | import androidx.compose.foundation.shape.RoundedCornerShape 6 | import androidx.compose.material3.Button 7 | import androidx.compose.material3.ButtonDefaults 8 | import androidx.compose.material3.Card 9 | import androidx.compose.material3.CardDefaults 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.graphics.Color 13 | import androidx.compose.ui.unit.dp 14 | 15 | class LightThemeButton : ThemeButton { 16 | @Composable 17 | override fun Create(onClick: () -> Unit, content: @Composable () -> Unit) { 18 | Button( 19 | onClick = onClick, 20 | colors = ButtonDefaults.buttonColors( 21 | containerColor = Color.White, 22 | contentColor = Color.Black 23 | ), 24 | shape = RoundedCornerShape(8.dp), 25 | modifier = Modifier.padding(8.dp) 26 | ) { 27 | content() 28 | } 29 | } 30 | } 31 | 32 | class LightThemeCard : ThemeCard { 33 | @Composable 34 | override fun Create(content: @Composable () -> Unit) { 35 | Card( 36 | shape = RoundedCornerShape(8.dp), 37 | colors = CardDefaults.cardColors( 38 | containerColor = Color.White, 39 | contentColor = Color.Black, 40 | ), 41 | modifier = Modifier 42 | .padding(8.dp) 43 | .size(200.dp) 44 | ) { 45 | content() 46 | } 47 | } 48 | } 49 | 50 | class DarkThemeButton : ThemeButton { 51 | @Composable 52 | override fun Create(onClick: () -> Unit, content: @Composable () -> Unit) { 53 | Button( 54 | onClick = onClick, 55 | colors = ButtonDefaults.buttonColors( 56 | containerColor = Color.Black, 57 | contentColor = Color.White 58 | ), 59 | shape = RoundedCornerShape(8.dp), 60 | modifier = Modifier.padding(8.dp) 61 | ) { 62 | content() 63 | } 64 | } 65 | } 66 | 67 | class DarkThemeCard : ThemeCard { 68 | @Composable 69 | override fun Create(content: @Composable () -> Unit) { 70 | Card( 71 | shape = RoundedCornerShape(8.dp), 72 | colors = CardDefaults.cardColors( 73 | containerColor = Color.Black, 74 | contentColor = Color.White, 75 | ), 76 | modifier = Modifier 77 | .padding(8.dp) 78 | .size(200.dp) 79 | ) { 80 | content() 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/abstract_factory/ThemeInterfaces.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.abstract_factory 2 | 3 | import androidx.compose.runtime.Composable 4 | 5 | interface ThemeButton { 6 | @Composable 7 | fun Create(onClick: () -> Unit, content: @Composable () -> Unit) 8 | } 9 | 10 | interface ThemeCard { 11 | @Composable 12 | fun Create(content: @Composable () -> Unit) 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/adapter/WeatherData.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.adapter 2 | 3 | data class WeatherData( 4 | val temperature: Float, 5 | val humidity: Float, 6 | val windSpeed: Float, 7 | val condition: String 8 | ) 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/adapter/WeatherDataAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.adapter 2 | 3 | import com.cavin.designpatterns.R 4 | 5 | class WeatherDataAdapter { 6 | fun adapt(data: WeatherData): WeatherPresentation { 7 | return WeatherPresentation( 8 | tempDisplay = "${data.temperature}°C", 9 | humidityDisplay = "${data.humidity}%", 10 | windDisplay = "${data.windSpeed} m/s", 11 | weatherIcon = when (data.condition.lowercase()) { 12 | "sunny" -> R.drawable.logo 13 | "cloudy" -> R.drawable.ic_launcher_background 14 | "rainy" -> R.drawable.ic_launcher_foreground 15 | else -> R.drawable.logo 16 | } 17 | ) 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/adapter/WeatherPresentation.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.adapter 2 | 3 | data class WeatherPresentation( 4 | val tempDisplay: String, 5 | val humidityDisplay: String, 6 | val windDisplay: String, 7 | val weatherIcon: Int 8 | ) 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/bridge/Video.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.bridge 2 | 3 | abstract class Video(private val processor: VideoProcessor) { 4 | abstract fun play(videoFile: String) 5 | 6 | protected fun process(videoFile: String) { 7 | processor.process(videoFile) 8 | } 9 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/bridge/VideoImpl.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.bridge 2 | 3 | class YoutubeVideo(processor: VideoProcessor) : Video(processor) { 4 | override fun play(videoFile: String) { 5 | process(videoFile) 6 | println("Playing $videoFile on YouTube.") 7 | } 8 | } 9 | 10 | 11 | class NetflixVideo(processor: VideoProcessor) : Video(processor) { 12 | override fun play(videoFile: String) { 13 | process(videoFile) 14 | println("Playing $videoFile on Netflix.") 15 | } 16 | } 17 | 18 | 19 | class AmazonPrimeVideo(processor: VideoProcessor) : Video(processor) { 20 | override fun play(videoFile: String) { 21 | process(videoFile) 22 | println("Playing $videoFile on Amazon Prime.") 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/bridge/VideoProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.bridge 2 | 3 | interface VideoProcessor { 4 | fun process(videoFile: String) 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/bridge/VideoProcessorImpl.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.bridge 2 | 3 | class HDProcessor : VideoProcessor { 4 | override fun process(videoFile: String) { 5 | println("$videoFile is being processed with HD quality.") 6 | } 7 | } 8 | 9 | class UHD4KProcessor : VideoProcessor { 10 | override fun process(videoFile: String) { 11 | println("$videoFile is being processed with UHD 4K quality.") 12 | } 13 | } 14 | 15 | class QUHD8KProcessor : VideoProcessor { 16 | override fun process(videoFile: String) { 17 | println("$videoFile is being processed with QUHD 8K quality.") 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/chain_of_responsibility/ButtonInteractionHandler.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.chain_of_responsibility 2 | 3 | class ButtonInteractionHandler : InteractionHandler() { 4 | override fun handleInteraction(interactionType: String, onResult: (String) -> Unit) { 5 | if (interactionType == "buttonClick") { 6 | onResult("Button Clicked: Button interaction handled.") 7 | } else { 8 | handleNext(interactionType, onResult) 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/chain_of_responsibility/FormInteractionHandler.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.chain_of_responsibility 2 | 3 | class FormInteractionHandler : InteractionHandler() { 4 | override fun handleInteraction(interactionType: String, onResult: (String) -> Unit) { 5 | if (interactionType == "formSubmit") { 6 | onResult("Form submitted successfully.") 7 | } else { 8 | handleNext(interactionType, onResult) 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/chain_of_responsibility/InteractionHandler.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.chain_of_responsibility 2 | 3 | 4 | abstract class InteractionHandler { 5 | private var nextHandler: InteractionHandler? = null 6 | 7 | fun setNextHandler(handler: InteractionHandler) { 8 | nextHandler = handler 9 | } 10 | 11 | fun handleNext(interactionType: String, onResult: (String) -> Unit) { 12 | if (nextHandler != null) { 13 | nextHandler!!.handleInteraction(interactionType, onResult) 14 | } else { 15 | onResult("Unrecognized interaction: $interactionType") 16 | } 17 | } 18 | 19 | abstract fun handleInteraction(interactionType: String, onResult: (String) -> Unit) 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/command/TextCommand.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.command 2 | 3 | interface TextCommand { 4 | fun execute() 5 | fun undo() 6 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/command/TextEditorController.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.command 2 | 3 | class TextEditorController { 4 | private val commandHistory = mutableListOf() 5 | private var currentCommandIndex = -1 6 | 7 | fun executeCommand(command: TextCommand) { 8 | if (currentCommandIndex != commandHistory.size - 1) { 9 | commandHistory.subList(currentCommandIndex + 1, commandHistory.size).clear() 10 | } 11 | commandHistory.add(command) 12 | currentCommandIndex++ 13 | command.execute() 14 | } 15 | 16 | fun undo() { 17 | if (currentCommandIndex >= 0) { 18 | commandHistory[currentCommandIndex].undo() 19 | currentCommandIndex-- 20 | } 21 | } 22 | 23 | fun redo() { 24 | if (currentCommandIndex < commandHistory.size - 1) { 25 | currentCommandIndex++ 26 | commandHistory[currentCommandIndex].execute() 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/command/UpdateTextCommand.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.command 2 | 3 | import androidx.compose.runtime.MutableState 4 | import androidx.compose.ui.text.TextRange 5 | import androidx.compose.ui.text.input.TextFieldValue 6 | 7 | class UpdateTextCommand( 8 | private val controller: TextFieldValue, 9 | private val onTextChanged: (TextFieldValue) -> Unit, 10 | private val newText: String 11 | ) : TextCommand { 12 | private val oldText = controller.text 13 | 14 | override fun execute() { 15 | val updatedText = TextFieldValue( 16 | text = newText, 17 | selection = TextRange(newText.length) // Move cursor to the end 18 | ) 19 | onTextChanged(updatedText) 20 | } 21 | 22 | override fun undo() { 23 | val updatedText = TextFieldValue( 24 | text = oldText, 25 | selection = TextRange(oldText.length) // Move cursor to the end 26 | ) 27 | onTextChanged(updatedText) 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/composite/CartItem.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.composite 2 | 3 | import androidx.compose.runtime.Composable 4 | 5 | interface CartItem { 6 | fun getName(): String 7 | 8 | fun getPrice(): Double 9 | 10 | @Composable 11 | fun Render() 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/composite/Category.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.composite 2 | 3 | import androidx.compose.foundation.clickable 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.foundation.layout.Row 6 | import androidx.compose.foundation.layout.Spacer 7 | import androidx.compose.foundation.layout.fillMaxWidth 8 | import androidx.compose.foundation.layout.padding 9 | import androidx.compose.material.icons.Icons 10 | import androidx.compose.material.icons.filled.ArrowDropDown 11 | import androidx.compose.material.icons.filled.KeyboardArrowUp 12 | import androidx.compose.material3.Icon 13 | import androidx.compose.material3.MaterialTheme 14 | import androidx.compose.material3.Text 15 | import androidx.compose.runtime.Composable 16 | import androidx.compose.runtime.getValue 17 | import androidx.compose.runtime.mutableStateOf 18 | import androidx.compose.runtime.remember 19 | import androidx.compose.runtime.setValue 20 | import androidx.compose.ui.Alignment 21 | import androidx.compose.ui.Modifier 22 | import androidx.compose.ui.unit.dp 23 | 24 | data class Category( 25 | private val name: String, 26 | val children: List, 27 | var isExpanded: Boolean = false 28 | ) : CartItem { 29 | override fun getName() = name 30 | override fun getPrice() = children.sumOf { it.getPrice() } 31 | 32 | @Composable 33 | override fun Render() { 34 | var expanded by remember { mutableStateOf(isExpanded) } 35 | Column(modifier = Modifier.fillMaxWidth().padding(8.dp)) { 36 | Row( 37 | modifier = Modifier 38 | .fillMaxWidth() 39 | .clickable { expanded = !expanded } 40 | .padding(8.dp), 41 | verticalAlignment = Alignment.CenterVertically 42 | ) { 43 | Text(text = name, style = MaterialTheme.typography.titleMedium) 44 | Spacer(modifier = Modifier.weight(1f)) 45 | Icon( 46 | imageVector = if (expanded) Icons.Default.ArrowDropDown else Icons.Default.KeyboardArrowUp, 47 | contentDescription = null 48 | ) 49 | } 50 | if (expanded) { 51 | Column { 52 | children.forEach { it.Render() } 53 | } 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/composite/Product.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.composite 2 | 3 | import androidx.compose.foundation.Image 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.foundation.layout.Row 6 | import androidx.compose.foundation.layout.Spacer 7 | import androidx.compose.foundation.layout.fillMaxWidth 8 | import androidx.compose.foundation.layout.padding 9 | import androidx.compose.foundation.layout.size 10 | import androidx.compose.foundation.layout.width 11 | import androidx.compose.foundation.shape.RoundedCornerShape 12 | import androidx.compose.material.icons.Icons 13 | import androidx.compose.material.icons.filled.Add 14 | import androidx.compose.material.icons.filled.Clear 15 | import androidx.compose.material3.Card 16 | import androidx.compose.material3.Icon 17 | import androidx.compose.material3.IconButton 18 | import androidx.compose.material3.MaterialTheme 19 | import androidx.compose.material3.Text 20 | import androidx.compose.runtime.Composable 21 | import androidx.compose.ui.Alignment 22 | import androidx.compose.ui.Modifier 23 | import androidx.compose.ui.draw.clip 24 | import androidx.compose.ui.unit.dp 25 | import coil3.compose.rememberAsyncImagePainter 26 | 27 | data class Product( 28 | val title: String, 29 | val description: String, 30 | val imageUrl: String, 31 | private val price: Double, 32 | var quantity: Int = 0 33 | ) : CartItem { 34 | override fun getName() = title 35 | override fun getPrice() = price * quantity 36 | 37 | @Composable 38 | override fun Render() { 39 | Card( 40 | modifier = Modifier 41 | .fillMaxWidth() 42 | .padding(8.dp) 43 | ) { 44 | Row( 45 | modifier = Modifier 46 | .padding(8.dp) 47 | .fillMaxWidth(), 48 | verticalAlignment = Alignment.CenterVertically 49 | ) { 50 | Image( 51 | painter = rememberAsyncImagePainter(imageUrl), 52 | contentDescription = null, 53 | modifier = Modifier 54 | .size(60.dp) 55 | .clip(RoundedCornerShape(8.dp)) 56 | ) 57 | Spacer(modifier = Modifier.width(8.dp)) 58 | Column(modifier = Modifier.weight(1f)) { 59 | Text(text = title, style = MaterialTheme.typography.titleMedium) 60 | Text(text = description, style = MaterialTheme.typography.bodyMedium) 61 | } 62 | Row { 63 | IconButton(onClick = { if (quantity > 0) quantity-- }) { 64 | Icon(imageVector = Icons.Default.Clear, contentDescription = "Remove") 65 | } 66 | Text(text = "$quantity", modifier = Modifier.align(Alignment.CenterVertically)) 67 | IconButton(onClick = { quantity++ }) { 68 | Icon(imageVector = Icons.Default.Add, contentDescription = "Add") 69 | } 70 | } 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/decorator/BaseCard.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.decorator 2 | 3 | import androidx.compose.foundation.layout.fillMaxWidth 4 | import androidx.compose.foundation.layout.padding 5 | import androidx.compose.foundation.layout.width 6 | import androidx.compose.material3.Card 7 | import androidx.compose.material3.CardDefaults 8 | import androidx.compose.material3.CardElevation 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.ui.Modifier 11 | import androidx.compose.ui.unit.dp 12 | 13 | @Composable 14 | fun BaseCard( 15 | modifier: Modifier = Modifier, 16 | content: @Composable () -> Unit, 17 | ) { 18 | Card( 19 | modifier = modifier 20 | .fillMaxWidth() 21 | .padding(8.dp), 22 | elevation = CardDefaults.cardElevation(2.dp) 23 | ) { 24 | content() 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/decorator/BorderDecorator.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.decorator 2 | 3 | import androidx.compose.foundation.border 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.graphics.Color 8 | import androidx.compose.ui.unit.dp 9 | 10 | @Composable 11 | fun BorderDecorator( 12 | modifier: Modifier = Modifier, 13 | content: @Composable () -> Unit 14 | ) { 15 | Box( 16 | modifier = modifier.border(2.dp, Color.Blue) 17 | ) { 18 | content() 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/decorator/DecoratedCard.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.decorator 2 | 3 | import androidx.compose.foundation.layout.padding 4 | import androidx.compose.material3.Text 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.unit.dp 8 | 9 | @Composable 10 | fun DecoratedCard() { 11 | ShadowDecorator { 12 | BorderDecorator { 13 | PaddingDecorator { 14 | BaseCard { 15 | Text( 16 | text = "This is a decorated card!", 17 | modifier = Modifier.padding(8.dp) 18 | ) 19 | } 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/decorator/PaddingDecorator.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.decorator 2 | 3 | import androidx.compose.foundation.layout.Box 4 | import androidx.compose.foundation.layout.fillMaxWidth 5 | import androidx.compose.foundation.layout.padding 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Modifier 8 | import androidx.compose.ui.unit.dp 9 | 10 | @Composable 11 | fun PaddingDecorator( 12 | modifier: Modifier = Modifier, 13 | content: @Composable () -> Unit 14 | ) { 15 | Box( 16 | modifier = modifier.padding(16.dp), 17 | ) { 18 | content() 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/decorator/ShadowDecorator.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.decorator 2 | 3 | import androidx.compose.foundation.layout.Box 4 | import androidx.compose.foundation.layout.fillMaxWidth 5 | import androidx.compose.foundation.shape.RoundedCornerShape 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Modifier 8 | import androidx.compose.ui.draw.shadow 9 | import androidx.compose.ui.unit.dp 10 | 11 | @Composable 12 | fun ShadowDecorator( 13 | modifier: Modifier = Modifier, 14 | content: @Composable () -> Unit 15 | ) { 16 | Box( 17 | modifier = modifier.shadow(2.dp, shape = RoundedCornerShape(8.dp)) 18 | ) { 19 | content() 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/facade/AnalyticsManager.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.facade 2 | 3 | class AnalyticsManager { 4 | fun logEvent(event: String) { 5 | println("Logging event: $event") 6 | } 7 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/facade/AudioPlayer.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.facade 2 | 3 | class AudioPlayer { 4 | fun play(track: String) { 5 | println("Playing track: $track") 6 | } 7 | 8 | fun pause() { 9 | println("Audio paused") 10 | } 11 | 12 | fun stop() { 13 | println("Audio stopped") 14 | } 15 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/facade/MusicPlayerFacade.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.facade 2 | 3 | class MusicPlayerFacade( 4 | private val audioPlayer: AudioPlayer, 5 | private val notificationManager: NotificationManager, 6 | private val analyticsManager: AnalyticsManager 7 | ) { 8 | 9 | fun playTrack(track: String) { 10 | audioPlayer.play(track) 11 | notificationManager.showNotification(track) 12 | analyticsManager.logEvent("Track played: $track") 13 | } 14 | 15 | fun pauseTrack() { 16 | audioPlayer.pause() 17 | analyticsManager.logEvent("Track paused") 18 | } 19 | 20 | fun stopTrack() { 21 | audioPlayer.stop() 22 | notificationManager.clearNotification() 23 | analyticsManager.logEvent("Track stopped") 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/facade/NotificationManager.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.facade 2 | 3 | class NotificationManager { 4 | fun showNotification(track: String) { 5 | println("Showing notification: Now playing $track") 6 | } 7 | 8 | fun clearNotification() { 9 | println("Clearing notification") 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/factory/CardFactoryProvider.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.factory 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.CompositionLocalProvider 5 | import androidx.compose.runtime.staticCompositionLocalOf 6 | 7 | val LocalCardFactory = staticCompositionLocalOf { 8 | SimpleCardFactory() // Default 9 | } 10 | 11 | enum class CardType { SIMPLE, RICH } 12 | 13 | @Composable 14 | fun CardFactoryProvider( 15 | cardType: CardType = CardType.SIMPLE, 16 | content: @Composable () -> Unit 17 | ) { 18 | val factory = when (cardType) { 19 | CardType.SIMPLE -> SimpleCardFactory() 20 | CardType.RICH -> RichCardFactory() 21 | } 22 | CompositionLocalProvider(LocalCardFactory provides factory) { 23 | content() 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/factory/NewsCardFactory.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.factory 2 | 3 | import androidx.compose.runtime.Composable 4 | 5 | interface NewsCardFactory { 6 | @Composable 7 | fun CreateCard(newsItem: NewsItem) 8 | } 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/factory/NewsCardFactoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.factory 2 | 3 | import androidx.compose.foundation.layout.Column 4 | import androidx.compose.foundation.layout.fillMaxWidth 5 | import androidx.compose.foundation.layout.height 6 | import androidx.compose.foundation.layout.padding 7 | import androidx.compose.foundation.shape.RoundedCornerShape 8 | import androidx.compose.material3.Card 9 | import androidx.compose.material3.MaterialTheme 10 | import androidx.compose.material3.Text 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.draw.clip 14 | import androidx.compose.ui.layout.ContentScale 15 | import androidx.compose.ui.unit.dp 16 | import coil3.compose.AsyncImage 17 | 18 | class SimpleCardFactory : NewsCardFactory { 19 | @Composable 20 | override fun CreateCard(newsItem: NewsItem) { 21 | Card( 22 | modifier = Modifier 23 | .fillMaxWidth() 24 | .padding(8.dp), 25 | shape = RoundedCornerShape(8.dp) 26 | ) { 27 | Column( 28 | modifier = Modifier.padding(16.dp) 29 | ) { 30 | Text(newsItem.title, style = MaterialTheme.typography.titleSmall) 31 | Text(newsItem.content, style = MaterialTheme.typography.bodyMedium) 32 | } 33 | } 34 | } 35 | } 36 | 37 | 38 | class RichCardFactory : NewsCardFactory { 39 | @Composable 40 | override fun CreateCard(newsItem: NewsItem) { 41 | Card( 42 | modifier = Modifier 43 | .fillMaxWidth() 44 | .padding(8.dp), 45 | shape = RoundedCornerShape(8.dp) 46 | ) { 47 | Column(modifier = Modifier.padding(16.dp)) { 48 | newsItem.imageUrl?.let { url -> 49 | AsyncImage( 50 | model = url, 51 | contentDescription = null, 52 | contentScale = ContentScale.FillBounds, 53 | modifier = Modifier 54 | .fillMaxWidth() 55 | .height(200.dp) 56 | .clip(RoundedCornerShape(3.dp)) 57 | ) 58 | } 59 | Text(newsItem.title, style = MaterialTheme.typography.titleSmall) 60 | Text(newsItem.content, style = MaterialTheme.typography.bodyMedium) 61 | 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/factory/NewsItem.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.factory 2 | 3 | data class NewsItem(val title: String, val content: String, val imageUrl: String?) -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/flyweight/FlyWeight.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.flyweight 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.ui.graphics.Color 5 | import androidx.compose.ui.unit.Dp 6 | 7 | interface Flyweight { 8 | 9 | @Composable 10 | fun render(color: Color, size: Dp) 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/flyweight/IconFactory.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.flyweight 2 | 3 | import androidx.compose.ui.graphics.vector.ImageVector 4 | 5 | object IconFactory { 6 | private val icons = mutableMapOf() 7 | 8 | fun getIcon(icon: ImageVector): IconFlyweight { 9 | return icons.getOrPut(icon) { IconFlyweight(icon) } 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/flyweight/IconFlyWeight.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.flyweight 2 | 3 | import androidx.compose.foundation.layout.size 4 | import androidx.compose.material3.Icon 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.graphics.Color 8 | import androidx.compose.ui.graphics.vector.ImageVector 9 | import androidx.compose.ui.unit.Dp 10 | 11 | class IconFlyweight(private val icon: ImageVector) : Flyweight { 12 | 13 | @Composable 14 | override fun render(color: Color, size: Dp) { 15 | Icon( 16 | imageVector = icon, 17 | contentDescription = null, 18 | tint = color, 19 | modifier = Modifier.size(size), 20 | ) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/iterator/ConcreteImage.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.iterator 2 | 3 | import androidx.compose.foundation.layout.size 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.ui.Modifier 6 | import androidx.compose.ui.unit.dp 7 | import coil3.compose.AsyncImage 8 | 9 | class ConcreteExpressionImage(private val url: String) : UIComponent { 10 | @Composable 11 | override fun interpret() { 12 | AsyncImage( 13 | model = url, 14 | contentDescription = null, 15 | modifier = Modifier.size(100.dp) 16 | ) 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/iterator/ConcreteText.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.iterator 2 | 3 | import androidx.compose.material3.Text 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.ui.text.TextStyle 6 | import androidx.compose.ui.unit.sp 7 | 8 | class ConcreteExpressionText(private val text: String) : UIComponent { 9 | @Composable 10 | override fun interpret() { 11 | Text(text = text, style = TextStyle(fontSize = 20.sp)) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/iterator/UiComponent.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.iterator 2 | 3 | import androidx.compose.runtime.Composable 4 | 5 | sealed interface UIComponent { 6 | @Composable 7 | fun interpret() 8 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/iterator/WidgetParser.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.iterator 2 | 3 | class WidgetParser { 4 | fun parseScript(script: String): List { 5 | val expressions = mutableListOf() 6 | script.lines().forEach { line -> 7 | val trimmedLine = line.trim() 8 | when { 9 | trimmedLine.startsWith("Text:") -> { 10 | val text = trimmedLine.substringAfter("Text:").trim() 11 | expressions.add(ConcreteExpressionText(text)) 12 | } 13 | trimmedLine.startsWith("Image:") -> { 14 | val url = trimmedLine.substringAfter("Image:").trim() 15 | if (url.startsWith("https://")) { 16 | expressions.add(ConcreteExpressionImage(url)) 17 | } 18 | } 19 | } 20 | } 21 | return expressions 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/prototype/AppDocument.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.prototype 2 | 3 | data class AppDocument( 4 | val title: String, 5 | val content: String, 6 | val author: String 7 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/proxy/WeatherService.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.proxy 2 | 3 | interface WeatherService { 4 | suspend fun getWeatherData(): String 5 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/proxy/WeatherServiceImpl.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.proxy 2 | 3 | class WeatherApiService : WeatherService { 4 | override suspend fun getWeatherData(): String { 5 | return "Sunny, 25°C" 6 | } 7 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/proxy/WeatherServiceProxy.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.proxy 2 | 3 | class WeatherServiceProxy(private val apiService: WeatherApiService = WeatherApiService()) : WeatherService { 4 | private var cachedData: String? = null 5 | 6 | override suspend fun getWeatherData(): String { 7 | return if (cachedData == null) { 8 | println("Fetching data from API...") 9 | cachedData = apiService.getWeatherData() 10 | cachedData!! 11 | } else { 12 | println("Returning cached data...") 13 | cachedData!! 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/singleton/Singleton.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.singleton 2 | 3 | // 1.Object Declaration** (Most Common) 4 | // Kotlin's `object` keyword inherently implements the singleton pattern. 5 | 6 | object MySingleton { 7 | fun doSomething() { 8 | println("Singleton Instance") 9 | } 10 | } 11 | 12 | 13 | // 2. Lazy Initialization 14 | // Use the `lazy` delegate to create a singleton only when accessed for the first time. 15 | 16 | class MySingleton2 private constructor() { 17 | companion object { 18 | val instance: MySingleton2 by lazy { MySingleton2() } 19 | } 20 | } 21 | 22 | 23 | // 3. Double-Checked Locking (Thread-Safe Singleton) 24 | // Ensures thread safety in a multithreaded environment. 25 | 26 | class MySingleton3 private constructor() { 27 | companion object { 28 | @Volatile 29 | private var instance: MySingleton3? = null 30 | 31 | fun getInstance(): MySingleton3 { 32 | return instance ?: synchronized(this) { 33 | instance ?: MySingleton3().also { instance = it } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/patterns/singleton/ThemeConfig.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.patterns.singleton 2 | 3 | object ThemeConfig { 4 | private var darkModeEnabled: Boolean = false 5 | 6 | fun isDarkModeEnabled(): Boolean = darkModeEnabled 7 | 8 | fun toggleDarkMode() { 9 | darkModeEnabled = !darkModeEnabled 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.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) -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.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.ui.platform.LocalContext 13 | 14 | private val DarkColorScheme = darkColorScheme( 15 | primary = Purple80, 16 | secondary = PurpleGrey80, 17 | tertiary = Pink80 18 | ) 19 | 20 | private val LightColorScheme = lightColorScheme( 21 | primary = Purple40, 22 | secondary = PurpleGrey40, 23 | tertiary = Pink40 24 | 25 | /* Other default colors to override 26 | background = Color(0xFFFFFBFE), 27 | surface = Color(0xFFFFFBFE), 28 | onPrimary = Color.White, 29 | onSecondary = Color.White, 30 | onTertiary = Color.White, 31 | onBackground = Color(0xFF1C1B1F), 32 | onSurface = Color(0xFF1C1B1F), 33 | */ 34 | ) 35 | 36 | @Composable 37 | fun JetpackComposeDesignPatternsTheme( 38 | darkTheme: Boolean = isSystemInDarkTheme(), 39 | // Dynamic color is available on Android 12+ 40 | dynamicColor: Boolean = true, 41 | content: @Composable () -> Unit 42 | ) { 43 | val colorScheme = when { 44 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { 45 | val context = LocalContext.current 46 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 47 | } 48 | 49 | darkTheme -> DarkColorScheme 50 | else -> LightColorScheme 51 | } 52 | 53 | MaterialTheme( 54 | colorScheme = colorScheme, 55 | typography = Typography, 56 | content = content 57 | ) 58 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.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 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/views/AbstractFactoryView.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.views 2 | 3 | import androidx.compose.foundation.layout.Column 4 | import androidx.compose.foundation.layout.fillMaxSize 5 | import androidx.compose.foundation.layout.padding 6 | import androidx.compose.material3.Scaffold 7 | import androidx.compose.material3.Text 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.runtime.getValue 10 | import androidx.compose.runtime.mutableStateOf 11 | import androidx.compose.runtime.remember 12 | import androidx.compose.runtime.setValue 13 | import androidx.compose.ui.Modifier 14 | import com.cavin.designpatterns.patterns.abstract_factory.DarkThemeFactory 15 | import com.cavin.designpatterns.patterns.abstract_factory.LightThemeFactory 16 | import com.cavin.designpatterns.patterns.abstract_factory.ThemeComponentsFactory 17 | 18 | @Composable 19 | fun AbstractFactoryView(modifier: Modifier = Modifier) { 20 | var isDarkTheme by remember { mutableStateOf(false) } 21 | val themeFactory: ThemeComponentsFactory = if (isDarkTheme) { 22 | DarkThemeFactory() 23 | } else { 24 | LightThemeFactory() 25 | } 26 | Scaffold { innerPadding -> 27 | Column( 28 | modifier = modifier 29 | .fillMaxSize() 30 | .padding(innerPadding) 31 | ) { 32 | 33 | themeFactory.createButton().Create( 34 | onClick = { isDarkTheme = !isDarkTheme } 35 | ) { 36 | Text("Switch Theme") 37 | } 38 | 39 | themeFactory.createCard().Create { 40 | Text("This is a themed card") 41 | } 42 | } 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/views/AdapterView.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.views 2 | 3 | import androidx.compose.foundation.Image 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.foundation.layout.padding 6 | import androidx.compose.foundation.layout.size 7 | import androidx.compose.material3.Text 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.runtime.remember 10 | import androidx.compose.ui.Modifier 11 | import androidx.compose.ui.res.painterResource 12 | import androidx.compose.ui.unit.dp 13 | import com.cavin.designpatterns.patterns.adapter.WeatherData 14 | import com.cavin.designpatterns.patterns.adapter.WeatherDataAdapter 15 | 16 | @Composable 17 | fun AdaptorView() { 18 | val dummyWeatherData = remember { 19 | WeatherData( 20 | temperature = 25.3f, 21 | humidity = 65.0f, 22 | windSpeed = 12.0f, 23 | condition = "Sunny" 24 | ) 25 | } 26 | val adapter = remember { WeatherDataAdapter() } 27 | val presentation = adapter.adapt(dummyWeatherData) 28 | 29 | Column(modifier = Modifier.padding(16.dp)) { 30 | Text("Temperature: ${presentation.tempDisplay}") 31 | Text("Humidity: ${presentation.humidityDisplay}") 32 | Text("Wind Speed: ${presentation.windDisplay}") 33 | Image( 34 | painter = painterResource(presentation.weatherIcon), 35 | contentDescription = "Weather Icon", 36 | modifier = Modifier.size(50.dp) 37 | ) 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/views/BridgeView.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.views 2 | 3 | import android.widget.Toast 4 | import androidx.compose.foundation.layout.Arrangement 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.material3.Button 10 | import androidx.compose.material3.Text 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.ui.Alignment 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.platform.LocalContext 15 | import androidx.compose.ui.unit.dp 16 | import com.cavin.designpatterns.patterns.bridge.AmazonPrimeVideo 17 | import com.cavin.designpatterns.patterns.bridge.HDProcessor 18 | import com.cavin.designpatterns.patterns.bridge.NetflixVideo 19 | import com.cavin.designpatterns.patterns.bridge.QUHD8KProcessor 20 | import com.cavin.designpatterns.patterns.bridge.UHD4KProcessor 21 | import com.cavin.designpatterns.patterns.bridge.YoutubeVideo 22 | 23 | @Composable 24 | fun BridgeView() { 25 | val context = LocalContext.current 26 | 27 | Column( 28 | modifier = Modifier.fillMaxSize(), 29 | verticalArrangement = Arrangement.Center, 30 | horizontalAlignment = Alignment.CenterHorizontally 31 | ) { 32 | Button( 33 | onClick = { 34 | val youtubeVideo = YoutubeVideo(HDProcessor()) 35 | youtubeVideo.play("video_hd.mp4") 36 | Toast.makeText(context, "Playing HD video on YouTube", Toast.LENGTH_SHORT).show() 37 | }) { 38 | Text("Watch HD Video on YouTube") 39 | } 40 | 41 | Spacer(modifier = Modifier.height(16.dp)) 42 | 43 | Button( 44 | onClick = { 45 | val netflixVideo = NetflixVideo(UHD4KProcessor()) 46 | netflixVideo.play("video_4k.mp4") 47 | Toast.makeText(context, "Playing UHD 4K video on Netflix", Toast.LENGTH_SHORT) 48 | .show() 49 | }) { 50 | Text("Watch UHD 4K Video on Netflix") 51 | } 52 | 53 | Spacer(modifier = Modifier.height(16.dp)) 54 | 55 | Button( 56 | onClick = { 57 | val amazonPrimeVideo = AmazonPrimeVideo(QUHD8KProcessor()) 58 | amazonPrimeVideo.play("video_8k.mp4") 59 | Toast.makeText(context, "Playing QUHD 8K video on Amazon Prime", Toast.LENGTH_SHORT) 60 | .show() 61 | }) { 62 | Text("Watch QUHD 8K Video on Amazon Prime") 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/views/ChainOfResponsibilityView.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.views 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.foundation.layout.Spacer 6 | import androidx.compose.foundation.layout.fillMaxSize 7 | import androidx.compose.foundation.layout.height 8 | import androidx.compose.foundation.layout.padding 9 | import androidx.compose.material3.Button 10 | import androidx.compose.material3.ExperimentalMaterial3Api 11 | import androidx.compose.material3.MaterialTheme 12 | import androidx.compose.material3.Scaffold 13 | import androidx.compose.material3.Text 14 | import androidx.compose.material3.TopAppBar 15 | import androidx.compose.runtime.Composable 16 | import androidx.compose.runtime.getValue 17 | import androidx.compose.runtime.mutableStateOf 18 | import androidx.compose.runtime.remember 19 | import androidx.compose.runtime.setValue 20 | import androidx.compose.ui.Alignment 21 | import androidx.compose.ui.Modifier 22 | import androidx.compose.ui.unit.dp 23 | import com.cavin.designpatterns.patterns.chain_of_responsibility.ButtonInteractionHandler 24 | import com.cavin.designpatterns.patterns.chain_of_responsibility.FormInteractionHandler 25 | 26 | @OptIn(ExperimentalMaterial3Api::class) 27 | @Composable 28 | fun ChainOfResponsibilityView() { 29 | var message by remember { mutableStateOf("") } 30 | 31 | // Initialize handlers 32 | val buttonHandler = ButtonInteractionHandler() 33 | val formHandler = FormInteractionHandler() 34 | buttonHandler.setNextHandler(formHandler) 35 | 36 | // UI 37 | Scaffold( 38 | topBar = { 39 | TopAppBar(title = { Text("Chain of Responsibility in Compose") }) 40 | }, 41 | content = { padding -> 42 | Column( 43 | modifier = Modifier 44 | .fillMaxSize() 45 | .padding(padding), 46 | verticalArrangement = Arrangement.Center, 47 | horizontalAlignment = Alignment.CenterHorizontally 48 | ) { 49 | Button(onClick = { 50 | buttonHandler.handleInteraction("buttonClick") { result -> 51 | message = result 52 | } 53 | }) { 54 | Text("Click Me") 55 | } 56 | 57 | Spacer(modifier = Modifier.height(16.dp)) 58 | 59 | Button(onClick = { 60 | buttonHandler.handleInteraction("formSubmit") { result -> 61 | message = result 62 | } 63 | }) { 64 | Text("Submit Form") 65 | } 66 | 67 | Spacer(modifier = Modifier.height(16.dp)) 68 | 69 | Button(onClick = { 70 | buttonHandler.handleInteraction("unknown") { result -> 71 | message = result 72 | } 73 | }) { 74 | Text("Unknown") 75 | } 76 | 77 | Spacer(modifier = Modifier.height(32.dp)) 78 | 79 | Text( 80 | text = message, 81 | style = MaterialTheme.typography.bodyLarge, 82 | modifier = Modifier.padding(16.dp) 83 | ) 84 | } 85 | } 86 | ) 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/views/CommandView.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.views 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.foundation.layout.Row 6 | import androidx.compose.foundation.layout.fillMaxSize 7 | import androidx.compose.foundation.layout.fillMaxWidth 8 | import androidx.compose.foundation.layout.padding 9 | import androidx.compose.material3.Button 10 | import androidx.compose.material3.ExperimentalMaterial3Api 11 | import androidx.compose.material3.Scaffold 12 | import androidx.compose.material3.Text 13 | import androidx.compose.material3.TextField 14 | import androidx.compose.material3.TopAppBar 15 | import androidx.compose.runtime.Composable 16 | import androidx.compose.runtime.mutableStateOf 17 | import androidx.compose.runtime.remember 18 | import androidx.compose.ui.Alignment 19 | import androidx.compose.ui.Modifier 20 | import androidx.compose.ui.text.input.TextFieldValue 21 | import androidx.compose.ui.unit.dp 22 | import com.cavin.designpatterns.patterns.command.TextEditorController 23 | import com.cavin.designpatterns.patterns.command.UpdateTextCommand 24 | 25 | @OptIn(ExperimentalMaterial3Api::class) 26 | @Composable 27 | fun CommandPatternView() { 28 | val textFieldState = remember { mutableStateOf(TextFieldValue(text = "")) } 29 | val textEditorController = remember { TextEditorController() } 30 | 31 | Scaffold( 32 | topBar = { 33 | TopAppBar(title = { Text("Command Pattern in Jetpack Compose") }) 34 | } 35 | ) { padding -> 36 | Column( 37 | modifier = Modifier 38 | .fillMaxSize() 39 | .padding(padding) 40 | .padding(16.dp), 41 | verticalArrangement = Arrangement.spacedBy(8.dp) 42 | ) { 43 | TextField( 44 | value = textFieldState.value, 45 | onValueChange = { newValue -> 46 | textEditorController.executeCommand( 47 | UpdateTextCommand( 48 | controller = textFieldState.value, 49 | onTextChanged = { updatedValue -> textFieldState.value = updatedValue }, 50 | newText = newValue.text 51 | ) 52 | ) 53 | }, 54 | modifier = Modifier.fillMaxWidth() 55 | ) 56 | 57 | Row( 58 | horizontalArrangement = Arrangement.spacedBy(8.dp), 59 | modifier = Modifier.fillMaxWidth() 60 | ) { 61 | Button( 62 | onClick = { textEditorController.undo() }, 63 | modifier = Modifier.weight(1f) 64 | ) { 65 | Text("Undo") 66 | } 67 | 68 | Button( 69 | onClick = { textEditorController.redo() }, 70 | modifier = Modifier.weight(1f) 71 | ) { 72 | Text("Redo") 73 | } 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/views/CompositeView.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.views 2 | 3 | import androidx.compose.foundation.layout.fillMaxSize 4 | import androidx.compose.foundation.layout.padding 5 | import androidx.compose.foundation.lazy.LazyColumn 6 | import androidx.compose.foundation.lazy.items 7 | import androidx.compose.material3.MaterialTheme 8 | import androidx.compose.material3.Scaffold 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.runtime.remember 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.tooling.preview.Preview 13 | import androidx.compose.ui.unit.dp 14 | import com.cavin.designpatterns.patterns.composite.Category 15 | import com.cavin.designpatterns.patterns.composite.Product 16 | 17 | @Composable 18 | fun CompositeView() { 19 | val categories = remember { 20 | listOf( 21 | Category( 22 | name = "Desktop Computer", 23 | children = listOf( 24 | Product( 25 | "Main Board", 26 | "Part of the computer", 27 | "https://via.placeholder.com/150", 28 | 1000.0 29 | ), 30 | Product( 31 | "CPU", 32 | "Part of the computer", 33 | "https://via.placeholder.com/150", 34 | 2000.0 35 | ) 36 | ) 37 | ), 38 | Category( 39 | name = "Car", 40 | children = listOf( 41 | Product( 42 | "Electric Car", 43 | "Type of the car", 44 | "https://via.placeholder.com/150", 45 | 9000.0 46 | ), 47 | Product( 48 | "Wheel", 49 | "Part of the car", 50 | "https://via.placeholder.com/150", 51 | 1000.0 52 | ) 53 | ) 54 | ) 55 | ) 56 | 57 | } 58 | 59 | Scaffold { innerPadding -> 60 | LazyColumn( 61 | modifier = Modifier 62 | .fillMaxSize() 63 | .padding(innerPadding) 64 | .padding(8.dp) 65 | ) { 66 | items(categories) { category -> 67 | category.Render() 68 | } 69 | } 70 | 71 | } 72 | } 73 | 74 | @Preview(showBackground = true) 75 | @Composable 76 | fun PreviewShoppingCart() { 77 | MaterialTheme { 78 | CompositeView() 79 | } 80 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/views/DecoratorView.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.views 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.foundation.layout.fillMaxSize 6 | import androidx.compose.foundation.layout.padding 7 | import androidx.compose.material3.ExperimentalMaterial3Api 8 | import androidx.compose.material3.Scaffold 9 | import androidx.compose.material3.Text 10 | import androidx.compose.material3.TopAppBar 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.ui.Alignment 13 | import androidx.compose.ui.Modifier 14 | import com.cavin.designpatterns.patterns.decorator.DecoratedCard 15 | 16 | @OptIn(ExperimentalMaterial3Api::class) 17 | @Composable 18 | fun DecoratorPatternExample() { 19 | Scaffold( 20 | topBar = { 21 | TopAppBar(title = { Text("Decorator Pattern Example") }) 22 | } 23 | ) { 24 | Column( 25 | modifier = Modifier 26 | .fillMaxSize() 27 | .padding(it), 28 | verticalArrangement = Arrangement.Center, 29 | horizontalAlignment = Alignment.CenterHorizontally 30 | ) { 31 | DecoratedCard() 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/views/FacadeScreen.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.views 2 | 3 | import androidx.compose.foundation.layout.Column 4 | import androidx.compose.foundation.layout.Spacer 5 | import androidx.compose.foundation.layout.height 6 | import androidx.compose.foundation.layout.padding 7 | import androidx.compose.material3.Button 8 | import androidx.compose.material3.MaterialTheme 9 | import androidx.compose.material3.Scaffold 10 | import androidx.compose.material3.Text 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.getValue 13 | import androidx.compose.runtime.mutableStateOf 14 | import androidx.compose.runtime.remember 15 | import androidx.compose.runtime.setValue 16 | import androidx.compose.ui.Modifier 17 | import androidx.compose.ui.unit.dp 18 | import com.cavin.designpatterns.patterns.facade.AnalyticsManager 19 | import com.cavin.designpatterns.patterns.facade.AudioPlayer 20 | import com.cavin.designpatterns.patterns.facade.MusicPlayerFacade 21 | import com.cavin.designpatterns.patterns.facade.NotificationManager 22 | 23 | @Composable 24 | fun FacadeView() { 25 | val musicPlayerFacade = remember { 26 | MusicPlayerFacade( 27 | audioPlayer = AudioPlayer(), 28 | notificationManager = NotificationManager(), 29 | analyticsManager = AnalyticsManager() 30 | ) 31 | 32 | } 33 | 34 | var isPlaying by remember { mutableStateOf(false) } 35 | val track = "My Favorite Song" 36 | 37 | Scaffold { 38 | 39 | Column(modifier = Modifier 40 | .padding(16.dp) 41 | .padding(it)) { 42 | Text("Music Player", style = MaterialTheme.typography.titleMedium) 43 | Spacer(modifier = Modifier.height(16.dp)) 44 | 45 | Button( 46 | onClick = { 47 | if (isPlaying) { 48 | musicPlayerFacade.pauseTrack() 49 | } else { 50 | musicPlayerFacade.playTrack(track) 51 | } 52 | isPlaying = !isPlaying 53 | }) { 54 | Text(if (isPlaying) "Pause" else "Play") 55 | } 56 | 57 | Spacer(modifier = Modifier.height(8.dp)) 58 | 59 | Button( 60 | onClick = { 61 | musicPlayerFacade.stopTrack() 62 | isPlaying = false 63 | }) { 64 | Text("Stop") 65 | } 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/views/FactoryView.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.views 2 | 3 | import androidx.compose.foundation.layout.Row 4 | import androidx.compose.foundation.layout.Spacer 5 | import androidx.compose.foundation.layout.padding 6 | import androidx.compose.foundation.layout.width 7 | import androidx.compose.foundation.lazy.LazyColumn 8 | import androidx.compose.foundation.lazy.items 9 | import androidx.compose.material3.Button 10 | import androidx.compose.material3.Scaffold 11 | import androidx.compose.material3.Text 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.getValue 14 | import androidx.compose.runtime.mutableStateOf 15 | import androidx.compose.runtime.remember 16 | import androidx.compose.runtime.setValue 17 | import androidx.compose.ui.Modifier 18 | import androidx.compose.ui.unit.dp 19 | import com.cavin.designpatterns.patterns.factory.CardFactoryProvider 20 | import com.cavin.designpatterns.patterns.factory.CardType 21 | import com.cavin.designpatterns.patterns.factory.LocalCardFactory 22 | import com.cavin.designpatterns.patterns.factory.NewsItem 23 | 24 | @Composable 25 | fun FactoryView() { 26 | 27 | val newsItems = remember { 28 | listOf( 29 | NewsItem("Title 1", "Content 1", "https://picsum.photos/200"), 30 | NewsItem("Title 2", "Content 2", "https://picsum.photos/100"), 31 | NewsItem("Title 3", "Content 3", "https://picsum.photos/300"), 32 | NewsItem("Title 4", "Content 4", "https://picsum.photos/400"), 33 | ) 34 | } 35 | 36 | 37 | Scaffold { innerPadding -> 38 | NewsFeed( 39 | newsItems = newsItems, 40 | modifier = Modifier.padding(innerPadding) 41 | ) 42 | } 43 | } 44 | 45 | @Composable 46 | fun NewsFeed( 47 | newsItems: List, 48 | modifier: Modifier, 49 | ) { 50 | var selectedCardType by remember { mutableStateOf(CardType.SIMPLE) } 51 | 52 | CardFactoryProvider(cardType = selectedCardType) { // Or dynamically choose based on item type 53 | LazyColumn(modifier = modifier) { 54 | item { 55 | Row(modifier = Modifier.padding(8.dp)) { 56 | Button(onClick = { selectedCardType = CardType.SIMPLE }) { 57 | Text("Simple Card") 58 | } 59 | Spacer(modifier = Modifier.width(8.dp)) 60 | Button(onClick = { selectedCardType = CardType.RICH }) { 61 | Text("Rich Card") 62 | } 63 | } 64 | } 65 | items(newsItems) { item -> 66 | val cardFactory = LocalCardFactory.current 67 | cardFactory.CreateCard(item) 68 | } 69 | } 70 | } 71 | } 72 | 73 | 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/views/FlyWeightView.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.views 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.foundation.layout.Row 6 | import androidx.compose.foundation.layout.Spacer 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.material.icons.Icons 12 | import androidx.compose.material.icons.filled.Favorite 13 | import androidx.compose.material.icons.filled.Share 14 | import androidx.compose.material3.Card 15 | import androidx.compose.material3.MaterialTheme 16 | import androidx.compose.material3.Text 17 | import androidx.compose.runtime.Composable 18 | import androidx.compose.runtime.remember 19 | import androidx.compose.ui.Modifier 20 | import androidx.compose.ui.graphics.Color 21 | import androidx.compose.ui.unit.dp 22 | import com.cavin.designpatterns.patterns.flyweight.IconFactory 23 | 24 | @Composable 25 | fun FlyWeightView() { 26 | val posts = remember { 27 | listOf( 28 | "Exploring the Flyweight Pattern in Jetpack Compose!", 29 | "Happy to share my latest project—excited for your feedback! 🎉", 30 | "Just finished a great coding session, feeling productive! 💻✨", 31 | "Check out this beautiful sunset I captured today! 🌅", 32 | "Learning Jetpack Compose has been a game changer. 🚀", 33 | "Enjoying a cozy day with some coffee and code ☕👩‍💻", 34 | "Remember: Consistency is key to growth! 💪", 35 | "What are your thoughts on declarative UI? Let's discuss! 📢", 36 | "Can't wait to deploy the new features next week! 🔥", 37 | "Embracing challenges—every day is a learning opportunity. 🌟" 38 | ) 39 | } 40 | Column( 41 | modifier = Modifier 42 | .fillMaxSize() 43 | .padding(16.dp) 44 | ) { 45 | posts.forEach { post -> 46 | PostItem(postContent = post) 47 | } 48 | } 49 | } 50 | 51 | 52 | @Composable 53 | fun PostItem(postContent: String) { 54 | val likeIcon = IconFactory.getIcon(Icons.Default.Favorite) 55 | val shareIcon = IconFactory.getIcon(Icons.Default.Share) 56 | 57 | Card (modifier = Modifier.padding(12.dp)){ 58 | Column( 59 | modifier = Modifier 60 | .fillMaxWidth() 61 | .padding(8.dp) 62 | ) { 63 | Text(text = postContent, style = MaterialTheme.typography.bodyMedium) 64 | Spacer(modifier = Modifier.height(10.dp)) 65 | Row( 66 | modifier = Modifier.fillMaxWidth(), 67 | horizontalArrangement = Arrangement.SpaceAround 68 | ) { 69 | likeIcon.render(color = Color.Red, size = 24.dp) 70 | shareIcon.render(color = Color.Blue, size = 24.dp) 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/views/InterpreterView.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.views 2 | 3 | import androidx.compose.foundation.border 4 | import androidx.compose.foundation.layout.Arrangement 5 | import androidx.compose.foundation.layout.Box 6 | import androidx.compose.foundation.layout.Column 7 | import androidx.compose.foundation.layout.Spacer 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.height 11 | import androidx.compose.foundation.layout.padding 12 | import androidx.compose.foundation.text.BasicTextField 13 | import androidx.compose.material3.Button 14 | import androidx.compose.material3.ExperimentalMaterial3Api 15 | import androidx.compose.material3.MaterialTheme 16 | import androidx.compose.material3.Scaffold 17 | import androidx.compose.material3.Text 18 | import androidx.compose.material3.TextField 19 | import androidx.compose.material3.TopAppBar 20 | import androidx.compose.runtime.Composable 21 | import androidx.compose.runtime.getValue 22 | import androidx.compose.runtime.mutableStateOf 23 | import androidx.compose.runtime.remember 24 | import androidx.compose.runtime.setValue 25 | import androidx.compose.ui.Modifier 26 | import androidx.compose.ui.unit.dp 27 | import com.cavin.designpatterns.patterns.iterator.WidgetParser 28 | 29 | @OptIn(ExperimentalMaterial3Api::class) 30 | @Composable 31 | fun InterpreterView() { 32 | var script by remember { mutableStateOf("") } 33 | val parser = remember { WidgetParser() } 34 | var expressions by remember { mutableStateOf(parser.parseScript(script)) } 35 | 36 | Scaffold( 37 | topBar = { 38 | TopAppBar( 39 | title = { Text("Interpreter Pattern") } 40 | ) 41 | }, 42 | content = { paddingValues -> 43 | Column( 44 | modifier = Modifier 45 | .fillMaxSize() 46 | .padding(paddingValues) 47 | .padding(16.dp), 48 | verticalArrangement = Arrangement.spacedBy(16.dp) 49 | ) { 50 | TextField( 51 | value = script, 52 | onValueChange = { script = it }, 53 | modifier = Modifier.fillMaxWidth(), 54 | ) 55 | Button(onClick = { expressions = parser.parseScript(script) }) { 56 | Text("Interpret the Script") 57 | } 58 | Spacer(modifier = Modifier.height(16.dp)) 59 | expressions.forEach { expression -> 60 | expression.interpret() 61 | Spacer(modifier = Modifier.height(8.dp)) 62 | } 63 | } 64 | } 65 | ) 66 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/views/ProtoTypeView.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.views 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.foundation.layout.fillMaxSize 6 | import androidx.compose.foundation.layout.fillMaxWidth 7 | import androidx.compose.foundation.layout.padding 8 | import androidx.compose.material3.Card 9 | import androidx.compose.material3.MaterialTheme 10 | import androidx.compose.material3.Text 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.unit.dp 14 | import com.cavin.designpatterns.patterns.prototype.AppDocument 15 | 16 | @Composable 17 | fun ProtoTypeView() { 18 | // Original prototype 19 | val originalDocument = AppDocument( 20 | title = "Prototype Pattern", 21 | content = "This is the original document content.", 22 | author = "John Doe" 23 | ) 24 | 25 | // Clone the prototype and modify properties 26 | val clonedDocument = originalDocument.copy( 27 | title = "Cloned Prototype", 28 | content = "This is the cloned document content." 29 | ) 30 | 31 | // UI Layout 32 | Column( 33 | modifier = Modifier 34 | .fillMaxSize() 35 | .padding(16.dp), 36 | verticalArrangement = Arrangement.spacedBy(16.dp) 37 | ) { 38 | Text("Documents", style = MaterialTheme.typography.titleLarge) 39 | 40 | // Display Original Document 41 | DocumentCard(document = originalDocument, modifier = Modifier.fillMaxWidth()) 42 | 43 | // Display Cloned Document 44 | DocumentCard(document = clonedDocument, modifier = Modifier.fillMaxWidth()) 45 | } 46 | } 47 | @Composable 48 | fun DocumentCard(document: AppDocument, modifier: Modifier = Modifier) { 49 | Card( 50 | modifier = modifier.padding(8.dp), 51 | shape = MaterialTheme.shapes.medium 52 | ) { 53 | Column(modifier = Modifier.padding(16.dp)) { 54 | Text(text = "Title: ${document.title}", style = MaterialTheme.typography.bodyLarge) 55 | Text(text = "Content: ${document.content}", style = MaterialTheme.typography.bodyMedium) 56 | Text(text = "Author: ${document.author}", style = MaterialTheme.typography.bodySmall) 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/views/ProxyView.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.views 2 | 3 | import androidx.compose.foundation.layout.Box 4 | import androidx.compose.foundation.layout.fillMaxSize 5 | import androidx.compose.foundation.layout.padding 6 | import androidx.compose.material3.ExperimentalMaterial3Api 7 | import androidx.compose.material3.MaterialTheme 8 | import androidx.compose.material3.Scaffold 9 | import androidx.compose.material3.Text 10 | import androidx.compose.material3.TopAppBar 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.LaunchedEffect 13 | import androidx.compose.runtime.mutableStateOf 14 | import androidx.compose.runtime.remember 15 | import androidx.compose.ui.Alignment 16 | import androidx.compose.ui.Modifier 17 | import com.cavin.designpatterns.patterns.proxy.WeatherService 18 | import com.cavin.designpatterns.patterns.proxy.WeatherServiceProxy 19 | import kotlinx.coroutines.delay 20 | 21 | @OptIn(ExperimentalMaterial3Api::class) 22 | @Composable 23 | fun ProxyView() { 24 | val weatherService: WeatherService = remember { WeatherServiceProxy() } 25 | 26 | val weatherData = remember { mutableStateOf("Loading...") } 27 | 28 | LaunchedEffect(Unit) { 29 | delay(3000) 30 | weatherData.value = getWeatherFiveTimes(weatherService) 31 | } 32 | 33 | Scaffold( 34 | topBar = { 35 | TopAppBar(title = { Text("Weather App") }) 36 | } 37 | ) { padding -> 38 | Box( 39 | modifier = Modifier 40 | .fillMaxSize() 41 | .padding(padding), 42 | contentAlignment = Alignment.Center 43 | ) { 44 | Text(text = weatherData.value, style = MaterialTheme.typography.bodyMedium) 45 | } 46 | } 47 | } 48 | 49 | suspend fun getWeatherFiveTimes(service: WeatherService): String { 50 | val results = mutableListOf() 51 | repeat(5) { 52 | val data = service.getWeatherData() 53 | results.add(data) 54 | } 55 | return results.joinToString("\n") 56 | } -------------------------------------------------------------------------------- /app/src/main/java/com/cavin/designpatterns/views/SingletonView.kt: -------------------------------------------------------------------------------- 1 | package com.cavin.designpatterns.views 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.foundation.layout.fillMaxSize 6 | import androidx.compose.foundation.layout.padding 7 | import androidx.compose.material3.Button 8 | import androidx.compose.material3.MaterialTheme 9 | import androidx.compose.material3.Scaffold 10 | import androidx.compose.material3.Text 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.getValue 13 | import androidx.compose.runtime.mutableStateOf 14 | import androidx.compose.runtime.remember 15 | import androidx.compose.runtime.setValue 16 | import androidx.compose.ui.Alignment 17 | import androidx.compose.ui.Modifier 18 | import com.cavin.designpatterns.patterns.singleton.ThemeConfig 19 | 20 | @Composable 21 | fun SingletonView(modifier: Modifier = Modifier) { 22 | Scaffold { 23 | Column( 24 | modifier = modifier 25 | .padding(it) 26 | .fillMaxSize(), 27 | verticalArrangement = Arrangement.Center, 28 | horizontalAlignment = Alignment.CenterHorizontally, 29 | ) { 30 | ThemeToggleButton() 31 | } 32 | } 33 | } 34 | 35 | 36 | @Composable 37 | fun ThemeToggleButton() { 38 | var isDarkMode by remember { mutableStateOf(ThemeConfig.isDarkModeEnabled()) } 39 | 40 | Button(onClick = { 41 | ThemeConfig.toggleDarkMode() 42 | isDarkMode = ThemeConfig.isDarkModeEnabled() 43 | }) { 44 | Text(if (isDarkMode) "Switch to Light Mode" else "Switch to Dark Mode") 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meticha/jetpack-compose-design-patterns/780becf7d277be1d5a0fd2fd3a93eed0c868e5ac/app/src/main/res/drawable/logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meticha/jetpack-compose-design-patterns/780becf7d277be1d5a0fd2fd3a93eed0c868e5ac/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meticha/jetpack-compose-design-patterns/780becf7d277be1d5a0fd2fd3a93eed0c868e5ac/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meticha/jetpack-compose-design-patterns/780becf7d277be1d5a0fd2fd3a93eed0c868e5ac/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meticha/jetpack-compose-design-patterns/780becf7d277be1d5a0fd2fd3a93eed0c868e5ac/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meticha/jetpack-compose-design-patterns/780becf7d277be1d5a0fd2fd3a93eed0c868e5ac/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meticha/jetpack-compose-design-patterns/780becf7d277be1d5a0fd2fd3a93eed0c868e5ac/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meticha/jetpack-compose-design-patterns/780becf7d277be1d5a0fd2fd3a93eed0c868e5ac/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meticha/jetpack-compose-design-patterns/780becf7d277be1d5a0fd2fd3a93eed0c868e5ac/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meticha/jetpack-compose-design-patterns/780becf7d277be1d5a0fd2fd3a93eed0c868e5ac/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meticha/jetpack-compose-design-patterns/780becf7d277be1d5a0fd2fd3a93eed0c868e5ac/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Jetpack Compose Design Patterns 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |