├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── .idea
├── .gitignore
├── .name
├── compiler.xml
├── gradle.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── kotlinc.xml
├── misc.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── peterchege
│ │ └── aiimagegenerator
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── peterchege
│ │ │ └── aiimagegenerator
│ │ │ ├── MainActivity.kt
│ │ │ ├── data
│ │ │ ├── api
│ │ │ │ ├── NetworkResult.kt
│ │ │ │ ├── NetworkUtil.kt
│ │ │ │ └── OpenAIApi.kt
│ │ │ ├── di
│ │ │ │ ├── AppModule.kt
│ │ │ │ └── ImageGeneratorApp.kt
│ │ │ └── repository
│ │ │ │ └── ImageRepositoryImpl.kt
│ │ │ ├── domain
│ │ │ ├── downloader
│ │ │ │ ├── AndroidDownloader.kt
│ │ │ │ ├── DownloadCompletedReceiver.kt
│ │ │ │ └── Downloader.kt
│ │ │ ├── models
│ │ │ │ ├── ImageResponse.kt
│ │ │ │ └── RequestBody.kt
│ │ │ └── repository
│ │ │ │ └── ImageRepository.kt
│ │ │ ├── ui
│ │ │ ├── AppNavigation.kt
│ │ │ ├── components
│ │ │ │ ├── DropdownMenu.kt
│ │ │ │ └── PagerIndicator.kt
│ │ │ ├── screens
│ │ │ │ ├── HistoryScreen.kt
│ │ │ │ └── HomeScreen.kt
│ │ │ ├── theme
│ │ │ │ ├── Color.kt
│ │ │ │ ├── Shape.kt
│ │ │ │ ├── Theme.kt
│ │ │ │ └── Type.kt
│ │ │ └── viewModels
│ │ │ │ ├── HistoryScreenViewModel.kt
│ │ │ │ └── HomeScreenViewModel.kt
│ │ │ └── util
│ │ │ ├── Constants.kt
│ │ │ ├── ImageSizes.kt
│ │ │ ├── Resource.kt
│ │ │ ├── Screens.kt
│ │ │ └── UiEvent.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ │ └── xml
│ │ ├── backup_rules.xml
│ │ └── data_extraction_rules.xml
│ └── test
│ └── java
│ └── com
│ └── peterchege
│ └── aiimagegenerator
│ ├── ExampleUnitTest.kt
│ ├── MainDispatchersRule.kt
│ └── ui
│ └── viewModels
│ └── HomeScreenViewModelTest.kt
├── build.gradle.kts
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle.kts
└── spotless
└── LICENSE.txt
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: AI Image Generator App CI/CD WorkFlow
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Checkout
11 | uses: actions/checkout@v2
12 |
13 | - name: Create Local Properties File
14 | run: cat /home/runner/work/AIImageGeneratorApp/AIImageGeneratorApp/local.properties | base64
15 | - name: Putting data
16 | env:
17 | DATA: ${{ secrets.PROPERTIES_CONTENT }}
18 | run: echo $DATA > /home/runner/work/AIImageGeneratorApp/AIImageGeneratorApp/local.properties
19 |
20 | - name: Set up JDK
21 | uses: actions/setup-java@v1
22 | with:
23 | java-version: 11
24 |
25 | - name: Cache gradle
26 | uses: actions/cache@v1
27 | with:
28 | path: ~/.gradle/caches
29 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
30 | restore-keys: |
31 | ${{ runner.os }}-gradle-
32 |
33 | - name: Build App with Gradle
34 | run: ./gradlew assembleDebug
35 |
36 | - name: Run Unit Tests with Gradle
37 | run: ./gradlew test
38 |
39 | - name: Upload a Build Artifact (APK)
40 | uses: actions/upload-artifact@v2.2.4
41 | with:
42 | name: app
43 | path: app/build/outputs/apk/debug/app-debug.apk
44 |
--------------------------------------------------------------------------------
/.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 | AI Image Generator
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AI Image Generator App
2 |
3 |
4 | This is an android AI image generator app that consumes the OPEN AI API
5 | to generate images based on a given prompt
6 |
7 | To run this code make sure you get your own API key and add it to the
8 | local.properties file for the code to run smoothly
9 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
2 |
3 | plugins {
4 | id ("com.android.application")
5 | id ("org.jetbrains.kotlin.android")
6 |
7 | }
8 |
9 |
10 | val key: String = gradleLocalProperties(rootDir).getProperty("OPEN_AI_API_KEY")
11 | android {
12 | namespace ="com.peterchege.aiimagegenerator"
13 | compileSdk =34
14 |
15 | defaultConfig {
16 | applicationId ="com.peterchege.aiimagegenerator"
17 | minSdk = 21
18 | targetSdk = 34
19 | versionCode= 1
20 | versionName = "1.0"
21 |
22 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
23 | vectorDrawables {
24 | useSupportLibrary = true
25 | }
26 |
27 | }
28 |
29 | buildTypes {
30 | getByName("debug"){
31 | buildConfigField("String", "OPEN_AI_API_KEY", key)
32 | }
33 | getByName("release") {
34 | buildConfigField("String", "OPEN_AI_API_KEY", key)
35 | isMinifyEnabled =false
36 | proguardFiles (getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
37 | }
38 | }
39 | compileOptions {
40 | sourceCompatibility = JavaVersion.VERSION_1_8
41 | targetCompatibility = JavaVersion.VERSION_1_8
42 | }
43 | kotlinOptions {
44 | jvmTarget = "1.8"
45 | }
46 | buildFeatures {
47 | compose= true
48 | }
49 | composeOptions {
50 | kotlinCompilerExtensionVersion ="1.5.3"
51 | }
52 | packagingOptions {
53 | resources {
54 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
55 | }
56 | }
57 |
58 |
59 | }
60 |
61 | dependencies {
62 |
63 |
64 | implementation ("androidx.core:core-ktx:1.10.1")
65 | implementation ("androidx.compose.ui:ui:1.5.0-beta01")
66 | implementation ("androidx.compose.material:material:1.5.0-beta01")
67 | implementation ("androidx.compose.ui:ui-tooling-preview:1.5.0-beta01")
68 | implementation ("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
69 | implementation ("androidx.activity:activity-compose:1.7.2")
70 | testImplementation ("junit:junit:4.13.2")
71 | androidTestImplementation ("androidx.test.ext:junit:1.1.5")
72 | androidTestImplementation ("androidx.test.espresso:espresso-core:3.5.1")
73 | androidTestImplementation ("androidx.compose.ui:ui-test-junit4:1.5.0-beta01")
74 | debugImplementation ("androidx.compose.ui:ui-tooling:1.5.0-beta01")
75 |
76 |
77 | implementation ("androidx.constraintlayout:constraintlayout-compose:1.0.1")
78 |
79 | // retrofit
80 | implementation("com.squareup.retrofit2:retrofit:2.9.0")
81 | implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
82 | implementation ("com.squareup.okhttp3:okhttp:5.0.0-alpha.2")
83 | implementation ("com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2")
84 | implementation("androidx.navigation:navigation-compose:2.7.1")
85 |
86 | // view model
87 | implementation ("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
88 | implementation ("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
89 | implementation ("androidx.lifecycle:lifecycle-runtime-compose:2.6.1")
90 |
91 |
92 | //coroutines
93 | implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
94 | implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
95 |
96 | // dagger hilt
97 | implementation ("com.google.dagger:hilt-android:2.48")
98 | ksp("com.google.dagger:dagger-compiler:2.48") // Dagger compiler
99 | ksp("com.google.dagger:hilt-compiler:2.48") // Hilt compiler
100 | implementation ("androidx.hilt:hilt-navigation-compose:1.0.0")
101 | // coil
102 | implementation ("io.coil-kt:coil-compose:2.4.0")
103 |
104 |
105 | //pager
106 | implementation ("com.google.accompanist:accompanist-pager:0.30.1")
107 | implementation ("com.google.accompanist:accompanist-pager-indicators:0.30.1")
108 |
109 | testImplementation ("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
110 | testImplementation("io.mockk:mockk:1.13.5")
111 | androidTestImplementation( "io.mockk:mockk-android:1.13.5")
112 | debugImplementation ("com.github.chuckerteam.chucker:library:4.0.0")
113 | releaseImplementation ("com.github.chuckerteam.chucker:library-no-op:4.0.0")
114 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/peterchege/aiimagegenerator/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator
17 |
18 | import androidx.test.platform.app.InstrumentationRegistry
19 | import androidx.test.ext.junit.runners.AndroidJUnit4
20 |
21 | import org.junit.Test
22 | import org.junit.runner.RunWith
23 |
24 | import org.junit.Assert.*
25 |
26 | /**
27 | * Instrumented test, which will execute on an Android device.
28 | *
29 | * See [testing documentation](http://d.android.com/tools/testing).
30 | */
31 | @RunWith(AndroidJUnit4::class)
32 | class ExampleInstrumentedTest {
33 | @Test
34 | fun useAppContext() {
35 | // Context of the app under test.
36 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
37 | assertEquals("com.peterchege.aiimagegenerator", appContext.packageName)
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
20 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/MainActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator
17 |
18 | import android.os.Bundle
19 | import androidx.activity.ComponentActivity
20 | import androidx.activity.compose.setContent
21 | import androidx.compose.foundation.layout.fillMaxSize
22 | import androidx.compose.material.MaterialTheme
23 | import androidx.compose.material.Surface
24 | import androidx.compose.material.Text
25 | import androidx.compose.runtime.Composable
26 | import androidx.compose.ui.Modifier
27 | import androidx.compose.ui.tooling.preview.Preview
28 | import androidx.navigation.compose.rememberNavController
29 | import com.peterchege.aiimagegenerator.ui.AppNavigation
30 | import com.peterchege.aiimagegenerator.ui.theme.AIImageGeneratorTheme
31 | import dagger.hilt.android.AndroidEntryPoint
32 |
33 |
34 | @AndroidEntryPoint
35 | class MainActivity : ComponentActivity() {
36 | override fun onCreate(savedInstanceState: Bundle?) {
37 | super.onCreate(savedInstanceState)
38 | setContent {
39 | AIImageGeneratorTheme {
40 | // A surface container using the 'background' color from the theme
41 | Surface(
42 | modifier = Modifier.fillMaxSize(),
43 | color = MaterialTheme.colors.background
44 | ) {
45 | val navController = rememberNavController()
46 | AppNavigation(navController = navController)
47 | }
48 | }
49 | }
50 | }
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/data/api/NetworkResult.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.data.api
17 |
18 | sealed class NetworkResult {
19 | class Success(val data: T) : NetworkResult()
20 | class Error(val code: Int, val message: String?) : NetworkResult()
21 | class Exception(val e: Throwable) : NetworkResult()
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/data/api/NetworkUtil.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.data.api
17 |
18 | import retrofit2.HttpException
19 | import retrofit2.Response
20 |
21 |
22 | suspend fun handleApi(
23 | execute: suspend () -> Response
24 | ): NetworkResult {
25 | return try {
26 | val response = execute()
27 | val body = response.body()
28 | if (response.isSuccessful && body != null) {
29 | NetworkResult.Success(body)
30 | } else {
31 | NetworkResult.Error(code = response.code(), message = response.message())
32 | }
33 | } catch (e: HttpException) {
34 | NetworkResult.Error(code = e.code(), message = e.message())
35 | } catch (e: Throwable) {
36 | NetworkResult.Exception(e)
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/data/api/OpenAIApi.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.data.api
17 |
18 |
19 | import com.peterchege.aiimagegenerator.BuildConfig
20 | import com.peterchege.aiimagegenerator.domain.models.ImageResponse
21 | import com.peterchege.aiimagegenerator.domain.models.RequestBody
22 | import com.peterchege.aiimagegenerator.util.Constants
23 | import retrofit2.Response
24 | import retrofit2.http.*
25 |
26 | interface OpenAIApi {
27 |
28 | @POST("generations")
29 | suspend fun generateImages(@Body requestBody: RequestBody): Response
30 |
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/data/di/AppModule.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.data.di
17 |
18 | import android.content.Context
19 | import com.chuckerteam.chucker.api.ChuckerCollector
20 | import com.chuckerteam.chucker.api.ChuckerInterceptor
21 | import com.chuckerteam.chucker.api.RetentionManager
22 | import com.peterchege.aiimagegenerator.BuildConfig
23 | import com.peterchege.aiimagegenerator.data.api.OpenAIApi
24 | import com.peterchege.aiimagegenerator.data.repository.ImageRepositoryImpl
25 | import com.peterchege.aiimagegenerator.domain.repository.ImageRepository
26 | import com.peterchege.aiimagegenerator.util.Constants
27 | import dagger.Module
28 | import dagger.Provides
29 | import dagger.hilt.InstallIn
30 | import dagger.hilt.android.qualifiers.ApplicationContext
31 | import dagger.hilt.components.SingletonComponent
32 | import okhttp3.Interceptor
33 | import okhttp3.OkHttpClient
34 | import retrofit2.Retrofit
35 | import retrofit2.converter.moshi.MoshiConverterFactory
36 | import javax.inject.Singleton
37 |
38 | @Module
39 | @InstallIn(SingletonComponent::class)
40 | object AppModule {
41 |
42 |
43 | @Provides
44 | @Singleton
45 | fun provideChuckerCollector(
46 | @ApplicationContext context: Context
47 | ):ChuckerCollector{
48 | return ChuckerCollector(
49 | context = context,
50 | showNotification = true,
51 | retentionPeriod = RetentionManager.Period.ONE_HOUR
52 | )
53 | }
54 |
55 | @Provides
56 | @Singleton
57 | fun provideChuckerInterceptor(
58 | chuckerCollector: ChuckerCollector,
59 | @ApplicationContext context: Context
60 | ):ChuckerInterceptor{
61 | return ChuckerInterceptor.Builder(context)
62 | .collector(collector = chuckerCollector)
63 | .maxContentLength(250_000L)
64 | .alwaysReadResponseBody(enable = true)
65 | .build()
66 |
67 | }
68 |
69 | @Provides
70 | @Singleton
71 | fun provideOkhttpClient(
72 | chuckerInterceptor: ChuckerInterceptor,
73 | ):OkHttpClient{
74 | return OkHttpClient.Builder()
75 | .addInterceptor(OAuthInterceptor("Bearer", BuildConfig.OPEN_AI_API_KEY))
76 | .addInterceptor(chuckerInterceptor)
77 | .build()
78 | }
79 |
80 |
81 |
82 |
83 | @Provides
84 | @Singleton
85 | fun provideOpenAIApi(
86 | client:OkHttpClient
87 | ): OpenAIApi {
88 | return Retrofit.Builder()
89 | .addConverterFactory(MoshiConverterFactory.create())
90 | .baseUrl(Constants.BASE_URL)
91 | .client(client)
92 | .build()
93 | .create(OpenAIApi::class.java)
94 | }
95 |
96 | @Provides
97 | @Singleton
98 | fun provideImageRepository(api:OpenAIApi): ImageRepository {
99 | return ImageRepositoryImpl(api = api)
100 | }
101 |
102 | }
103 |
104 | class OAuthInterceptor(private val tokenType: String, private val acceessToken: String):
105 | Interceptor {
106 |
107 | override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
108 | var request = chain.request()
109 | request = request.newBuilder().header("Authorization", "$tokenType $acceessToken").build()
110 |
111 | return chain.proceed(request)
112 | }
113 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/data/di/ImageGeneratorApp.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.data.di
17 |
18 | import android.app.Application
19 | import dagger.hilt.android.HiltAndroidApp
20 |
21 |
22 | @HiltAndroidApp
23 | class ImageGeneratorApp : Application()
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/data/repository/ImageRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.data.repository
17 |
18 | import com.peterchege.aiimagegenerator.data.api.NetworkResult
19 | import com.peterchege.aiimagegenerator.data.api.OpenAIApi
20 | import com.peterchege.aiimagegenerator.data.api.handleApi
21 | import com.peterchege.aiimagegenerator.domain.models.ImageResponse
22 | import com.peterchege.aiimagegenerator.domain.models.RequestBody
23 | import com.peterchege.aiimagegenerator.domain.repository.ImageRepository
24 | import javax.inject.Inject
25 |
26 | class ImageRepositoryImpl @Inject constructor(
27 | private val api:OpenAIApi
28 | ):ImageRepository {
29 |
30 |
31 | override suspend fun generateImages(requestBody: RequestBody): NetworkResult {
32 | return handleApi { api.generateImages(requestBody = requestBody) }
33 | }
34 |
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/domain/downloader/AndroidDownloader.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.domain.downloader
17 |
18 | import android.app.DownloadManager
19 | import android.content.Context
20 | import android.os.Build
21 | import android.os.Environment
22 | import androidx.annotation.RequiresApi
23 | import androidx.core.net.toUri
24 |
25 | class AndroidDownloader(
26 | context: Context
27 | ): Downloader {
28 |
29 | @RequiresApi(Build.VERSION_CODES.M)
30 | private val downloadManager = context.getSystemService(DownloadManager::class.java)
31 |
32 | @RequiresApi(Build.VERSION_CODES.M)
33 | override fun downloadFile(url: String): Long {
34 | val request = DownloadManager.Request(url.toUri())
35 | .setMimeType("image/png")
36 | .setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI)
37 | .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
38 | .setTitle("${getRandomString(10)}.png")
39 | .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "image.png")
40 |
41 | return downloadManager.enqueue(request)
42 | }
43 | private fun getRandomString(length: Int) : String {
44 | val charset = "ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz0123456789"
45 | return (1..length)
46 | .map { charset.random() }
47 | .joinToString("")
48 | }
49 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/domain/downloader/DownloadCompletedReceiver.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.domain.downloader
17 |
18 | import android.app.DownloadManager
19 | import android.content.BroadcastReceiver
20 | import android.content.Context
21 | import android.content.Intent
22 |
23 | class DownloadCompletedReceiver: BroadcastReceiver() {
24 |
25 | override fun onReceive(context: Context?, intent: Intent?) {
26 | if(intent?.action == "android.intent.action.DOWNLOAD_COMPLETE") {
27 | val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1L)
28 | if(id != -1L) {
29 | println("Download with ID $id finished!")
30 | }
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/domain/downloader/Downloader.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.domain.downloader
17 |
18 | interface Downloader {
19 | fun downloadFile(url: String): Long
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/domain/models/ImageResponse.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.domain.models
17 |
18 | import com.squareup.moshi.Json
19 |
20 | data class ImageResponse (
21 | @field:Json(name = "created")
22 | val created:String,
23 | @field:Json(name = "data")
24 | val data:List
25 | )
26 |
27 | data class ImageItem(
28 | @field:Json(name = "url")
29 | val url:String
30 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/domain/models/RequestBody.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.domain.models
17 |
18 | data class RequestBody (
19 | val prompt:String,
20 | val n:Int,
21 | val size:String,
22 |
23 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/domain/repository/ImageRepository.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.domain.repository
17 |
18 | import com.peterchege.aiimagegenerator.data.api.NetworkResult
19 | import com.peterchege.aiimagegenerator.domain.models.ImageResponse
20 | import com.peterchege.aiimagegenerator.domain.models.RequestBody
21 |
22 | interface ImageRepository {
23 |
24 | suspend fun generateImages(requestBody: RequestBody):NetworkResult
25 |
26 |
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/ui/AppNavigation.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.ui
17 |
18 | import androidx.compose.runtime.Composable
19 | import androidx.navigation.NavController
20 | import androidx.navigation.NavHostController
21 | import androidx.navigation.compose.NavHost
22 | import androidx.navigation.compose.composable
23 | import com.peterchege.aiimagegenerator.ui.screens.HistoryScreen
24 | import com.peterchege.aiimagegenerator.ui.screens.HomeScreen
25 | import com.peterchege.aiimagegenerator.util.Screens
26 |
27 |
28 | @Composable
29 | fun AppNavigation (
30 | navController:NavHostController
31 | ){
32 | NavHost(
33 | navController = navController,
34 | startDestination = Screens.HOME_SCREEN){
35 | composable(
36 | route = Screens.HOME_SCREEN
37 | ){
38 | HomeScreen(navController = navController)
39 | }
40 | composable(
41 | route = Screens.HISTORY_SCREEN
42 | ){
43 | HistoryScreen(navController = navController)
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/ui/components/DropdownMenu.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.ui.components
17 |
18 | import android.util.Log
19 | import androidx.compose.foundation.background
20 | import androidx.compose.foundation.border
21 | import androidx.compose.foundation.clickable
22 | import androidx.compose.foundation.layout.*
23 | import androidx.compose.material.*
24 | import androidx.compose.material.icons.Icons
25 | import androidx.compose.material.icons.filled.ArrowDropDown
26 | import androidx.compose.runtime.*
27 | import androidx.compose.ui.Alignment
28 | import androidx.compose.ui.Modifier
29 | import androidx.compose.ui.unit.dp
30 | import androidx.constraintlayout.compose.ConstraintLayout
31 | import androidx.constraintlayout.compose.Dimension
32 | import com.peterchege.aiimagegenerator.util.imageSizes
33 |
34 | @Composable
35 | fun MyCustomDropDownMenu(
36 | selectedIndex:Int,
37 | listItems:List,
38 | onChangeSelectedIndex:(Int) -> Unit,
39 | width:Float
40 | ){
41 | var menuListExpanded by remember { mutableStateOf(false) }
42 | Box(
43 | modifier = Modifier
44 | .fillMaxSize()
45 | .fillMaxWidth()
46 | .padding(5.dp),
47 | contentAlignment = Alignment.CenterStart
48 | ) {
49 | ComposeMenu(
50 | menuItems = listItems,
51 | menuExpandedState = menuListExpanded,
52 | seletedIndex = selectedIndex,
53 | updateMenuExpandStatus = {
54 | menuListExpanded = true
55 | },
56 | onDismissMenuView = {
57 | menuListExpanded = false
58 | },
59 | onMenuItemclick = { index->
60 | Log.d("Index",index.toString())
61 | onChangeSelectedIndex(index)
62 | menuListExpanded = false
63 | },
64 | setWidth = width
65 | )
66 | }
67 | }
68 |
69 |
70 | @Composable
71 | fun ComposeMenu(
72 | menuItems: List,
73 | menuExpandedState: Boolean,
74 | seletedIndex : Int,
75 | updateMenuExpandStatus : () -> Unit,
76 | onDismissMenuView : () -> Unit,
77 | onMenuItemclick : (Int) -> Unit,
78 | setWidth: Float,
79 | ) {
80 | Box(
81 | modifier = Modifier
82 | .fillMaxSize()
83 | .wrapContentSize(Alignment.TopStart)
84 | .padding(top = 3.dp)
85 | .border(0.5.dp, MaterialTheme.colors.onSurface.copy(alpha = 0.5f))
86 | .clickable(
87 | onClick = {
88 | updateMenuExpandStatus()
89 | },
90 | ),
91 |
92 | ) {
93 |
94 | ConstraintLayout(
95 | modifier = Modifier
96 | .fillMaxWidth()
97 | .padding(10.dp)
98 | ) {
99 |
100 | val (lable, iconView) = createRefs()
101 |
102 | Text(
103 | text= menuItems[seletedIndex],
104 | modifier = Modifier
105 | .fillMaxWidth()
106 | .constrainAs(lable) {
107 | top.linkTo(parent.top)
108 | bottom.linkTo(parent.bottom)
109 | start.linkTo(parent.start)
110 | end.linkTo(iconView.start)
111 | width = Dimension.fillToConstraints
112 | }
113 | )
114 | Icon(
115 | Icons.Filled.ArrowDropDown,
116 | contentDescription = null,
117 | modifier = Modifier
118 | .size(20.dp, 20.dp)
119 | .constrainAs(iconView) {
120 | end.linkTo(parent.end)
121 | top.linkTo(parent.top)
122 | bottom.linkTo(parent.bottom)
123 | },
124 | tint = MaterialTheme.colors.onSurface
125 | )
126 |
127 | DropdownMenu(
128 | expanded = menuExpandedState,
129 | onDismissRequest = { onDismissMenuView() },
130 | modifier = Modifier
131 | .fillMaxWidth(fraction = setWidth)
132 | .background(MaterialTheme.colors.surface)
133 | ) {
134 | menuItems.forEachIndexed { index, title ->
135 | DropdownMenuItem(
136 | onClick = {
137 | onMenuItemclick(index)
138 | }) {
139 | Text(text = title)
140 | }
141 | }
142 | }
143 | }
144 | }
145 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/ui/components/PagerIndicator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.ui.components
17 |
18 | import androidx.compose.foundation.background
19 | import androidx.compose.foundation.clickable
20 | import androidx.compose.foundation.layout.*
21 | import androidx.compose.foundation.lazy.LazyColumn
22 | import androidx.compose.foundation.lazy.LazyListScope
23 | import androidx.compose.foundation.lazy.LazyRow
24 | import androidx.compose.foundation.lazy.rememberLazyListState
25 | import androidx.compose.foundation.shape.CircleShape
26 | import androidx.compose.material.MaterialTheme
27 | import androidx.compose.runtime.Composable
28 | import androidx.compose.runtime.LaunchedEffect
29 | import androidx.compose.runtime.derivedStateOf
30 | import androidx.compose.runtime.remember
31 | import androidx.compose.ui.Modifier
32 | import androidx.compose.ui.draw.clip
33 | import androidx.compose.ui.graphics.Color
34 | import androidx.compose.ui.graphics.Shape
35 | import androidx.compose.ui.graphics.graphicsLayer
36 | import androidx.compose.ui.platform.LocalDensity
37 | import androidx.compose.ui.unit.Dp
38 | import androidx.compose.ui.unit.dp
39 | import com.google.accompanist.pager.ExperimentalPagerApi
40 | import com.google.accompanist.pager.PagerState
41 | import androidx.compose.runtime.*
42 |
43 |
44 |
45 | @OptIn(ExperimentalPagerApi::class)
46 | @Composable
47 | fun PagerIndicator(
48 | modifier: Modifier = Modifier,
49 | pagerState: PagerState,
50 | indicatorCount: Int = 5,
51 | indicatorSize: Dp = 8.dp,
52 | indicatorShape: Shape = CircleShape,
53 | space: Dp = 6.dp,
54 | activeColor: Color = Color.Blue,
55 | inActiveColor: Color = Color.LightGray,
56 | orientation: IndicatorOrientation = IndicatorOrientation.Horizontal,
57 | onClick: ((Int) -> Unit)? = null
58 | ) {
59 |
60 | val listState = rememberLazyListState()
61 |
62 | val totalWidth: Dp = indicatorSize * indicatorCount + space * (indicatorCount - 1)
63 | val widthInPx = LocalDensity.current.run { indicatorSize.toPx() }
64 |
65 | val currentItem by remember {
66 | derivedStateOf {
67 | pagerState.currentPage
68 | }
69 | }
70 |
71 | val itemCount = pagerState.pageCount
72 |
73 | LaunchedEffect(key1 = currentItem) {
74 | val viewportSize = listState.layoutInfo.viewportSize
75 | if (orientation == IndicatorOrientation.Horizontal) {
76 | listState.animateScrollToItem(
77 | currentItem,
78 | (widthInPx / 2 - viewportSize.width / 2).toInt()
79 | )
80 | } else {
81 | listState.animateScrollToItem(
82 | currentItem,
83 | (widthInPx / 2 - viewportSize.height / 2).toInt()
84 | )
85 | }
86 |
87 | }
88 |
89 | if (orientation == IndicatorOrientation.Horizontal) {
90 | LazyRow(
91 | modifier = modifier.width(totalWidth),
92 | state = listState,
93 | contentPadding = PaddingValues(vertical = space),
94 | horizontalArrangement = Arrangement.spacedBy(space),
95 | userScrollEnabled = false
96 | ) {
97 | indicatorItems(
98 | itemCount,
99 | currentItem,
100 | indicatorCount,
101 | indicatorShape,
102 | activeColor,
103 | inActiveColor,
104 | indicatorSize,
105 | onClick
106 | )
107 | }
108 | } else {
109 | LazyColumn(
110 | modifier = modifier.height(totalWidth),
111 | state = listState,
112 | contentPadding = PaddingValues(horizontal = space),
113 | verticalArrangement = Arrangement.spacedBy(space),
114 | userScrollEnabled = false
115 | ) {
116 | indicatorItems(
117 | itemCount,
118 | currentItem,
119 | indicatorCount,
120 | indicatorShape,
121 | activeColor,
122 | inActiveColor,
123 | indicatorSize,
124 | onClick
125 | )
126 | }
127 | }
128 |
129 | }
130 |
131 | private fun LazyListScope.indicatorItems(
132 | itemCount: Int,
133 | currentItem: Int,
134 | indicatorCount: Int,
135 | indicatorShape: Shape,
136 | activeColor: Color,
137 | inActiveColor: Color,
138 | indicatorSize: Dp,
139 | onClick: ((Int) -> Unit)?
140 | ) {
141 | items(itemCount) { index ->
142 |
143 | val isSelected = (index == currentItem)
144 |
145 | // Index of item in center when odd number of indicators are set
146 | // for 5 indicators this is 2nd indicator place
147 | val centerItemIndex = indicatorCount / 2
148 |
149 | val right1 =
150 | (currentItem < centerItemIndex &&
151 | index >= indicatorCount - 1)
152 |
153 | val right2 =
154 | (currentItem >= centerItemIndex &&
155 | index >= currentItem + centerItemIndex &&
156 | index < itemCount - centerItemIndex + 1)
157 | val isRightEdgeItem = right1 || right2
158 |
159 | // Check if this item's distance to center item is smaller than half size of
160 | // the indicator count when current indicator at the center or
161 | // when we reach the end of list. End of the list only one item is on edge
162 | // with 10 items and 7 indicators
163 | // 7-3= 4th item can be the first valid left edge item and
164 | val isLeftEdgeItem =
165 | index <= currentItem - centerItemIndex &&
166 | currentItem > centerItemIndex &&
167 | index < itemCount - indicatorCount + 1
168 |
169 | Box(
170 | modifier = Modifier
171 | .graphicsLayer {
172 | val scale = if (isSelected) {
173 | 1f
174 | } else if (isLeftEdgeItem || isRightEdgeItem) {
175 | .5f
176 | } else {
177 | .8f
178 | }
179 | scaleX = scale
180 | scaleY = scale
181 |
182 | }
183 | .clip(indicatorShape)
184 | .size(indicatorSize)
185 | .background(
186 | if (isSelected) activeColor else inActiveColor,
187 | indicatorShape
188 | )
189 | .then(
190 | if (onClick != null) {
191 | Modifier
192 | .clickable {
193 | onClick.invoke(index)
194 | }
195 | } else Modifier
196 | )
197 | )
198 | }
199 | }
200 |
201 | enum class IndicatorOrientation {
202 | Horizontal, Vertical
203 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/ui/screens/HistoryScreen.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.ui.screens
17 |
18 | import androidx.compose.runtime.Composable
19 | import androidx.navigation.NavController
20 |
21 | @Composable
22 | fun HistoryScreen(
23 | navController: NavController
24 |
25 | ) {
26 |
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/ui/screens/HomeScreen.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.ui.screens
17 |
18 | import android.annotation.SuppressLint
19 | import android.os.Build
20 | import android.util.Log
21 | import androidx.annotation.RequiresApi
22 | import androidx.compose.foundation.background
23 | import androidx.compose.foundation.layout.*
24 | import androidx.compose.foundation.shape.RoundedCornerShape
25 | import androidx.compose.material.*
26 | import androidx.compose.runtime.Composable
27 | import androidx.compose.runtime.LaunchedEffect
28 | import androidx.compose.runtime.getValue
29 | import androidx.compose.runtime.rememberCoroutineScope
30 | import androidx.compose.ui.Alignment
31 | import androidx.compose.ui.Modifier
32 | import androidx.compose.ui.draw.clip
33 | import androidx.compose.ui.graphics.Color
34 | import androidx.compose.ui.layout.ContentScale
35 | import androidx.compose.ui.platform.LocalContext
36 | import androidx.compose.ui.text.style.TextAlign
37 | import androidx.compose.ui.unit.dp
38 | import androidx.compose.ui.unit.sp
39 | import androidx.hilt.navigation.compose.hiltViewModel
40 | import androidx.lifecycle.compose.collectAsStateWithLifecycle
41 | import androidx.navigation.NavController
42 | import coil.compose.SubcomposeAsyncImage
43 | import com.google.accompanist.pager.ExperimentalPagerApi
44 | import com.google.accompanist.pager.HorizontalPager
45 | import com.google.accompanist.pager.rememberPagerState
46 | import com.peterchege.aiimagegenerator.domain.downloader.AndroidDownloader
47 | import com.peterchege.aiimagegenerator.ui.components.MyCustomDropDownMenu
48 | import com.peterchege.aiimagegenerator.ui.components.PagerIndicator
49 | import com.peterchege.aiimagegenerator.ui.viewModels.FormState
50 | import com.peterchege.aiimagegenerator.ui.viewModels.HomeScreenUiState
51 | import com.peterchege.aiimagegenerator.ui.viewModels.HomeScreenViewModel
52 | import com.peterchege.aiimagegenerator.util.UiEvent
53 | import com.peterchege.aiimagegenerator.util.imageCounts
54 | import com.peterchege.aiimagegenerator.util.imageSizes
55 | import kotlinx.coroutines.flow.SharedFlow
56 | import kotlinx.coroutines.flow.collectLatest
57 | import kotlinx.coroutines.launch
58 |
59 | @RequiresApi(Build.VERSION_CODES.M)
60 | @Composable
61 | fun HomeScreen(
62 | navController:NavController,
63 | viewModel:HomeScreenViewModel = hiltViewModel()
64 | ){
65 | val uiState by viewModel.uiState.collectAsStateWithLifecycle()
66 | val formState by viewModel.formState.collectAsStateWithLifecycle()
67 | HomeScreenContent(
68 | uiState = uiState,
69 | formState = formState,
70 | eventFlow = viewModel.eventFlow,
71 | onChangePrompt = { viewModel.onChangePrompt(it) },
72 | onChangeSize = { viewModel.onChangeSelectedImageSizeIndex(it) },
73 | onChangeImageCount = { viewModel.onChangeSelectedImageCountIndex(it) },
74 | onSubmit = { viewModel.generateImages() }
75 | )
76 |
77 | }
78 |
79 |
80 | @RequiresApi(Build.VERSION_CODES.M)
81 | @OptIn(ExperimentalPagerApi::class)
82 | @SuppressLint("UnusedMaterialScaffoldPaddingParameter")
83 | @Composable
84 | fun HomeScreenContent(
85 | uiState: HomeScreenUiState,
86 | formState:FormState,
87 | eventFlow:SharedFlow,
88 | onChangePrompt:(String) -> Unit,
89 | onChangeSize:(Int) -> Unit,
90 | onChangeImageCount: (Int) -> Unit,
91 | onSubmit:() -> Unit,
92 | ) {
93 | val context = LocalContext.current
94 | val scaffoldState = rememberScaffoldState()
95 |
96 | LaunchedEffect(key1 = true) {
97 | eventFlow.collectLatest { event ->
98 | when (event) {
99 | is UiEvent.ShowSnackbar -> {
100 | scaffoldState.snackbarHostState.showSnackbar(
101 | message = event.uiText
102 | )
103 | }
104 | is UiEvent.Navigate -> {
105 |
106 | }
107 | }
108 | }
109 | }
110 |
111 | Scaffold(
112 | scaffoldState = scaffoldState,
113 | modifier = Modifier.fillMaxSize(),
114 | topBar = {
115 | TopAppBar(
116 | title = {
117 | Row(
118 | modifier = Modifier
119 | .fillMaxSize()
120 | .padding(end = 15.dp),
121 | verticalAlignment = Alignment.CenterVertically,
122 | horizontalArrangement = Arrangement.SpaceBetween,
123 | ) {
124 | Text(text = "AI Image Generator")
125 |
126 | }
127 | }
128 | )
129 | },
130 | ) {
131 |
132 | Box(modifier = Modifier.fillMaxSize()){
133 | Column(
134 | modifier = Modifier
135 | .fillMaxSize()
136 | .padding(10.dp),
137 | verticalArrangement = Arrangement.Top,
138 | horizontalAlignment = Alignment.CenterHorizontally,
139 | ) {
140 | TextField(
141 | modifier = Modifier.fillMaxWidth(),
142 | label = {
143 | Text(text = "Enter Image Description")
144 | },
145 | value = formState.prompt,
146 | onValueChange = {
147 | onChangePrompt(it)
148 |
149 | })
150 | Spacer(modifier = Modifier.height(10.dp))
151 | Row(
152 | modifier = Modifier
153 | .fillMaxWidth()
154 | .height(110.dp),
155 | verticalAlignment = Alignment.CenterVertically,
156 | horizontalArrangement = Arrangement.SpaceBetween,
157 | ) {
158 | Column(
159 | modifier = Modifier
160 | .fillMaxWidth(fraction = 0.4f)
161 | .fillMaxHeight()
162 |
163 | ) {
164 | Text(
165 | text = "Select Image Size",
166 | fontSize = 14.sp
167 | )
168 | MyCustomDropDownMenu(
169 | listItems = imageSizes,
170 | selectedIndex = imageSizes.indexOf(formState.size) ,
171 | onChangeSelectedIndex = {
172 | onChangeSize(it)
173 | },
174 | width = 0.4f,
175 | )
176 | }
177 | Column(
178 | modifier = Modifier
179 | .fillMaxWidth(fraction = 0.4f)
180 | .fillMaxHeight()
181 |
182 | ) {
183 | Text(
184 | text = "No. of Images",
185 | fontSize = 14.sp
186 | )
187 | MyCustomDropDownMenu(
188 | listItems = imageCounts.map { it.toString() },
189 | selectedIndex = imageCounts.indexOf(formState.imageCount) ,
190 | onChangeSelectedIndex = {
191 | onChangeImageCount(it)
192 | },
193 | width = 0.4f,
194 | )
195 | }
196 | Column(
197 | verticalArrangement = Arrangement.Center,
198 | horizontalAlignment = Alignment.CenterHorizontally,
199 | ) {
200 | Button(onClick = {
201 | onSubmit()
202 |
203 | }) {
204 | Text(text = "Generate")
205 | }
206 | }
207 |
208 | }
209 |
210 | when(uiState){
211 | is HomeScreenUiState.Idle -> {
212 | Box(modifier = Modifier.fillMaxSize()){
213 | Text(
214 | text = "Start generating images",
215 | modifier = Modifier.align(Alignment.Center)
216 | )
217 | }
218 | }
219 | is HomeScreenUiState.Loading -> {
220 | Box(modifier = Modifier.fillMaxSize()){
221 | CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
222 | }
223 | }
224 | is HomeScreenUiState.Error -> {
225 | Box(modifier = Modifier.fillMaxSize()){
226 | Text(
227 | text = "An unexpected error",
228 | modifier = Modifier.align(Alignment.Center)
229 | )
230 | }
231 | }
232 | is HomeScreenUiState.Success -> {
233 | val images = uiState.images
234 | val pagerState1 = rememberPagerState(initialPage = 0)
235 | val coroutineScope = rememberCoroutineScope()
236 | HorizontalPager(
237 | count = images.size,
238 | state = pagerState1
239 | ) { image ->
240 | Box(
241 | modifier = Modifier.fillMaxWidth()
242 | ){
243 | SubcomposeAsyncImage(
244 | model = images[image].url,
245 | loading = {
246 | Box(modifier = Modifier.fillMaxSize()) {
247 | CircularProgressIndicator(
248 | modifier = Modifier.align(
249 | Alignment.Center
250 | )
251 | )
252 | }
253 | },
254 | contentScale = ContentScale.Crop,
255 | modifier = Modifier
256 | .fillMaxWidth()
257 | .height(300.dp),
258 | contentDescription = "Generated Images"
259 | )
260 | Box(
261 | modifier = Modifier
262 | .padding(10.dp)
263 | .width(45.dp)
264 | .align(Alignment.TopEnd)
265 | .height(25.dp)
266 | .clip(RoundedCornerShape(15.dp))
267 | .background(Color.White)
268 |
269 | ){
270 | Text(
271 | modifier = Modifier
272 | .align(Alignment.Center)
273 | .padding(horizontal = 3.dp),
274 | textAlign = TextAlign.Start,
275 | fontSize = 17.sp,
276 | text = "${image + 1}/${images.size}"
277 | )
278 | }
279 | }
280 | }
281 | Box(
282 | modifier = Modifier
283 | .fillMaxWidth()
284 | .height(20.dp)
285 | ) {
286 | PagerIndicator(
287 | modifier = Modifier.align(Alignment.Center),
288 | pagerState = pagerState1
289 | ) {
290 | coroutineScope.launch {
291 | pagerState1.scrollToPage(it)
292 | }
293 | }
294 | }
295 |
296 | Text(
297 | fontSize = 13.sp,
298 | textAlign = TextAlign.Center,
299 | modifier = Modifier.fillMaxWidth(),
300 | text = "NB: The images might load slowly because of the API so please be patient enough for them to load ")
301 | Row(
302 | modifier = Modifier
303 | .fillMaxWidth()
304 | .height(90.dp),
305 | verticalAlignment = Alignment.CenterVertically,
306 | horizontalArrangement = Arrangement.Center,
307 | ){
308 | Button(
309 | onClick = {
310 | val downloader = AndroidDownloader(context = context)
311 | images.map {
312 | downloader.downloadFile(url = it.url)
313 | }
314 |
315 | }
316 | ){
317 | Text(text = "Download Images")
318 | }
319 | }
320 | }
321 | }
322 | }
323 | }
324 | }
325 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.ui.theme
17 |
18 | import androidx.compose.ui.graphics.Color
19 |
20 | val Purple200 = Color(0xFFBB86FC)
21 | val Purple500 = Color(0xFF6200EE)
22 | val Purple700 = Color(0xFF3700B3)
23 | val Teal200 = Color(0xFF03DAC5)
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/ui/theme/Shape.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.ui.theme
17 |
18 | import androidx.compose.foundation.shape.RoundedCornerShape
19 | import androidx.compose.material.Shapes
20 | import androidx.compose.ui.unit.dp
21 |
22 | val Shapes = Shapes(
23 | small = RoundedCornerShape(4.dp),
24 | medium = RoundedCornerShape(4.dp),
25 | large = RoundedCornerShape(0.dp)
26 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.ui.theme
17 |
18 | import androidx.compose.foundation.isSystemInDarkTheme
19 | import androidx.compose.material.MaterialTheme
20 | import androidx.compose.material.darkColors
21 | import androidx.compose.material.lightColors
22 | import androidx.compose.runtime.Composable
23 |
24 | private val DarkColorPalette = darkColors(
25 | primary = Purple200,
26 | primaryVariant = Purple700,
27 | secondary = Teal200
28 | )
29 |
30 | private val LightColorPalette = lightColors(
31 | primary = Purple500,
32 | primaryVariant = Purple700,
33 | secondary = Teal200
34 |
35 | /* Other default colors to override
36 | background = Color.White,
37 | surface = Color.White,
38 | onPrimary = Color.White,
39 | onSecondary = Color.Black,
40 | onBackground = Color.Black,
41 | onSurface = Color.Black,
42 | */
43 | )
44 |
45 | @Composable
46 | fun AIImageGeneratorTheme(
47 | darkTheme: Boolean = isSystemInDarkTheme(),
48 | content: @Composable () -> Unit
49 | ) {
50 | val colors = if (darkTheme) {
51 | DarkColorPalette
52 | } else {
53 | LightColorPalette
54 | }
55 |
56 | MaterialTheme(
57 | colors = colors,
58 | typography = Typography,
59 | shapes = Shapes,
60 | content = content
61 | )
62 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.ui.theme
17 |
18 | import androidx.compose.material.Typography
19 | import androidx.compose.ui.text.TextStyle
20 | import androidx.compose.ui.text.font.FontFamily
21 | import androidx.compose.ui.text.font.FontWeight
22 | import androidx.compose.ui.unit.sp
23 |
24 | // Set of Material typography styles to start with
25 | val Typography = Typography(
26 | body1 = TextStyle(
27 | fontFamily = FontFamily.Default,
28 | fontWeight = FontWeight.Normal,
29 | fontSize = 16.sp
30 | )
31 | /* Other default text styles to override
32 | button = TextStyle(
33 | fontFamily = FontFamily.Default,
34 | fontWeight = FontWeight.W500,
35 | fontSize = 14.sp
36 | ),
37 | caption = TextStyle(
38 | fontFamily = FontFamily.Default,
39 | fontWeight = FontWeight.Normal,
40 | fontSize = 12.sp
41 | )
42 | */
43 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/ui/viewModels/HistoryScreenViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.ui.viewModels
17 |
18 | import androidx.lifecycle.ViewModel
19 | import dagger.hilt.android.lifecycle.HiltViewModel
20 | import javax.inject.Inject
21 |
22 |
23 | @HiltViewModel
24 | class HistoryScreenViewModel @Inject constructor(
25 |
26 | ) :ViewModel() {
27 |
28 |
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/ui/viewModels/HomeScreenViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.ui.viewModels
17 |
18 |
19 | import androidx.compose.runtime.State
20 | import androidx.compose.runtime.mutableStateOf
21 | import androidx.lifecycle.ViewModel
22 | import androidx.lifecycle.viewModelScope
23 | import com.peterchege.aiimagegenerator.data.api.NetworkResult
24 | import com.peterchege.aiimagegenerator.domain.models.ImageItem
25 | import com.peterchege.aiimagegenerator.domain.models.RequestBody
26 | import com.peterchege.aiimagegenerator.domain.repository.ImageRepository
27 | import com.peterchege.aiimagegenerator.util.Resource
28 | import com.peterchege.aiimagegenerator.util.UiEvent
29 | import com.peterchege.aiimagegenerator.util.imageCounts
30 | import com.peterchege.aiimagegenerator.util.imageSizes
31 | import dagger.hilt.android.lifecycle.HiltViewModel
32 | import kotlinx.coroutines.flow.MutableSharedFlow
33 | import kotlinx.coroutines.flow.MutableStateFlow
34 | import kotlinx.coroutines.flow.asSharedFlow
35 | import kotlinx.coroutines.flow.asStateFlow
36 | import kotlinx.coroutines.flow.launchIn
37 | import kotlinx.coroutines.flow.onEach
38 | import kotlinx.coroutines.launch
39 | import javax.inject.Inject
40 |
41 | data class FormState(
42 | val prompt:String = "",
43 | val size:String = "1024x1024",
44 | val imageCount:Int = 1
45 | )
46 |
47 | sealed interface HomeScreenUiState {
48 | object Idle : HomeScreenUiState
49 | object Loading : HomeScreenUiState
50 | data class Error(val message: String) : HomeScreenUiState
51 | data class Success(val images: List, ) : HomeScreenUiState
52 | }
53 |
54 | @HiltViewModel
55 | class HomeScreenViewModel @Inject constructor(
56 | private val repository: ImageRepository,
57 | ) : ViewModel() {
58 |
59 | private val _formState = MutableStateFlow(FormState())
60 | val formState = _formState.asStateFlow()
61 |
62 |
63 | private val _uiState = MutableStateFlow(HomeScreenUiState.Idle)
64 | val uiState = _uiState.asStateFlow()
65 |
66 |
67 | private val _eventFlow = MutableSharedFlow()
68 | val eventFlow = _eventFlow.asSharedFlow()
69 |
70 |
71 | fun onChangeSelectedImageSizeIndex(index: Int) {
72 | _formState.value = _formState.value.copy(size = imageSizes[index])
73 |
74 | }
75 | fun onChangeSelectedImageCountIndex(index: Int) {
76 | _formState.value = _formState.value.copy(imageCount = imageCounts[index])
77 | }
78 |
79 | fun onChangePrompt(text: String) {
80 | _formState.value = _formState.value.copy(prompt = text)
81 | }
82 |
83 | fun generateImages() {
84 | _uiState.value = HomeScreenUiState.Loading
85 |
86 | val requestBody = RequestBody(
87 | prompt = _formState.value.prompt,
88 | n = _formState.value.imageCount,
89 | size = _formState.value.size
90 | )
91 | viewModelScope.launch {
92 | when (val response = repository.generateImages(requestBody = requestBody)) {
93 | is NetworkResult.Success -> {
94 | _uiState.value = HomeScreenUiState.Success(images = response.data.data)
95 | }
96 | is NetworkResult.Error -> {
97 | _uiState.value = HomeScreenUiState.Error(
98 | message = response.message ?: "An unexpected error occurred")
99 | _eventFlow.emit(UiEvent.ShowSnackbar(uiText = "${response.code} ${response.message}"))
100 | }
101 | is NetworkResult.Exception -> {
102 | _uiState.value = HomeScreenUiState.Error(
103 | message = response.e.message ?: "An unexpected error occurred")
104 | _eventFlow.emit(UiEvent.ShowSnackbar(uiText = "${response.e.message}"))
105 | }
106 | }
107 | }
108 |
109 | }
110 |
111 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/util/Constants.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.util
17 |
18 | import com.peterchege.aiimagegenerator.BuildConfig
19 |
20 |
21 |
22 | object Constants {
23 | var OPEN_AI_API_KEY: String = BuildConfig.OPEN_AI_API_KEY
24 |
25 | const val BASE_URL = "https://api.openai.com/v1/images/"
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/util/ImageSizes.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.util
17 |
18 |
19 |
20 |
21 | val imageSizes = listOf("1024x1024","512x512","256x256")
22 | val imageCounts = listOf(1,2,3,4,5,6,7,8,9,10)
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/util/Resource.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.util
17 |
18 | import com.peterchege.aiimagegenerator.data.api.NetworkResult
19 | import com.peterchege.aiimagegenerator.domain.models.ImageResponse
20 |
21 | sealed class Resource(
22 | val data: T? = null,
23 | val message: String? = null
24 | ) {
25 | class Success(data: T) : Resource(data)
26 | class Error(message: String, data: T? = null) : Resource(data, message)
27 | class Loading : Resource()
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/util/Screens.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.util
17 |
18 | object Screens {
19 | const val HOME_SCREEN= "HOME_SCREEN"
20 | const val HISTORY_SCREEN= "HISTORY_SCREEN"
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peterchege/aiimagegenerator/util/UiEvent.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.util
17 |
18 | abstract class Event
19 |
20 | sealed class UiEvent: Event() {
21 | data class ShowSnackbar(val uiText: String) : UiEvent()
22 | data class Navigate(val route: String) : UiEvent()
23 |
24 |
25 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chege4179/AIImageGeneratorApp/c87ec4f2fa0dfeda90faaa0ac04ac513bc3af5e2/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chege4179/AIImageGeneratorApp/c87ec4f2fa0dfeda90faaa0ac04ac513bc3af5e2/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chege4179/AIImageGeneratorApp/c87ec4f2fa0dfeda90faaa0ac04ac513bc3af5e2/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chege4179/AIImageGeneratorApp/c87ec4f2fa0dfeda90faaa0ac04ac513bc3af5e2/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chege4179/AIImageGeneratorApp/c87ec4f2fa0dfeda90faaa0ac04ac513bc3af5e2/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chege4179/AIImageGeneratorApp/c87ec4f2fa0dfeda90faaa0ac04ac513bc3af5e2/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chege4179/AIImageGeneratorApp/c87ec4f2fa0dfeda90faaa0ac04ac513bc3af5e2/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chege4179/AIImageGeneratorApp/c87ec4f2fa0dfeda90faaa0ac04ac513bc3af5e2/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chege4179/AIImageGeneratorApp/c87ec4f2fa0dfeda90faaa0ac04ac513bc3af5e2/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chege4179/AIImageGeneratorApp/c87ec4f2fa0dfeda90faaa0ac04ac513bc3af5e2/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 | AI Image Generator
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/app/src/test/java/com/peterchege/aiimagegenerator/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator
17 |
18 | import org.junit.Test
19 |
20 | import org.junit.Assert.*
21 |
22 | /**
23 | * Example local unit test, which will execute on the development machine (host).
24 | *
25 | * See [testing documentation](http://d.android.com/tools/testing).
26 | */
27 | class ExampleUnitTest {
28 | @Test
29 | fun addition_isCorrect() {
30 | assertEquals(4, 2 + 2)
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/peterchege/aiimagegenerator/MainDispatchersRule.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator
17 |
18 | import kotlinx.coroutines.Dispatchers
19 | import kotlinx.coroutines.ExperimentalCoroutinesApi
20 | import kotlinx.coroutines.test.TestDispatcher
21 | import kotlinx.coroutines.test.UnconfinedTestDispatcher
22 | import kotlinx.coroutines.test.resetMain
23 | import kotlinx.coroutines.test.setMain
24 | import org.junit.rules.TestWatcher
25 | import org.junit.runner.Description
26 |
27 | class MainDispatcherRule(
28 | val testDispatcher: TestDispatcher = UnconfinedTestDispatcher(),
29 | ) : TestWatcher() {
30 | @OptIn(ExperimentalCoroutinesApi::class)
31 | override fun starting(description: Description) {
32 | Dispatchers.setMain(testDispatcher)
33 | }
34 |
35 | @OptIn(ExperimentalCoroutinesApi::class)
36 | override fun finished(description: Description) {
37 | Dispatchers.resetMain()
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/peterchege/aiimagegenerator/ui/viewModels/HomeScreenViewModelTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.peterchege.aiimagegenerator.ui.viewModels
17 |
18 | import com.peterchege.aiimagegenerator.MainDispatcherRule
19 | import com.peterchege.aiimagegenerator.data.api.NetworkResult
20 | import com.peterchege.aiimagegenerator.domain.models.ImageItem
21 | import com.peterchege.aiimagegenerator.domain.models.ImageResponse
22 | import com.peterchege.aiimagegenerator.domain.repository.ImageRepository
23 | import io.mockk.coEvery
24 | import io.mockk.mockk
25 | import kotlinx.coroutines.ExperimentalCoroutinesApi
26 | import kotlinx.coroutines.test.*
27 | import org.junit.Assert.*
28 |
29 | import org.junit.Before
30 | import org.junit.Rule
31 | import org.junit.Test
32 |
33 | val fakeImages = ('a'..'z').map { c ->
34 | ImageItem(
35 | url = c.toString()
36 | )
37 | }
38 |
39 | class HomeScreenViewModelTest {
40 | lateinit var homeScreenViewModel: HomeScreenViewModel
41 | val imageRepository = mockk(relaxed = true)
42 |
43 | @get:Rule
44 | val mainDispatcherRule = MainDispatcherRule()
45 |
46 | @Before
47 | fun setUp() {
48 | homeScreenViewModel = HomeScreenViewModel(repository = imageRepository)
49 | }
50 |
51 | @Test
52 | fun `When app is launched the state is idle `(){
53 | assert(homeScreenViewModel.uiState.value is HomeScreenUiState.Idle)
54 | }
55 |
56 |
57 | @Test
58 | fun `When the an error occurs, then an error state is loaded`() = runTest {
59 | coEvery { imageRepository.generateImages(any()) } returns
60 | NetworkResult.Error(code = 4000 ,message = "An unexpected error occurred")
61 | homeScreenViewModel.generateImages()
62 | assert(homeScreenViewModel.uiState.value is HomeScreenUiState.Error)
63 |
64 | }
65 | @Test
66 | fun `When the request is successful the images are loaded into state`() = runTest {
67 | coEvery { imageRepository.generateImages(any()) } returns
68 | NetworkResult.Success(data = ImageResponse(created = "", data = fakeImages))
69 |
70 | homeScreenViewModel.generateImages()
71 | assert(homeScreenViewModel.uiState.value is HomeScreenUiState.Success)
72 | }
73 |
74 | }
75 |
76 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.diffplug.spotless") version "5.3.0"
3 | id ("org.jetbrains.kotlin.jvm") version "1.9.0"
4 | id("com.google.devtools.ksp") version "1.9.10-1.0.13"
5 |
6 | }
7 |
8 | subprojects{
9 | apply(plugin = "com.google.devtools.ksp")
10 | apply(plugin = "dagger.hilt.android.plugin")
11 | }
12 | buildscript {
13 |
14 | repositories {
15 | google()
16 | mavenCentral()
17 |
18 | }
19 | dependencies {
20 | classpath ("com.android.tools.build:gradle:7.4.2")
21 | classpath ("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.10")
22 | classpath ("com.google.dagger:hilt-android-gradle-plugin:2.48")
23 | }
24 | }
25 |
26 | apply(plugin = "com.diffplug.spotless")
27 | spotless {
28 | kotlin {
29 | target("**/*.kt")
30 | licenseHeaderFile(
31 | rootProject.file("${project.rootDir}/spotless/LICENSE.txt"),
32 | "^(package|object|import|interface)"
33 | )
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chege4179/AIImageGeneratorApp/c87ec4f2fa0dfeda90faaa0ac04ac513bc3af5e2/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Dec 22 00:21:17 EAT 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | }
15 | rootProject.name = "AI Image Generator"
16 | include (":app")
17 |
--------------------------------------------------------------------------------
/spotless/LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright $YEAR AI Image Generator App Peter Chege Mwangi
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
--------------------------------------------------------------------------------