Skip to content

Commit 66e7f69

Browse files
committed
fix: Android N Cat Editor draw problem
ref: https://issuetracker.google.com/issues/37138664 Signed-off-by: Hu Shenghao <[email protected]>
1 parent 6f3f3c3 commit 66e7f69

File tree

15 files changed

+189
-18
lines changed

15 files changed

+189
-18
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
### v3.5.0
44

5+
- Fix Cat Editor draw problem on Android N [#37138664](https://issuetracker.google.com/issues/37138664)
56
- Fix Cat Editor color hvs palette maxWidth [#522](https://github.com/hushenghao/AndroidEasterEggs/issues/522)
67
- Fix Cat Editor storage permission on Android Q below
78
- Update Android Baklava Snapshot background

CHANGELOG_zh.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
### v3.5.0
44

5+
- 修复 Cat Editor 在 Android N 的绘制问题 [#37138664](https://issuetracker.google.com/issues/37138664)
56
- 修复 Cat Editor hvs颜色选择器最大宽度 [#522](https://github.com/hushenghao/AndroidEasterEggs/issues/522)
67
- 修复 Cat Editor 在 Android Q 以下时的存储权限
78
- 更新 Android Baklava 快照背景

basic/src/main/java/com/dede/basic/ImageExt.kt

+19-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@
22

33
package com.dede.basic
44

5-
import android.content.*
5+
import android.Manifest
6+
import android.content.ContentResolver
7+
import android.content.ContentUris
8+
import android.content.ContentValues
9+
import android.content.Context
10+
import android.content.Intent
611
import android.graphics.Bitmap
712
import android.net.Uri
813
import android.os.Build
914
import android.os.Environment
1015
import android.provider.MediaStore
1116
import android.util.Log
17+
import androidx.annotation.RequiresPermission
1218
import java.io.File
1319
import java.io.FileNotFoundException
1420
import java.io.InputStream
@@ -62,6 +68,10 @@ private class OutputFileTaker(var file: File? = null)
6268
* @param fileName 文件名。 需要携带后缀
6369
* @param relativePath 相对于Pictures的路径
6470
*/
71+
@RequiresPermission(
72+
allOf = [Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE],
73+
conditional = true
74+
)
6575
fun File.copyToAlbum(context: Context, fileName: String, relativePath: String?): Uri? {
6676
if (!this.canRead() || !this.exists()) {
6777
Log.w(TAG, "check: read file error: $this")
@@ -79,6 +89,10 @@ fun File.copyToAlbum(context: Context, fileName: String, relativePath: String?):
7989
* @param fileName 文件名。 需要携带后缀
8090
* @param relativePath 相对于Pictures的路径
8191
*/
92+
@RequiresPermission(
93+
allOf = [Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE],
94+
conditional = true
95+
)
8296
fun InputStream.saveToAlbum(context: Context, fileName: String, relativePath: String?): Uri? {
8397
val resolver = context.contentResolver
8498
val outputFile = OutputFileTaker()
@@ -107,6 +121,10 @@ fun InputStream.saveToAlbum(context: Context, fileName: String, relativePath: St
107121
* @param relativePath 相对于Pictures的路径
108122
* @param quality 质量
109123
*/
124+
@RequiresPermission(
125+
allOf = [Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE],
126+
conditional = true
127+
)
110128
fun Bitmap.saveToAlbum(
111129
context: Context,
112130
fileName: String,

basic/src/main/java/com/dede/basic/utils/ShareCatUtils.kt

+12-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import android.graphics.Bitmap
1212
import android.net.Uri
1313
import android.os.Build
1414
import android.util.Log
15+
import androidx.annotation.ChecksSdkIntAtLeast
16+
import androidx.annotation.RequiresPermission
1517
import com.dede.basic.MIME_PNG
1618
import com.dede.basic.createChooser
1719
import com.dede.basic.launch
@@ -33,14 +35,19 @@ object ShareCatUtils {
3335
private const val CATS_DIR = "Cats"
3436

3537
@JvmStatic
36-
val isRequireStoragePermissions = Build.VERSION.SDK_INT < Build.VERSION_CODES.Q
38+
@ChecksSdkIntAtLeast(Build.VERSION_CODES.Q)
39+
val isNotRequireStoragePermissions = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
3740

3841
@JvmStatic
3942
val storagePermissions = arrayOf(
4043
Manifest.permission.READ_EXTERNAL_STORAGE,
4144
Manifest.permission.WRITE_EXTERNAL_STORAGE
4245
)
4346

47+
@RequiresPermission(
48+
allOf = [Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE],
49+
conditional = true
50+
)
4451
suspend fun saveCat(context: Context, bitmap: Bitmap, catName: String): Uri? =
4552
withContext(Dispatchers.IO) {
4653
try {
@@ -55,6 +62,10 @@ object ShareCatUtils {
5562
}
5663

5764
@JvmStatic
65+
@RequiresPermission(
66+
allOf = [Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE],
67+
conditional = true
68+
)
5869
fun shareCat(activity: Activity, bitmap: Bitmap, catName: String) {
5970
activity.lifecycleCompat.launch {
6071
val uri = saveCat(activity, bitmap, catName) ?: return@launch

eggs/Nougat/src/main/java/com/android_n/egg/neko/NekoLand.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ public void onClick(DialogInterface dialog, int which) {
253253
public void onClick(View v) {
254254
setContextGroupVisible(holder, false);
255255
Cat cat = mCats[holder.getBindingAdapterPosition()];
256-
if (!ShareCatUtils.isRequireStoragePermissions()) {
256+
if (ShareCatUtils.isNotRequireStoragePermissions()) {
257257
shareCat(cat);
258258
return;
259259
}

eggs/R/src/main/java/com/android_r/egg/neko/NekoLand.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ public void onClick(DialogInterface dialog, int which) {
262262
public void onClick(View v) {
263263
setContextGroupVisible(holder, false);
264264
Cat cat = mCats[holder.getBindingAdapterPosition()];
265-
if (!ShareCatUtils.isRequireStoragePermissions()) {
265+
if (ShareCatUtils.isNotRequireStoragePermissions()) {
266266
shareCat(cat);
267267
return;
268268
}

eggs/S/src/main/java/com/android_s/egg/neko/NekoLand.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ public void onClick(DialogInterface dialog, int which) {
261261
public void onClick(View v) {
262262
setContextGroupVisible(holder, false);
263263
Cat cat = mCats[holder.getBindingAdapterPosition()];
264-
if (!ShareCatUtils.isRequireStoragePermissions()) {
264+
if (ShareCatUtils.isNotRequireStoragePermissions()) {
265265
shareCat(cat);
266266
return;
267267
}

eggs/Tiramisu/src/main/java/com/android_t/egg/neko/NekoLand.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ public void onClick(DialogInterface dialog, int which) {
259259
public void onClick(View v) {
260260
setContextGroupVisible(holder, false);
261261
Cat cat = mCats[holder.getBindingAdapterPosition()];
262-
if (!ShareCatUtils.isRequireStoragePermissions()) {
262+
if (ShareCatUtils.isNotRequireStoragePermissions()) {
263263
shareCat(cat);
264264
return;
265265
}

fastlane/metadata/android/en-US/changelogs/60.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
- Fix Cat Editor draw problem on Android N
12
- Fix Cat Editor color hvs palette maxWidth
23
- Fix Cat Editor storage permission on Android Q below
34
- Update Android Baklava Snapshot background

fastlane/metadata/android/zh-CN/changelogs/60.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
- 修复 Cat Editor 在 Android N 的绘制问题
12
- 修复 Cat Editor hvs颜色选择器最大宽度
23
- 修复 Cat Editor 在 Android Q 以下时的存储权限
34
- 更新 Android Baklava 快照背景

feature/cat-editor/src/main/java/com/dede/android_eggs/cat_editor/CatEditor.kt

+40-11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
package com.dede.android_eggs.cat_editor
44

5+
import android.graphics.Bitmap
56
import androidx.compose.animation.AnimatedVisibility
67
import androidx.compose.animation.core.LinearEasing
78
import androidx.compose.animation.core.RepeatMode
@@ -22,21 +23,23 @@ import androidx.compose.foundation.layout.fillMaxSize
2223
import androidx.compose.runtime.Composable
2324
import androidx.compose.runtime.LaunchedEffect
2425
import androidx.compose.runtime.getValue
26+
import androidx.compose.runtime.mutableStateOf
2527
import androidx.compose.runtime.remember
2628
import androidx.compose.runtime.setValue
2729
import androidx.compose.ui.Alignment
2830
import androidx.compose.ui.ExperimentalComposeUiApi
2931
import androidx.compose.ui.Modifier
3032
import androidx.compose.ui.geometry.Offset
33+
import androidx.compose.ui.geometry.Size
3134
import androidx.compose.ui.graphics.Matrix
3235
import androidx.compose.ui.graphics.drawscope.withTransform
3336
import androidx.compose.ui.graphics.graphicsLayer
3437
import androidx.compose.ui.input.pointer.pointerInput
35-
import androidx.compose.ui.layout.onSizeChanged
3638
import androidx.compose.ui.res.stringResource
3739
import androidx.compose.ui.tooling.preview.Preview
3840
import com.dede.android_eggs.cat_editor.CaptureControllerDelegate.Companion.rememberCaptureControllerDelegate
3941
import com.dede.android_eggs.cat_editor.CatParts.VIEW_PORT_SIZE
42+
import com.dede.android_eggs.cat_editor.Utilities.asAndroidMatrix
4043
import com.dede.android_eggs.cat_editor.Utilities.toInvert
4144
import dev.shreyaspatil.capturable.capturable
4245
import kotlin.math.max
@@ -45,8 +48,8 @@ import com.dede.android_eggs.resources.R as StringR
4548

4649
private const val TAG = "CatEditor"
4750

48-
private const val S_MIN = 0.5f
49-
private const val S_MAX = 3f
51+
private const val S_MIN = 0.3f
52+
private const val S_MAX = 5f
5053

5154
private fun range(float: Float, max: Float, min: Float): Float {
5255
return min(max, max(float, min))
@@ -132,27 +135,26 @@ internal fun CatEditor(
132135
val infiniteTransition = rememberInfiniteTransition(label = "CatEditor_SelectedPart")
133136
val blendRatio by infiniteTransition.animateFloat(
134137
initialValue = 0f,
135-
targetValue = 0.5f,
138+
targetValue = if (selectedPart != -1) 0.5f else 0f,
136139
animationSpec = infiniteRepeatable(
137140
animation = tween(700, easing = LinearEasing),
138141
repeatMode = RepeatMode.Reverse,
139142
),
140143
label = "HighlightColorBlend"
141144
)
142145

143-
val canvasMatrix = remember { Matrix() }
146+
var canvasSize by remember { mutableStateOf(Size.Zero) }
147+
val canvasMatrix = remember(canvasSize) { createCanvasMatrix(canvasSize) }
148+
149+
val bitmap: Bitmap? = remember(canvasSize) {
150+
if (needAndroidCanvasDraw) createAndroidBitmap(canvasSize) else null
151+
}
144152
Canvas(
145153
contentDescription = stringResource(StringR.string.cat_editor),
146154
modifier = Modifier
147155
.fillMaxSize()
148156
.aspectRatio(1f)
149157
.capturable(captureController.getDelegate())
150-
.onSizeChanged {
151-
val size = min(it.width, it.height)
152-
canvasMatrix.reset()
153-
canvasMatrix.translate((it.width - size) / 2f, (it.height - size) / 2f)
154-
canvasMatrix.scale(size / VIEW_PORT_SIZE, size / VIEW_PORT_SIZE)
155-
}
156158
.pointerInput(controllerImpl.isSelectEnabled) {
157159
if (!controllerImpl.isSelectEnabled) {
158160
return@pointerInput
@@ -180,6 +182,25 @@ internal fun CatEditor(
180182
}
181183
},
182184
onDraw = {
185+
if (size != canvasSize) {
186+
// canvas size changed
187+
canvasSize = size
188+
return@Canvas
189+
}
190+
191+
// native canvas draw
192+
if (needAndroidCanvasDraw && bitmap != null) {
193+
androidCanvasDraw(
194+
canvasMatrix.asAndroidMatrix(),
195+
bitmap,
196+
controllerImpl.colorList,
197+
selectedPart,
198+
blendRatio
199+
)
200+
return@Canvas
201+
}
202+
203+
// compose canvas draw
183204
withTransform({ transform(canvasMatrix) }) {
184205
CatParts.drawOrders.forEachIndexed { index, pathDraw ->
185206
var color = controllerImpl.colorList[index]
@@ -195,3 +216,11 @@ internal fun CatEditor(
195216
}
196217
}
197218
}
219+
220+
private fun createCanvasMatrix(size: Size): Matrix {
221+
val matrix = Matrix()
222+
val minDimension = size.minDimension
223+
matrix.translate((size.width - minDimension) / 2f, (size.height - minDimension) / 2f)
224+
matrix.scale(minDimension / VIEW_PORT_SIZE, minDimension / VIEW_PORT_SIZE)
225+
return matrix
226+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.dede.android_eggs.cat_editor
2+
3+
import android.graphics.Bitmap
4+
import android.graphics.Color
5+
import android.graphics.Matrix
6+
import android.graphics.Paint
7+
import android.os.Build
8+
import androidx.compose.ui.geometry.Size
9+
import androidx.compose.ui.graphics.drawscope.DrawScope
10+
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
11+
import androidx.compose.ui.graphics.nativeCanvas
12+
import androidx.core.graphics.applyCanvas
13+
import androidx.core.graphics.createBitmap
14+
import androidx.core.graphics.withMatrix
15+
16+
17+
internal fun createAndroidBitmap(size: Size): Bitmap {
18+
var width = size.width.toInt()
19+
var height = size.height.toInt()
20+
if (width <= 0) {
21+
width = 1
22+
}
23+
if (height <= 0) {
24+
height = 1
25+
}
26+
return createBitmap(width, height)
27+
}
28+
29+
// fix Android N canvas scale
30+
internal val needAndroidCanvasDraw =
31+
Build.VERSION.SDK_INT in Build.VERSION_CODES.N..Build.VERSION_CODES.N_MR1
32+
33+
private val androidPaint = Paint(Paint.ANTI_ALIAS_FLAG)
34+
35+
// https://issuetracker.google.com/issues/37138664
36+
internal fun DrawScope.androidCanvasDraw(
37+
matrix: Matrix,
38+
bitmap: Bitmap,
39+
colorList: List<androidx.compose.ui.graphics.Color>,
40+
selectedPart: Int,
41+
blendRatio: Float
42+
) {
43+
// clear bitmap
44+
bitmap.eraseColor(Color.TRANSPARENT)
45+
46+
// draw bitmap
47+
bitmap.applyCanvas {
48+
withMatrix(matrix) {
49+
CatParts.drawOrders.forEachIndexed { index, pathDraw ->
50+
var color = colorList[index]
51+
if (selectedPart == index) {
52+
val blend = Utilities.getHighlightColor(color)
53+
color = Utilities.blendColor(color, blend, blendRatio)
54+
}
55+
pathDraw.drawLambda2.invoke(this, color, androidPaint)
56+
}
57+
}
58+
}
59+
60+
// draw native canvas
61+
drawIntoCanvas { c ->
62+
c.nativeCanvas.drawBitmap(bitmap, 0f, 0f, null)
63+
}
64+
}

feature/cat-editor/src/main/java/com/dede/android_eggs/cat_editor/CatEditorScreen.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ fun CatEditorScreen() {
209209
IconButton(
210210
onClick = {
211211
when {
212-
!ShareCatUtils.isRequireStoragePermissions -> saveCatToAlbum()
212+
ShareCatUtils.isNotRequireStoragePermissions -> saveCatToAlbum()
213213
storagePermissionState.allPermissionsGranted -> saveCatToAlbum()
214214
storagePermissionState.shouldShowRationale -> context.toast("🚫")
215215
else -> storagePermissionState.launchMultiplePermissionRequest()

0 commit comments

Comments
 (0)