Skip to content

Commit

Permalink
Merge pull request #51 from manununhez/release/v1/1.1.0
Browse files Browse the repository at this point in the history
release/v1/1.1.0
  • Loading branch information
manununhez authored Apr 19, 2024
2 parents 8f3db61 + ffed0e3 commit 2e3d9f7
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 43 deletions.
25 changes: 16 additions & 9 deletions .github/workflows/cd-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: set up JDK 17
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
Expand All @@ -32,7 +32,7 @@ jobs:
- name: Generate file name env var
run: |
DATE=$(date +'%d.%m.%Y')
DATE=$(date +'%d.%m.%Y-%H%M%S')
BRANCH_NAME=${GITHUB_REF##*/}
MESSAGE=$(cat << EOF
PurrfectPics-release-${BRANCH_NAME}-${DATE}
Expand All @@ -47,9 +47,9 @@ jobs:
mv "./app/build/outputs/apk/release/app-release.apk" "./app/build/outputs/apk/release/${{ env.OUTPUT_NAME }}.apk"

- name: Upload release APK
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v4
with:
name: ${{ env.OUTPUT_NAME }}
name: ${{ env.OUTPUT_NAME }}-APK
path: app/build/outputs/apk/release/${{ env.OUTPUT_NAME }}.apk

- name: Build release AAB
Expand All @@ -59,8 +59,15 @@ jobs:
run:
mv "./app/build/outputs/bundle/release/app-release.aab" "./app/build/outputs/bundle/release/${{ env.OUTPUT_NAME }}.aab"

- name: Upload debug AAB
uses: actions/upload-artifact@v1
- name: Upload release AAB
uses: actions/upload-artifact@v4
with:
name: ${{ env.OUTPUT_NAME }}
path: app/build/outputs/bundle/release/${{ env.OUTPUT_NAME }}.aab
name: ${{ env.OUTPUT_NAME }}-AAB
path: app/build/outputs/bundle/release/${{ env.OUTPUT_NAME }}.aab

- name: Merge Artifacts
uses: actions/upload-artifact/merge@v4
with:
name: merged-release-artifacts
pattern: ${{ env.OUTPUT_NAME }}-*
delete-merged: true
4 changes: 2 additions & 2 deletions .github/workflows/ci-android.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ jobs:

steps:
- name: Checkout project sources
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: set up JDK 17
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ heart.

## Screenshots

| ![Screenshot 2024-04-16 at 10 26 30 AM](https://github.com/manununhez/purrfect-pics/assets/5048531/c08f645e-a362-4d2e-bfff-c99d8966e2b8) | ![Screenshot 2024-04-19 at 12 06 41 AM](https://github.com/manununhez/purrfect-pics/assets/5048531/f7c9873c-8ab8-4215-b4f3-ed801dd8f6c9) | ![Screenshot 2024-04-19 at 12 17 14 AM](https://github.com/manununhez/purrfect-pics/assets/5048531/534f2f34-5f9d-4945-83bf-0076f667bc19)| ![Screenshot 2024-04-19 at 12 10 40 AM](https://github.com/manununhez/purrfect-pics/assets/5048531/69042702-71aa-4eae-b508-871d9c630ff8) | ![Screenshot 2024-04-19 at 12 16 02 AM](https://github.com/manununhez/purrfect-pics/assets/5048531/e55d1bae-6aa4-4401-a6b6-8754b0756efd)|
|----------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| ![Screenshot 2024-04-16 at 10 26 30 AM](https://github.com/manununhez/purrfect-pics/assets/5048531/c08f645e-a362-4d2e-bfff-c99d8966e2b8) | ![Screenshot 2024-04-19 at 12 06 41 AM](https://github.com/manununhez/purrfect-pics/assets/5048531/f7c9873c-8ab8-4215-b4f3-ed801dd8f6c9) | ![Screenshot 2024-04-19 at 12 17 14 AM](https://github.com/manununhez/purrfect-pics/assets/5048531/534f2f34-5f9d-4945-83bf-0076f667bc19) | ![Screenshot 2024-04-19 at 12 10 40 AM](https://github.com/manununhez/purrfect-pics/assets/5048531/69042702-71aa-4eae-b508-871d9c630ff8) | ![Screenshot 2024-04-19 at 12 16 02 AM](https://github.com/manununhez/purrfect-pics/assets/5048531/e55d1bae-6aa4-4401-a6b6-8754b0756efd) | ![Screenshot 2024-04-19 at 2 15 49 PM](https://github.com/manununhez/purrfect-pics/assets/5048531/b4185b5a-85b7-47e0-91df-ab0e0d822c42) |
|:--:|:--:|:--:|:--:|:--:|:--:|
| Splashscreen | Home - Featured and popular items | Popular paginated items | Your favorites cats | Detail image (share and favorite button) | Detail image with zoomable elements|

## Considerations

Expand Down
36 changes: 30 additions & 6 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
# hide the original source file name.
#-renamesourcefileattribute SourceFile

#Datastore
-keep class androidx.datastore.*.** {*;}
-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite* {
<fields>;
}

#https://stackoverflow.com/questions/42782328/retrofit-2-returns-null-in-release-apk-when-minifyenable-but-ok-in-debug-apk
-keep class com.manuelnunez.apps.core.services.dto.* {*;}
Expand Down Expand Up @@ -77,20 +82,39 @@
#Hilt
-keep class androidx.hilt.* {*;}

## Fragment names
-keepnames class * extends androidx.fragment.app.Fragment
#Serialization
# Keep `Companion` object fields of serializable classes.
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
-if @kotlinx.serialization.Serializable class **
-keepclassmembers class <1> {
static <1>$Companion Companion;
}

-keepnames class * extends android.os.Parcelable
-keepnames class * extends java.io.Serializable
# Keep `serializer()` on companion objects (both default and named) of serializable classes.
-if @kotlinx.serialization.Serializable class ** {
static **$* *;
}
-keepclassmembers class <2>$<3> {
kotlinx.serialization.KSerializer serializer(...);
}

# Keep `INSTANCE.serializer()` of serializable objects.
-if @kotlinx.serialization.Serializable class ** {
public static ** INSTANCE;
}
-keepclassmembers class <1> {
public static <1> INSTANCE;
kotlinx.serialization.KSerializer serializer(...);
}

# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
### R8
## Gson
-keepclassmembers,allowobfuscation class * {
@com.google.gson.annotations.SerializedName <fields>;
}

-keep class com.google.gson.reflect.TypeToken { *; }
-keep class * extends com.google.gson.reflect.TypeToken
## Kotlin suspend functions
-keep class kotlin.coroutines.Continuation

Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,45 @@
package com.manuelnunez.apps.core.ui.component

import androidx.compose.foundation.Image
import androidx.compose.foundation.gestures.detectTransformGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImagePainter
import coil.compose.AsyncImagePainter.State.Error
import coil.compose.AsyncImagePainter.State.Loading
import coil.compose.rememberAsyncImagePainter
import com.manuelnunez.apps.core.ui.R
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.roundToInt
import kotlin.math.sin

@Composable
fun StatefulAsyncImage(
modifier: Modifier = Modifier,
imageUrl: String,
contentDescription: String,
zoomable: Boolean = false,
placeholder: Painter = painterResource(R.drawable.ic_broken_image),
contentScale: ContentScale = ContentScale.Crop
) {
Expand All @@ -53,11 +66,73 @@ fun StatefulAsyncImage(
)
}

Image(
modifier = modifier,
painter = if (isError.not() && !isLocalInspection) imageLoader else placeholder,
contentScale =
if (isError.not() && !isLocalInspection) contentScale else ContentScale.FillBounds,
contentDescription = contentDescription)
if (zoomable) {
ZoomableImage(
modifier,
isError,
isLocalInspection,
imageLoader,
placeholder,
contentScale,
contentDescription)
} else {
Image(
modifier = modifier,
painter = if (isError.not() && !isLocalInspection) imageLoader else placeholder,
contentScale =
if (isError.not() && !isLocalInspection) contentScale else ContentScale.FillBounds,
contentDescription = contentDescription)
}
}
}

@Composable
private fun ZoomableImage(
modifier: Modifier,
isError: Boolean,
isLocalInspection: Boolean,
imageLoader: AsyncImagePainter,
placeholder: Painter,
contentScale: ContentScale,
contentDescription: String
) {
val angle by remember { mutableFloatStateOf(0f) }
var zoom by remember { mutableFloatStateOf(1f) }
var offsetX by remember { mutableFloatStateOf(0f) }
var offsetY by remember { mutableFloatStateOf(0f) }

val configuration = LocalConfiguration.current
val screenWidth = configuration.screenWidthDp.dp.value
val screenHeight = configuration.screenHeightDp.dp.value

Image(
modifier =
modifier
.offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
.graphicsLayer(scaleX = zoom, scaleY = zoom, rotationZ = angle)
.pointerInput(Unit) {
detectTransformGestures(
onGesture = { _, pan, gestureZoom, _ ->
zoom = (zoom * gestureZoom).coerceIn(1F..4F)
if (zoom > 1) {
val x = (pan.x * zoom)
val y = (pan.y * zoom)
val angleRad = angle * PI / 180.0

offsetX =
(offsetX + (x * cos(angleRad) - y * sin(angleRad)).toFloat()).coerceIn(
-(screenWidth * zoom)..(screenWidth * zoom))
offsetY =
(offsetY + (x * sin(angleRad) + y * cos(angleRad)).toFloat()).coerceIn(
-(screenHeight * zoom)..(screenHeight * zoom))
} else {
offsetX = 0F
offsetY = 0F
}
})
},
painter = if (isError.not() && !isLocalInspection) imageLoader else placeholder,
contentScale =
if (isError.not() && !isLocalInspection) contentScale else ContentScale.FillBounds,
contentDescription = contentDescription)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeContent
import androidx.compose.foundation.layout.windowInsetsPadding
Expand Down Expand Up @@ -76,10 +77,11 @@ private fun DetailPortrait(
modifier = Modifier.weight(1f).wrapContentSize(),
horizontalAlignment = Alignment.CenterHorizontally) {
StatefulAsyncImage(
modifier = Modifier.fillMaxWidth().padding(horizontal = 20.dp),
modifier = Modifier.fillMaxWidth().padding(horizontal = 20.dp).heightIn(min = 180.dp),
imageUrl = item.imageUrl,
contentDescription = item.photoId,
contentScale = ContentScale.Fit)
contentScale = ContentScale.Fit,
zoomable = true)

Row {
ShareImage(url = item.imageUrl)
Expand Down Expand Up @@ -128,10 +130,12 @@ private fun DetailLandscape(

Row(verticalAlignment = Alignment.CenterVertically) {
StatefulAsyncImage(
modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp).weight(0.7f),
modifier =
Modifier.fillMaxWidth().padding(vertical = 6.dp).weight(0.7f).heightIn(min = 180.dp),
imageUrl = item.imageUrl,
contentDescription = item.photoId,
contentScale = ContentScale.Fit)
contentScale = ContentScale.Fit,
zoomable = true)

Column(Modifier.weight(0.1f)) {
ShareImage(url = item.imageUrl)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
Expand All @@ -24,6 +26,9 @@ import androidx.compose.ui.unit.dp
import com.manuelnunez.apps.core.domain.model.Item
import com.manuelnunez.apps.core.ui.component.ImageCard
import com.manuelnunez.apps.core.ui.component.SurfaceText
import com.manuelnunez.apps.core.ui.theme.MainTheme
import com.manuelnunez.apps.core.ui.utils.OrientationPreviews
import com.manuelnunez.apps.core.ui.utils.ThemePreviews
import com.manuelnunez.apps.features.favorites.ui.R

@Composable
Expand All @@ -50,19 +55,50 @@ private fun FavoriteItems(navigateToDetails: (Item) -> Unit, favoriteItems: List
GridCells.Adaptive(minSize = 100.dp)
} else GridCells.Fixed(3)

LazyVerticalGrid(
columns = cellConfiguration,
modifier = Modifier.fillMaxSize(),
state = gridState,
contentPadding = PaddingValues(start = 20.dp, end = 20.dp, top = 10.dp, bottom = 20.dp),
verticalArrangement = Arrangement.spacedBy(10.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp)) {
items(items = favoriteItems) { item ->
ImageCard(
modifier = Modifier.size(width = 100.dp, height = 200.dp),
imageUrl = item.imageUrl,
cardContentDescription = item.description,
onClick = { navigateToDetails.invoke(item) })
if (favoriteItems.isEmpty()) {
Text(
modifier = Modifier.fillMaxSize().padding(10.dp),
color = MaterialTheme.colorScheme.onSurface,
text = "Add your favorite cats, and they will be display here! ")
} else {
LazyVerticalGrid(
columns = cellConfiguration,
modifier = Modifier.fillMaxSize(),
state = gridState,
contentPadding = PaddingValues(start = 20.dp, end = 20.dp, top = 10.dp, bottom = 20.dp),
verticalArrangement = Arrangement.spacedBy(10.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp)) {
items(items = favoriteItems) { item ->
ImageCard(
modifier = Modifier.size(width = 100.dp, height = 200.dp),
imageUrl = item.imageUrl,
cardContentDescription = item.description,
onClick = { navigateToDetails.invoke(item) })
}
}
}
}
}

@ThemePreviews
@OrientationPreviews
@Composable
fun FavoriteScreenEmptyPreview() {
MainTheme { FavoritesScreen(items = emptyList(), navigateToDetails = {}, onBackClick = {}) }
}

@ThemePreviews
@OrientationPreviews
@Composable
fun FavoriteScreenPreview() {
MainTheme {
val items =
List(5) { index ->
Item(
"$index",
"https://example.com/$index",
description = "description: $index",
thumbnailUrl = "https://example.com/$index")
}
FavoritesScreen(items = items, navigateToDetails = {}, onBackClick = {})
}
}

0 comments on commit 2e3d9f7

Please sign in to comment.