Skip to content

Commit 264e552

Browse files
committed
Merge origin/master into feat/migrate_to_compose (S4d-169: resolve PR #358 conflicts)
Unblock PR #358 pull_request CI while keeping it Draft. Resolve the 6 known conflicts: - legacy View files kept deleted: MainActivity/AboutActivity/OpenSourceActivity/LaunchView - .gitignore: union of both sides' additive entries - pr_pre_check.yml: preserve the migration safety net (:app:assembleDebug, :shared:desktopTest, :app:testDebugUnitTest, strict WATERMARK_GOLDEN goldens); adopt master scaffolding (permissions, concurrency, paths-ignore, workflow_dispatch, wrapper validation, setup-android); DROP master's draft-skip guard so Draft PR #358 still runs the safety net; do NOT adopt master's compile-only -x test - desktop_packaging.yml untouched Verified: :app:assembleDebug + :shared:desktopTest BUILD SUCCESSFUL. Local commit only; not pushed.
2 parents 789c794 + 71b1e10 commit 264e552

22 files changed

Lines changed: 313 additions & 337 deletions

File tree

.github/workflows/pr_pre_check.yml

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,75 @@
1-
name: Basic Check
1+
name: PR Checks
22

33
env:
44
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx3072m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:MaxMetaspaceSize=1g" -Dkotlin.daemon.jvm.options="-Xmx2560m" -Dkotlin.incremental=false
5-
CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 2 --no-daemon
65

76
on:
87
pull_request:
9-
types: [opened,reopened,synchronize]
8+
types: [opened, reopened, synchronize]
109
branches:
1110
- master
11+
paths-ignore:
12+
- '**/*.md'
13+
- 'fastlane/**'
14+
- 'static/**'
15+
- 'captures/**'
16+
workflow_dispatch:
17+
18+
permissions:
19+
contents: read
20+
21+
concurrency:
22+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
23+
cancel-in-progress: true
24+
1225
jobs:
13-
check:
14-
name: Check
15-
runs-on: ubuntu-latest
16-
steps:
17-
- uses: actions/checkout@v4
18-
- name: Skip duplicate actions
19-
uses: fkirc/skip-duplicate-actions@v5
20-
with:
21-
cancel_others: true
22-
- uses: actions/setup-java@v4
23-
with:
24-
distribution: 'zulu'
25-
java-version: 17
26-
cache: 'gradle'
27-
- uses: gradle/actions/setup-gradle@v4
28-
with:
29-
gradle-version: wrapper
30-
gradle-home-cache-cleanup: true
31-
# - run: ./gradlew spotlessCheck
32-
simple_compile:
26+
build:
27+
name: Compile + migration safety net (assembleDebug, :shared:desktopTest, strict goldens)
3328
runs-on: ubuntu-latest
29+
timeout-minutes: 30
30+
# S4d-169 (merge of origin/master): adopt master's scaffolding (paths-ignore, workflow_dispatch,
31+
# permissions, concurrency, wrapper validation, setup-android) BUT keep the migration safety net
32+
# (full tests, not master's compile-only `-x test`). The master `if: !github.event.pull_request.draft`
33+
# guard is deliberately DROPPED so Draft PR #358 still runs :shared:desktopTest + the strict watermark
34+
# goldens (the renderer pixel/dim gate the migration depends on).
3435
steps:
35-
- uses: actions/checkout@v4
36+
- name: Checkout
37+
uses: actions/checkout@v4
3638
with:
3739
fetch-depth: 0
38-
- uses: actions/setup-java@v4
40+
persist-credentials: false
41+
42+
- name: Validate Gradle Wrapper
43+
uses: gradle/wrapper-validation-action@v2
44+
45+
- name: Set up JDK 17
46+
uses: actions/setup-java@v4
3947
with:
40-
distribution: 'zulu'
41-
java-version: 17
42-
cache: 'gradle'
43-
- uses: gradle/actions/setup-gradle@v4
48+
distribution: 'temurin'
49+
java-version: '17'
50+
51+
- name: Set up Android SDK
52+
uses: android-actions/setup-android@v3
53+
54+
- name: Set up Gradle
55+
uses: gradle/actions/setup-gradle@v4
4456
with:
4557
gradle-version: wrapper
58+
cache-read-only: ${{ github.event_name == 'pull_request' }}
4659
gradle-home-cache-cleanup: true
60+
61+
- name: Make gradlew executable
62+
run: chmod +x ./gradlew
63+
4764
- name: Build apk with Gradle
4865
run: ./gradlew :app:assembleDebug
66+
4967
- name: Run :shared multiplatform tests (JVM, CMP plan C1.8)
5068
run: ./gradlew :shared:desktopTest
69+
5170
- name: Run :app unit tests incl. watermark golden (Robolectric NATIVE, CMP plan C1.7/C1.8)
5271
run: ./gradlew :app:testDebugUnitTest
72+
5373
- name: Run STRICT watermark goldens (S4b renderer pixel/dim gate; pins FNV baselines)
5474
# WATERMARK_GOLDEN_STRICT is not a Gradle-tracked input, so a warm cache could otherwise
5575
# serve the prior non-strict :app:testDebugUnitTest result and skip the strict assertions.

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,5 @@ hs_err_pid*
114114
.alma-snapshots
115115
.DS_Store
116116
.kotlin
117+
.weblate
118+
.weblate.ini

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ android {
4141
applicationId = "me.rosuh.easywatermark"
4242
minSdk = (Apps.minSdk)
4343
targetSdk = (Apps.targetSdk)
44-
versionCode = 20906
45-
versionName = "2.9.6"
44+
versionCode = 21000
45+
versionName = "2.10.0"
4646
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
4747
}
4848

app/src/main/AndroidManifest.xml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,18 @@
88
<uses-permission
99
android:name="android.permission.READ_EXTERNAL_STORAGE"
1010
android:maxSdkVersion="32" />
11+
<uses-permission android:name="android.permission.VIBRATE" />
12+
13+
<!-- Devices running Android 12L (API level 32) or lower -->
14+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
15+
16+
<!-- Devices running Android 13 (API level 33) or higher -->
1117
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
1218

19+
<!-- To handle the reselection within the app on devices running Android 14
20+
or higher if your app targets Android 14 (API level 34) or higher. -->
21+
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
22+
1323
<application
1424
android:name=".MyApp"
1525
android:allowBackup="true"
@@ -54,4 +64,4 @@
5464
</provider>
5565
</application>
5666

57-
</manifest>
67+
</manifest>

app/src/main/java/me/rosuh/easywatermark/utils/FileUtils.kt

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,33 @@ class FileUtils {
3535
}
3636

3737
fun isImage(resolver: ContentResolver, uri: Uri?): Boolean {
38-
val mimeType = getFileTypeFromUri(resolver, uri)
39-
return isImage(mimeType)
38+
return try {
39+
val mimeType = getFileTypeFromUri(resolver, uri)
40+
isImage(mimeType)
41+
} catch (e: SecurityException) {
42+
// Handle SecurityException on some Android systems (e.g., MIUI)
43+
// where ContentResolver.getType() might be restricted
44+
// Fall back to checking file extension
45+
isImageByExtension(uri)
46+
}
47+
}
48+
49+
/**
50+
* Fallback method to check if URI is an image by examining the file extension
51+
* when MIME type detection fails due to security restrictions
52+
*/
53+
private fun isImageByExtension(uri: Uri?): Boolean {
54+
if (uri == null) return false
55+
56+
val extension = MimeTypeMap.getFileExtensionFromUrl(uri.toString())?.lowercase()
57+
if (extension.isNullOrEmpty()) return false
58+
59+
val imageExtensions = setOf(
60+
"jpg", "jpeg", "png", "gif", "bmp", "webp",
61+
"tiff", "tif", "svg", "ico", "heic", "heif"
62+
)
63+
64+
return imageExtensions.contains(extension)
4065
}
4166
}
4267
}

app/src/main/java/me/rosuh/easywatermark/utils/PickImageContract.kt

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package me.rosuh.easywatermark.utils
22

3+
import android.os.Build
34
import android.view.HapticFeedbackConstants
45
import android.view.View
56

@@ -9,18 +10,30 @@ class VibrateHelper private constructor() {
910
private var cd: Long = 20L
1011

1112
fun doVibrate(view: View) {
12-
if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.M) {
13+
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
1314
return
1415
}
15-
if (System.currentTimeMillis() - latestVibration <= cd) {
16+
val now = System.currentTimeMillis()
17+
if (now - latestVibration <= cd) {
1618
return
1719
}
18-
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
20+
latestVibration = now
21+
performHapticFeedbackSafely {
22+
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
23+
}
1924
}
2025

2126
companion object {
2227
fun get(): VibrateHelper {
2328
return VibrateHelper()
2429
}
30+
31+
internal fun performHapticFeedbackSafely(performFeedback: () -> Boolean): Boolean {
32+
return try {
33+
performFeedback()
34+
} catch (_: SecurityException) {
35+
false
36+
}
37+
}
2538
}
2639
}

app/src/main/java/me/rosuh/easywatermark/utils/ktx/ContextExtension.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ fun Activity.checkReadingPermission(
3333
},
3434
grant: () -> Unit,
3535
) {
36-
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
36+
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
3737
Manifest.permission.READ_MEDIA_IMAGES
3838
} else {
3939
Manifest.permission.READ_EXTERNAL_STORAGE
4040
}
41-
preCheckStoragePermission(activityResultLauncher, permissions, failed, grant)
41+
preCheckStoragePermission(activityResultLauncher, permission, failed, grant)
4242
}
4343

4444
fun Activity.checkWritingPermission(
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package me.rosuh.easywatermark.utils.ktx
2+
3+
import android.view.View
4+
import androidx.core.view.ViewCompat
5+
import androidx.core.view.WindowInsetsCompat
6+
7+
data class InitialPadding(
8+
val left: Int,
9+
val top: Int,
10+
val right: Int,
11+
val bottom: Int
12+
)
13+
14+
fun View.doOnApplyWindowInsets(
15+
apply: (View, WindowInsetsCompat, InitialPadding) -> Unit
16+
) {
17+
val initialPadding = InitialPadding(paddingLeft, paddingTop, paddingRight, paddingBottom)
18+
ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
19+
apply(view, insets, initialPadding)
20+
WindowInsetsCompat.CONSUMED
21+
}
22+
if (isAttachedToWindow) {
23+
ViewCompat.requestApplyInsets(this)
24+
} else {
25+
addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
26+
override fun onViewAttachedToWindow(v: View) {
27+
v.removeOnAttachStateChangeListener(this)
28+
ViewCompat.requestApplyInsets(v)
29+
}
30+
31+
override fun onViewDetachedFromWindow(v: View) = Unit
32+
})
33+
}
34+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources>
3+
<string name="app_name">எளிதான வாட்டர்மார்க்</string>
4+
<string name="action_pick">படத்தை தேர்வு செய்யவும்</string>
5+
<string name="action_save">சேமிக்கவும்</string>
6+
<string name="action_settings">பற்றி</string>
7+
<string name="title_layout">மனையமைவு</string>
8+
<string name="title_style">சூல் தண்டு</string>
9+
<string name="title_content">உள்ளடக்கம்</string>
10+
<string name="tips_do_not_choose_image">படம் எதுவும் தேர்ந்தெடுக்கப்படவில்லை</string>
11+
<string name="tips_choose_color_dialog">வண்ணத்தைத் தேர்ந்தெடுங்கள்</string>
12+
<string name="tips_confirm_dialog">உறுதிப்படுத்தவும்</string>
13+
<string name="tips_cancel_dialog">ரத்துசெய்</string>
14+
<string name="tips_error">பிழை</string>
15+
<string name="title_text_style">சூல் தண்டு</string>
16+
<string name="title_text_size">அளவு</string>
17+
<string name="title_tile_mode">டைல்மோடு</string>
18+
<string name="title_horizon_layout">கிடைமட்ட</string>
19+
<string name="title_vertical_layout">செங்குத்து</string>
20+
<string name="title_text_color">நிறம்</string>
21+
<string name="title_text_rotate">கோணம்</string>
22+
<string name="request_permission_failed">இசைவு பெற முடியவில்லை</string>
23+
<string name="about_title_about">பற்றி</string>
24+
<string name="about_title_info">தகவல்</string>
25+
<string name="about_title_output">வெளியீட்டு வடிவம்</string>
26+
<string name="about_title_version">பதிப்பு</string>
27+
<string name="about_title_rating">எங்கள் பயன்பாட்டை மதிப்பிடவும்</string>
28+
<string name="about_title_feed_back">பின்னூட்டம்</string>
29+
<string name="about_title_update_log">சேஞ்ச்லாக்</string>
30+
<string name="about_title_open_source">திறந்த மூல உரிமங்கள்</string>
31+
<string name="error_not_img">தயவுசெய்து ஒரு படத்தைத் தேர்ந்தெடுக்கவும்</string>
32+
<string name="error_file_not_found">படத்தை திறக்க முடியவில்லை அல்லது தேர்ந்தெடுக்கப்படவில்லை</string>
33+
<string name="dialog_export_to_gallery">ஆல்பத்திற்கு ஏற்றுமதி செய்யவும்</string>
34+
<string name="share">பகிரவும்</string>
35+
<string name="water_mark_mode_text">உரை</string>
36+
<string name="water_mark_mode_image">படவுரு</string>
37+
<string name="tips_ok">உறுதிப்படுத்தவும்</string>
38+
<string name="tips_cancel_output">ரத்து செய்</string>
39+
<string name="tips_need_compress_img">போதுமான சேமிப்பிடம் இல்லை அல்லது படம் பெரிதாக உள்ளது</string>
40+
<string name="tips_compressing">சுருக்குகிறது…</string>
41+
<string name="tips_compress_images">படத்தை சுருக்கவும்</string>
42+
<string name="tips_compress_ok">அமுக்கி முடிந்தது. ஏற்றுமதியைத் தொடர சரி என்பதைக் சொடுக்கு செய்யவும்.</string>
43+
<string name="tips_compress_create_uri_failed">சுருக்கி முடிந்தது, ஆனால் யூரியை உருவாக்க முடியவில்லை. பயன்பாட்டை மறுதொடக்கம் செய்து மீண்டும் முயற்சிக்கவும். %1$s</string>
44+
<string name="tips_tip_title">விபத்து கண்டறியப்பட்டது</string>
45+
<string name="msg_crash">இந்த செயலிழப்பைப் புகாரளிக்க விரும்புகிறீர்களா? இது பயன்பாட்டை மேம்படுத்த எங்களுக்கு உதவும்! இது எந்த தனிப்பட்ட தகவலையும் சேகரிக்காது.</string>
46+
<string name="crash_mail">மின்னஞ்சல் அனுப்பவும்</string>
47+
<string name="tip_not_mail_found">மின்னஞ்சல் வாங்கி கண்டறியப்படவில்லை. மாற்றாக, நீங்கள் ஆப் கடை அல்லது GitHub இல் பிழை செய்தியை ஒட்டலாம்.</string>
48+
<string name="config_default_water_mark_text">👋 மறுபகிர்வு செய்ய வேண்டாம்</string>
49+
<string name="style_alpha">அகர</string>
50+
<string name="tips_not_app_can_open_images">கோப்பு மேலாளர் கண்டறியப்படவில்லை.</string>
51+
<string name="tips_choose_other_file_type">தவறான படம்</string>
52+
<string name="error_save_oom">போதுமான நினைவகம் இல்லை, பின்புலத்தை தூய்மை செய்து மீண்டும் முயற்சிக்கவும்</string>
53+
<string name="dev_comment">எந்த போதுமான மேம்பட்ட தொழில்நுட்பமும் மந்திரத்திலிருந்து பிரித்தறிய முடியாதது.</string>
54+
<string name="dialog_title_exist_confirm">எச்சரிக்கை</string>
55+
<string name="dialog_cancel_exist_confirm">ரத்துசெய்</string>
56+
<string name="dialog_content_exist_confirm">மாற்றங்களை நிராகரிக்கவா?</string>
57+
<string name="text_paint_fill">நிரப்பவும்</string>
58+
<string name="text_paint_stroke">பக்கவாதம்</string>
59+
<string name="text_typeface_normal">இயல்பானது</string>
60+
<string name="text_typeface_bold">தடிமான</string>
61+
<string name="text_typeface_italic">சாய்வு</string>
62+
<string name="text_typeface_bold_italic">தடித்த சாய்வு</string>
63+
<string name="dialog_save_export_list_title">ஏற்றுமதி பட்டியல்(%1$s)</string>
64+
<string name="dialog_open_in_gallery">கேலரியில் பார்க்கவும்</string>
65+
<string name="dialog_save_config_format">வடிவம்</string>
66+
<string name="dialog_save_config_quality">தகுதி</string>
67+
<string name="dialog_save_exporting">ஏற்றுமதி செய்கிறது</string>
68+
<string name="tips_pick_image">படங்களை தேர்வு செய்யவும்</string>
69+
<string name="pick_via_system">மேலும்…</string>
70+
<string name="recovery_title">மீட்பு முறை</string>
71+
<string name="copy_success">நகல் செய்</string>
72+
<string name="copy_failed">நகலெடுக்க முடியவில்லை</string>
73+
<string name="store_not_found">கடை எதுவும் நிறுவப்படவில்லையா?</string>
74+
<string name="recovery_mode_closed">இது மூடப்பட்டது, மீண்டும் முயற்சிக்கவும்</string>
75+
<string name="copy">பிழை செய்தியை நகலெடுக்கவும்</string>
76+
<string name="recovery_mode_tips">பல செயலிழப்புகள் காரணமாக மீட்பு பயன்முறையில் நுழைந்தது. பின்வரும் முறைகள் மூலம் சாதனம் மற்றும் செயலிழப்பு தகவலை கீழே அனுப்புவது சிக்கலை தீர்க்கலாம். நன்றி.</string>
77+
<string name="turn_off_recovery_mode">மீட்பு பயன்முறையை முடக்கு</string>
78+
<string name="tips_input_text_can_not_be_empty">உள்ளடக்கம் காலியாக இருக்கக்கூடாது</string>
79+
<string name="back">பின்</string>
80+
<string name="dialog_title_template_edit">டெம்ப்ளேட்டைத் திருத்து</string>
81+
<string name="dialog_title_edit_watermark">வாட்டர்மார்க் திருத்தவும்</string>
82+
<string name="dialog_title_template_title">டெம்ப்ளேட் பட்டியல்</string>
83+
<string name="tips_list_empty">வெறுமையாகத் தெரிகிறது</string>
84+
<string name="dialog_button_add_template">கூட்டு</string>
85+
<string name="tips_use_this_template">இந்த டெம்ப்ளேட்டைப் பயன்படுத்தவா?</string>
86+
<string name="tips_delete_template">உள்ளீட்டை நீக்கவா?</string>
87+
<string name="tips_database_init_error">தரவுத்தள துவக்கம் தோல்வியடைந்தது.</string>
88+
<string name="tile_mode_title_repeat">மீண்டும் செய்யவும்</string>
89+
<string name="tile_mode_title_decal">ஒற்றை</string>
90+
</resources>

0 commit comments

Comments
 (0)