-
Notifications
You must be signed in to change notification settings - Fork 200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Crop image to square #952
Open
warahiko
wants to merge
20
commits into
DroidKaigi:main
Choose a base branch
from
warahiko:feature/crop_image
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Crop image to square #952
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
f2facf2
feat: Define ProfileImage to handle images and their cropping
warahiko e24fa7e
feat: Enable cropping for ProfileImage
warahiko 8a14a0c
feat: Calculate size of ProfileImage
warahiko 8a47e49
feat: Cache ProfileImage in ProfileCardRepository
warahiko 6949bec
feat: Add CropImageScreen and navigate when the selected image is not…
warahiko 9c677ac
feat: Crop image in CropImageScreen and save it to the repository
warahiko 6c1472a
feat: Apply Cropped image in ProfileCardEditScreen
warahiko f7e92c9
feat: implement for iOS
warahiko 468d810
feat: Change to rememberSaveable to retain input inputs during image …
warahiko be8e272
feat: Temporarily split processing between Android and iOS
warahiko 5f5d498
Merge branch 'main' into feature/crop_image
takahirom 320c57a
style: Declare properties before methods
warahiko ecd26ce
fix: Incorrect modifier specification
warahiko 1155000
style: Add trailing comma
warahiko 3ced7ce
style: Declare parameters without defaults before parameters with def…
warahiko f90429c
test: Fix compile error
warahiko 203d189
refactor: Rename eventEmitter -> eventFlow
warahiko e9e04d2
refactor: Use a flag in UiState instead of passing a callback to the …
warahiko 044cb2f
refactor: Rename onConfirm -> onBackWithConfirm
warahiko e22abae
test: Check the launch of CropImageScreen
warahiko File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
core/model/src/androidMain/kotlin/io/github/droidkaigi/confsched/model/ProfileImage.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package io.github.droidkaigi.confsched.model | ||
|
||
import android.graphics.Bitmap | ||
import android.graphics.BitmapFactory | ||
import androidx.compose.ui.unit.IntRect | ||
import androidx.compose.ui.unit.IntSize | ||
import java.io.ByteArrayOutputStream | ||
|
||
actual val ByteArray.imageSize: IntSize | ||
get() = this.asBitmap().let { | ||
IntSize(width = it.width, height = it.height) | ||
} | ||
|
||
actual fun ByteArray.crop(rect: IntRect): ByteArray { | ||
val cropped = Bitmap.createBitmap(this.asBitmap(), rect.left, rect.top, rect.width, rect.height) | ||
|
||
return ByteArrayOutputStream(cropped.byteCount).use { stream -> | ||
cropped.compress(Bitmap.CompressFormat.PNG, 100, stream) | ||
stream.toByteArray() | ||
} | ||
} | ||
|
||
private fun ByteArray.asBitmap(): Bitmap { | ||
return BitmapFactory.decodeByteArray(this, 0, size) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/ProfileImage.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package io.github.droidkaigi.confsched.model | ||
|
||
import androidx.compose.ui.unit.IntRect | ||
import androidx.compose.ui.unit.IntSize | ||
|
||
data class ProfileImage( | ||
val bytes: ByteArray, | ||
) { | ||
val size: IntSize | ||
get() = bytes.imageSize | ||
|
||
fun crop(rect: IntRect): ProfileImage { | ||
return copy( | ||
bytes = bytes.crop(rect), | ||
) | ||
} | ||
|
||
override fun equals(other: Any?): Boolean { | ||
if (this === other) return true | ||
if (other == null || this::class != other::class) return false | ||
|
||
other as ProfileImage | ||
|
||
return bytes.contentEquals(other.bytes) | ||
} | ||
|
||
override fun hashCode(): Int { | ||
return bytes.contentHashCode() | ||
} | ||
} | ||
|
||
expect val ByteArray.imageSize: IntSize | ||
expect fun ByteArray.crop(rect: IntRect): ByteArray |
30 changes: 30 additions & 0 deletions
30
core/model/src/iosMain/kotlin/io/github/droidkaigi/confsched/model/ProfileImage.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package io.github.droidkaigi.confsched.model | ||
|
||
import androidx.compose.ui.unit.IntRect | ||
import androidx.compose.ui.unit.IntSize | ||
import org.jetbrains.skia.Bitmap | ||
import org.jetbrains.skia.EncodedImageFormat | ||
import org.jetbrains.skia.Image | ||
|
||
actual val ByteArray.imageSize: IntSize | ||
get() = Bitmap.makeFromImage( | ||
Image.makeFromEncoded(this), | ||
).let { | ||
IntSize(width = it.width, height = it.height) | ||
} | ||
|
||
actual fun ByteArray.crop(rect: IntRect): ByteArray { | ||
val image = Image.makeFromEncoded(this) | ||
|
||
val bitmap = Bitmap() | ||
val imageInfo = image.imageInfo.withWidthHeight( | ||
width = rect.width, | ||
height = rect.height, | ||
) | ||
bitmap.allocPixels(imageInfo) | ||
image.readPixels(dst = bitmap, srcX = rect.left, srcY = rect.top) | ||
|
||
val data = Image.makeFromBitmap(bitmap) | ||
.encodeToData(format = EncodedImageFormat.PNG, quality = 100) | ||
return requireNotNull(data).bytes | ||
} |
31 changes: 31 additions & 0 deletions
31
...esting/src/main/java/io/github/droidkaigi/confsched/testing/robot/CropImageScreenRobot.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package io.github.droidkaigi.confsched.testing.robot | ||
|
||
import androidx.compose.ui.test.assertIsDisplayed | ||
import androidx.compose.ui.test.hasTestTag | ||
import io.github.droidkaigi.confsched.designsystem.theme.KaigiTheme | ||
import io.github.droidkaigi.confsched.profilecard.CropImageScreen | ||
import io.github.droidkaigi.confsched.profilecard.CropImageScreenTestTag | ||
import javax.inject.Inject | ||
|
||
class CropImageScreenRobot @Inject constructor( | ||
screenRobot: DefaultScreenRobot, | ||
) : ScreenRobot by screenRobot { | ||
|
||
fun setupScreenContent() { | ||
robotTestRule.setContent { | ||
KaigiTheme { | ||
CropImageScreen( | ||
onNavigationIconClick = {}, | ||
onBackWithConfirm = {}, | ||
) | ||
} | ||
} | ||
waitUntilIdle() | ||
} | ||
|
||
fun checkScreenDisplayed() { | ||
composeTestRule | ||
.onNode(hasTestTag(CropImageScreenTestTag)) | ||
.assertIsDisplayed() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
.../kotlin/io/github/droidkaigi/confsched/profilecard/component/SingleImagePickerLauncher.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package io.github.droidkaigi.confsched.profilecard.component | ||
|
||
import androidx.compose.runtime.Composable | ||
import com.preat.peekaboo.image.picker.ImagePickerLauncher | ||
import com.preat.peekaboo.image.picker.toImageBitmap | ||
import kotlinx.coroutines.CoroutineScope | ||
|
||
@Composable | ||
internal actual fun rememberCroppingImagePickerLauncher( | ||
onCropImage: (ByteArray) -> Unit, | ||
onSelectedImage: (ByteArray) -> Unit, | ||
scope: CoroutineScope, | ||
): ImagePickerLauncher { | ||
return rememberSingleImagePickerLauncher(scope) { | ||
val imageBitmap = it.toImageBitmap() | ||
|
||
if (imageBitmap.height != imageBitmap.width) { | ||
onCropImage(it) | ||
} else { | ||
onSelectedImage(it) | ||
} | ||
} | ||
} |
55 changes: 55 additions & 0 deletions
55
.../androidUnitTest/kotlin/io/github/droidkaigi/confsched/profilecard/CropImageScreenTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package io.github.droidkaigi.confsched.profilecard | ||
|
||
import dagger.hilt.android.testing.BindValue | ||
import dagger.hilt.android.testing.HiltAndroidTest | ||
import io.github.droidkaigi.confsched.testing.DescribedBehavior | ||
import io.github.droidkaigi.confsched.testing.describeBehaviors | ||
import io.github.droidkaigi.confsched.testing.execute | ||
import io.github.droidkaigi.confsched.testing.robot.CropImageScreenRobot | ||
import io.github.droidkaigi.confsched.testing.robot.runRobot | ||
import io.github.droidkaigi.confsched.testing.rules.RobotTestRule | ||
import org.junit.Rule | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
import org.robolectric.ParameterizedRobolectricTestRunner | ||
import javax.inject.Inject | ||
|
||
@RunWith(ParameterizedRobolectricTestRunner::class) | ||
@HiltAndroidTest | ||
class CropImageScreenTest( | ||
private val testCase: DescribedBehavior<CropImageScreenRobot>, | ||
) { | ||
|
||
@get:Rule | ||
@BindValue | ||
val robotTestRule: RobotTestRule = RobotTestRule(testInstance = this) | ||
|
||
@Inject | ||
lateinit var cropImageScreenRobot: CropImageScreenRobot | ||
|
||
@Test | ||
fun runTest() { | ||
runRobot(cropImageScreenRobot) { | ||
testCase.execute(cropImageScreenRobot) | ||
} | ||
} | ||
|
||
companion object { | ||
@JvmStatic | ||
@ParameterizedRobolectricTestRunner.Parameters(name = "{0}") | ||
fun behaviors(): List<DescribedBehavior<CropImageScreenRobot>> { | ||
return describeBehaviors(name = "CropImageScreen") { | ||
describe("when launch") { | ||
doIt { | ||
setupScreenContent() | ||
} | ||
itShould("show screen") { | ||
captureScreenWithChecks { | ||
checkScreenDisplayed() | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is necessary to resolve the warning.