Skip to content

Commit

Permalink
Merge branch 'main' of github.com:manununhez/purrfect-pics into relea…
Browse files Browse the repository at this point in the history
…se/v1/1.0.0
  • Loading branch information
manununhez committed Apr 15, 2024
2 parents c94b89f + 9791be0 commit 983c419
Show file tree
Hide file tree
Showing 55 changed files with 548 additions and 351 deletions.
13 changes: 6 additions & 7 deletions .github/workflows/cd-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,23 @@ jobs:
cache: gradle

- name: Decode keystore and create jks
run: echo "${{ secrets.KEY_KEYSTORE_BASE64 }}" | base64 --decode > app/purrfect-pics-key.jks
run: echo "${{ secrets.KEY_KEYSTORE_BASE64 }}" | base64 --decode > app/keystore.jks

- name: Generate keystore.properties
run: |
cat <<EOF > keystore.properties
PEXELS_API_KEY:${{ secrets.PEXELS_API_KEY }}
KEYSTORE_PASSWORD:${{ secrets.KEYSTORE_PASSWORD }}
KEY_PASSWORD:${{ secrets.KEY_PASSWORD }}
KEY_ALIAS:${{ secrets.KEY_ALIAS }}
KEYSTORE_FILE:${{ secrets.KEYSTORE_FILE }}
PEXELS_API_KEY=${{ secrets.PEXELS_API_KEY }}
KEY_PASSWORD=${{ secrets.KEY_PASSWORD }}
KEY_ALIAS=${{ secrets.KEY_ALIAS }}
KEYSTORE_FILE=${{ secrets.KEYSTORE_FILE }}
EOF
- name: Generate file name env var
run: |
DATE=$(date +'%d.%m.%Y')
BRANCH_NAME=${GITHUB_REF##*/}
MESSAGE=$(cat << EOF
AppName-release-${BRANCH_NAME}-${DATE}
PurrfectPics-release-${BRANCH_NAME}-${DATE}
EOF)
echo OUTPUT_NAME=$MESSAGE >> $GITHUB_ENV
Expand Down
72 changes: 38 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,57 +9,62 @@ heart.
- **Discover:** Explore a vast collection of random cat images sourced from the web.
- **Share:** Share delightful cat images with friends, family, and fellow cat enthusiasts with just
a tap.
- **Save Favorites:** (Coming soon) Save your favorite cat images to easily revisit them later and
create your
personalized collection.
- **Save Favorites: (Coming soon)** Save your favorite cat images to easily revisit them later and
create your personalized collection.

## Tools/Libraries

### Android Libraries

- **UI Components:** Essential libraries for building UI components and handling UI-related
tasks. [AndroidX Core KTX](https://developer.android.com/jetpack/androidx/releases/core) | [Material Components for Android](https://github.com/material-components/material-components-android) | [Compose UI](https://developer.android.com/jetpack/androidx/releases/compose-ui)
- **Testing:** Frameworks and tools for writing and running tests to ensure code quality and
reliability. [JUnit](https://junit.org/junit5/) | [MockK](https://mockk.io/) | [Turbine](https://github.com/cashapp/turbine)
- **Dependency Injection:** Tools for managing dependencies and implementing dependency injection in
your project. [Hilt](https://developer.android.com/training/dependency-injection/hilt-android)
- **Coroutines:** Kotlin coroutine libraries for handling asynchronous programming tasks
efficiently. [Kotlin Coroutines](https://kotlinlang.org/docs/coroutines-overview.html)
- **Networking:** Libraries for making network requests and handling network
communication. [Retrofit](https://square.github.io/retrofit/)
- **Image Loading:** Libraries for loading and displaying images efficiently in your
app. [Coil](https://coil-kt.github.io/coil/)
- **UI Components:** [AndroidX Core KTX](https://developer.android.com/jetpack/androidx/releases/core) | [Material Components for Android](https://github.com/material-components/material-components-android) | [Compose UI](https://developer.android.com/jetpack/androidx/releases/compose-ui)
- **Testing:** [JUnit](https://junit.org/junit5/) | [MockK](https://mockk.io/) | [Turbine](https://github.com/cashapp/turbine)
- **Dependency Injection:** [Hilt](https://developer.android.com/training/dependency-injection/hilt-android)
- **Coroutines:** [Kotlin Coroutines](https://kotlinlang.org/docs/coroutines-overview.html)
- **Networking:** [Retrofit](https://square.github.io/retrofit/)
- **Image Loading:** [Coil](https://coil-kt.github.io/coil/)

### Compose Libraries

- **UI Components:** Libraries for building UI components using Jetpack
Compose. [Material3](https://developer.android.com/jetpack/androidx/releases/compose-material3) | [Compose UI](https://developer.android.com/jetpack/androidx/releases/compose-ui)
- **Navigation:** Libraries for implementing navigation in Jetpack Compose
apps. [Navigation Compose](https://developer.android.com/jetpack/androidx/releases/navigation) | [Hilt Navigation Compose](https://developer.android.com/training/dependency-injection/hilt-android#navigation-compose)
- **Material Design:** Libraries for implementing Material Design components and theming in Jetpack
Compose. [Material Components for Android](https://github.com/material-components/material-components-android)
- **UI Components:** [Material3](https://developer.android.com/jetpack/androidx/releases/compose-material3) | [Compose UI](https://developer.android.com/jetpack/androidx/releases/compose-ui)
- **Navigation:** [Navigation Compose](https://developer.android.com/jetpack/androidx/releases/navigation) | [Hilt Navigation Compose](https://developer.android.com/training/dependency-injection/hilt-android#navigation-compose)
- **Material Design:** [Material Components for Android](https://github.com/material-components/material-components-android)

### Other
## Screenshots

- **Build Tools:** Essential tools for building, testing, and packaging your Android
app. [Android Gradle Plugin](https://developer.android.com/studio/releases/gradle-plugin) | [Kotlin Symbol Processing (KSP)](https://github.com/google/ksp)
| ![Screenshot 2024-04-15 at 6 34 16 PM](https://github.com/manununhez/purrfect-pics/assets/5048531/a8024c0c-e31f-4189-b268-1167048658ad) | ![Screenshot 2024-04-15 at 6 35 33 PM](https://github.com/manununhez/purrfect-pics/assets/5048531/1a72c5ea-ee22-4470-8e98-6215ebb86924) | ![Screenshot 2024-04-15 at 6 36 08 PM](https://github.com/manununhez/purrfect-pics/assets/5048531/c31637d7-ddfe-436a-8e16-d27244d638ea) |
|---|---|---|

## Screenshots
## Considerations

[Include screenshots or GIFs of the app in action]
- FREE Cat APIs used in this version: [Cataas](https://cataas.com/)
and [Pexels](https://www.pexels.com/). In particular, Pexels API
is [rate-limited](https://www.pexels.com/api/documentation/#guidelines), in case of errors for
this, a new API key will be necessary to be generated.
- Not persisted in DB or preferences because this API has dynamic and frequently updated data. On
the other hand, Coil image lib does use disk and memory cache to smooth image loading.
- KtfmtFormat plugin applied for code formatting.
- There is a known issue with the splash screen not showing on Android 12. A temporary
solution is to **open the app from the app tray**, as indicated [here](https://stackoverflow.com/questions/69812590/android-12-splash-screen-icon-not-displaying))

## Getting Started

To get started with PurrfectPics, follow these steps:

1. Clone the repository to your local machine:
git clone [email protected]:manununhez/purrfect-pics.git


1. Clone the repository to your local
machine: `git clone [email protected]:manununhez/purrfect-pics.git`
2. Open the project in Android Studio.

3. Build and run the app on your device or emulator.

## Branching Strategy

- **Main Branch:** The primary branch of the project, reflecting the latest stable release.
- **Feature Branches:** Used for developing new features. Branch names should be descriptive of the
feature being developed.
- **Epic Branches:** Used for grouping related features or significant enhancements. Branch names
should reflect the overarching theme of the epic.
- **Release Branches:** Used for preparing releases. Branch names should follow the
pattern `release/v<MAJOR>/<MAJOR>.<MINOR>.<PATCH>`.

## Contributing

We welcome contributions from the open-source community to help improve PurrfectPics. Whether you're
Expand All @@ -76,6 +81,5 @@ PurrfectPics is licensed under the [MIT License](LICENSE).

## About

PurrfectPics is developed and maintained by [Manuel Nuñez]. For inquiries, please
contact [[email protected]].

PurrfectPics is developed and maintained by [Manuel Nuñez](mailto:[email protected]). For
inquiries, please contact [[email protected]].
30 changes: 14 additions & 16 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ android {
namespace = "com.manuelnunez.apps"
compileSdk = 34

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

defaultConfig {
applicationId = "com.manuelnunez.apps.purrfectpics"
minSdk = 21
Expand All @@ -31,12 +36,20 @@ android {
storeFile = file(getProperty("KEYSTORE_FILE"))
keyAlias = getProperty("KEY_ALIAS")
keyPassword = getProperty("KEY_PASSWORD")
storePassword = getProperty("KEYSTORE_PASSWORD")
storePassword = getProperty("KEY_PASSWORD")
}
}
}
}

buildFeatures { compose = true }

composeOptions { kotlinCompilerExtensionVersion = libs.versions.androidxComposeCompiler.get() }

tasks.withType<Test> { useJUnitPlatform() }

packaging { resources { excludes.add("META-INF/{LICENSE-notice.md,LICENSE.md}") } }

buildTypes {
getByName("release") {
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
Expand All @@ -53,21 +66,6 @@ android {
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
}
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

kotlinOptions { jvmTarget = JavaVersion.VERSION_17.toString() }

buildFeatures { compose = true }

composeOptions { kotlinCompilerExtensionVersion = libs.versions.androidxComposeCompiler.get() }

tasks.withType<Test> { useJUnitPlatform() }

packaging { resources { excludes.add("META-INF/{LICENSE-notice.md,LICENSE.md}") } }
}

dependencies {
Expand Down
6 changes: 2 additions & 4 deletions core/common-ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ android {
namespace = "com.manuelnunez.apps.core.ui"
compileSdk = 34

defaultConfig { minSdk = 21 }

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

defaultConfig { minSdk = 21 }

kotlinOptions { jvmTarget = JavaVersion.VERSION_17.toString() }

buildFeatures { compose = true }
Expand All @@ -29,8 +29,6 @@ dependencies {

implementation(libs.coil.kt.compose)

implementation(libs.androidx.navigation.compose)

// Compose
val composeBom = platform(libs.androidx.compose.bom)
implementation(composeBom)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
package com.manuelnunez.apps.core.ui.component

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.manuelnunez.apps.core.ui.R
import com.manuelnunez.apps.core.ui.theme.MainTheme
import com.manuelnunez.apps.core.ui.utils.ThemePreviews
import kotlin.math.roundToInt

@Composable
Expand All @@ -28,3 +40,49 @@ data class AdaptableVerticalGridDecoration(
val itemWidth: Dp,
val itemHorizontalMargin: Dp,
)

/** A simple grid which lays elements out vertically in evenly sized [columns]. */
@Composable
fun VerticalGrid(modifier: Modifier = Modifier, columns: Int = 2, content: @Composable () -> Unit) {
Layout(content = content, modifier = modifier) { measurables, constraints ->
val itemWidth = constraints.maxWidth / columns
// Keep given height constraints, but set an exact width
val itemConstraints = constraints.copy(minWidth = itemWidth, maxWidth = itemWidth)
// Measure each item with these constraints
val placeables = measurables.map { it.measure(itemConstraints) }
// Track each columns height so we can calculate the overall height
val columnHeights = Array(columns) { 0 }
placeables.forEachIndexed { index, placeable ->
val column = index % columns
columnHeights[column] += placeable.height
}
val height =
(columnHeights.maxOrNull() ?: constraints.minHeight).coerceAtMost(constraints.maxHeight)
layout(width = constraints.maxWidth, height = height) {
// Track the Y co-ord per column we have placed up to
val columnY = Array(columns) { 0 }
placeables.forEachIndexed { index, placeable ->
val column = index % columns
placeable.placeRelative(x = column * itemWidth, y = columnY[column])
columnY[column] += placeable.height
}
}
}
}

@ThemePreviews
@Composable
fun AdaptableVerticalGridPreview() {
MainTheme {
AdaptableVerticalGrid(decoration = AdaptableVerticalGridDecoration(20.dp, 25.dp, 55.dp)) {
List(10) {
Card(Modifier.size(50.dp, 80.dp).padding(horizontal = 10.dp).padding(bottom = 20.dp)) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Icon(
painter = painterResource(id = R.drawable.ic_broken_image), contentDescription = "")
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.vector.ImageVector
import com.manuelnunez.apps.core.ui.theme.MainTheme
import com.manuelnunez.apps.core.ui.utils.ThemePreviews

@Composable
fun ErrorDialog(dialogTitle: String, dialogText: String, onConfirmation: () -> Unit) {
Expand Down Expand Up @@ -46,3 +48,14 @@ fun ErrorAlertDialog(
confirmButton = { TextButton(onClick = { onConfirmation() }) { Text("Confirm") } },
dismissButton = { TextButton(onClick = { onDismissRequest() }) { Text("Dismiss") } })
}

@ThemePreviews
@Composable
fun ErrorDialogPreview() {
MainTheme {
ErrorDialog(
onConfirmation = {},
dialogTitle = "Title",
dialogText = "https://picsum.photos/id/237/200/300")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.manuelnunez.apps.core.ui.component
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
Expand All @@ -12,6 +13,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextAlign
import com.manuelnunez.apps.core.ui.theme.MainTheme
import com.manuelnunez.apps.core.ui.utils.ThemePreviews

Expand All @@ -20,12 +22,13 @@ fun TextCard(modifier: Modifier = Modifier, text: String, onClick: () -> Unit) {
Card(
colors =
CardDefaults.cardColors().copy(containerColor = MaterialTheme.colorScheme.onBackground),
modifier =
modifier.clickable(onClick = { onClick.invoke() }).semantics {
contentDescription = text
}) {
modifier = modifier.clickable(onClick = onClick).semantics { contentDescription = text }) {
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
Text(text = text, color = MaterialTheme.colorScheme.background)
Text(
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
text = text,
color = MaterialTheme.colorScheme.background)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,24 @@ fun TitleText(modifier: Modifier = Modifier, title: String, textAlign: TextAlign
style = MaterialTheme.typography.titleLarge)
}

@Composable
fun ErrorText(modifier: Modifier = Modifier, title: String, textAlign: TextAlign? = null) {
Text(
modifier = modifier,
text = title,
textAlign = textAlign,
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.titleSmall)
}

@ThemePreviews
@Composable
fun TitleTextPreview() {
MainTheme { TitleText(title = "See more") }
}

@ThemePreviews
@Composable
fun ErrorTextPreview() {
MainTheme { ErrorText(title = "See more") }
}
Loading

0 comments on commit 983c419

Please sign in to comment.