diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 9cd7039..cc0ee13 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -38,10 +38,7 @@ android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme" /> - - - - + @@ -59,6 +56,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + result.success(openFolder(call.argument("path") ?: "")) + "setAppIcon" -> result.success(setAppIcon(call.argument("id") ?: "")) "keepAlive" -> { keepAlive(call.argument("text") ?: "", call.argument("progress") ?: -1) result.success(null) @@ -238,6 +239,23 @@ class MainActivity : FlutterActivity() { } /// 폴더를 파일 앱으로 연다 (내장 저장소 안의 폴더만). 열 앱이 없으면 false. + /// 앱 아이콘 바꾸기: 고른 아이콘의 activity-alias 만 켜고 나머지는 끈다 (앱은 계속 켜 둔 채로) + private fun setAppIcon(id: String): Boolean { + val ids = listOf("yellow", "black", "film_jj", "film", "blue") + if (id !in ids) return false + val pm = packageManager + for (i in ids) { + val name = "$packageName.Icon" + i.split('_').joinToString("") { it.replaceFirstChar(Char::uppercaseChar) } + val state = if (i == id) PackageManager.COMPONENT_ENABLED_STATE_ENABLED + else PackageManager.COMPONENT_ENABLED_STATE_DISABLED + val cn = android.content.ComponentName(this, name) + if (pm.getComponentEnabledSetting(cn) != state) { + pm.setComponentEnabledSetting(cn, state, PackageManager.DONT_KILL_APP) + } + } + return true + } + private fun openFolder(path: String): Boolean { // 내장 저장소 (primary) 또는 SD 카드 · USB (/storage/XXXX-XXXX → 문서 ID "XXXX-XXXX:...") val root = Environment.getExternalStorageDirectory().path diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 80bcc6f..1b29709 100644 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,6 +1,6 @@ - - - + + + diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_black.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_black.xml new file mode 100644 index 0000000..c248192 --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_black.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_blue.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_blue.xml new file mode 100644 index 0000000..4923826 --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_blue.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_film.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_film.xml new file mode 100644 index 0000000..2459be6 --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_film.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_film_jj.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_film_jj.xml new file mode 100644 index 0000000..a44cc66 --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_film_jj.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_yellow.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_yellow.xml new file mode 100644 index 0000000..641ca40 --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_yellow.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index ea4a8ea..6dba372 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_bg.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_bg.png new file mode 100644 index 0000000..7163774 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_bg.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_black.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_black.png new file mode 100644 index 0000000..ea4a8ea Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_black.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_black_bg.png similarity index 100% rename from android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png rename to android/app/src/main/res/mipmap-hdpi/ic_launcher_black_bg.png diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_black_fg.png similarity index 100% rename from android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png rename to android/app/src/main/res/mipmap-hdpi/ic_launcher_black_fg.png diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_blue.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_blue.png new file mode 100644 index 0000000..e729bfd Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_blue.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_blue_bg.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_blue_bg.png new file mode 100644 index 0000000..fa8c0b6 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_blue_bg.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_blue_fg.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_blue_fg.png new file mode 100644 index 0000000..5a08bc1 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_blue_fg.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_fg.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_fg.png new file mode 100644 index 0000000..223f891 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_fg.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_film.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_film.png new file mode 100644 index 0000000..6d58d70 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_film.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_film_bg.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_film_bg.png new file mode 100644 index 0000000..73c4bae Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_film_bg.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_film_fg.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_film_fg.png new file mode 100644 index 0000000..29486f4 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_film_fg.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_film_jj.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_film_jj.png new file mode 100644 index 0000000..f9aab88 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_film_jj.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_film_jj_bg.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_film_jj_bg.png new file mode 100644 index 0000000..73c4bae Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_film_jj_bg.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_film_jj_fg.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_film_jj_fg.png new file mode 100644 index 0000000..b372574 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_film_jj_fg.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_yellow.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_yellow.png new file mode 100644 index 0000000..6dba372 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_yellow.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_yellow_bg.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_yellow_bg.png new file mode 100644 index 0000000..7163774 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_yellow_bg.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_yellow_fg.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_yellow_fg.png new file mode 100644 index 0000000..223f891 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_yellow_fg.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 2b51c3c..4f46e65 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_bg.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_bg.png new file mode 100644 index 0000000..65d0435 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_bg.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_black.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_black.png new file mode 100644 index 0000000..2b51c3c Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_black.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_black_bg.png similarity index 100% rename from android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png rename to android/app/src/main/res/mipmap-mdpi/ic_launcher_black_bg.png diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_black_fg.png similarity index 100% rename from android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png rename to android/app/src/main/res/mipmap-mdpi/ic_launcher_black_fg.png diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_blue.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_blue.png new file mode 100644 index 0000000..d762181 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_blue.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_blue_bg.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_blue_bg.png new file mode 100644 index 0000000..ec41826 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_blue_bg.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_blue_fg.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_blue_fg.png new file mode 100644 index 0000000..59fa089 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_blue_fg.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_fg.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_fg.png new file mode 100644 index 0000000..2cbad15 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_fg.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_film.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_film.png new file mode 100644 index 0000000..787c32d Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_film.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_film_bg.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_film_bg.png new file mode 100644 index 0000000..6f5572e Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_film_bg.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_film_fg.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_film_fg.png new file mode 100644 index 0000000..6986123 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_film_fg.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_film_jj.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_film_jj.png new file mode 100644 index 0000000..9acf839 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_film_jj.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_film_jj_bg.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_film_jj_bg.png new file mode 100644 index 0000000..6f5572e Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_film_jj_bg.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_film_jj_fg.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_film_jj_fg.png new file mode 100644 index 0000000..45fa2e8 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_film_jj_fg.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_yellow.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_yellow.png new file mode 100644 index 0000000..4f46e65 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_yellow.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_yellow_bg.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_yellow_bg.png new file mode 100644 index 0000000..65d0435 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_yellow_bg.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_yellow_fg.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_yellow_fg.png new file mode 100644 index 0000000..2cbad15 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_yellow_fg.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index efcd467..c5f76ea 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_bg.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_bg.png new file mode 100644 index 0000000..d496804 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_bg.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_black.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_black.png new file mode 100644 index 0000000..efcd467 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_black.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_black_bg.png similarity index 100% rename from android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png rename to android/app/src/main/res/mipmap-xhdpi/ic_launcher_black_bg.png diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_black_fg.png similarity index 100% rename from android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png rename to android/app/src/main/res/mipmap-xhdpi/ic_launcher_black_fg.png diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_blue.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_blue.png new file mode 100644 index 0000000..d63904c Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_blue.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_blue_bg.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_blue_bg.png new file mode 100644 index 0000000..eb5b9cd Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_blue_bg.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_blue_fg.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_blue_fg.png new file mode 100644 index 0000000..9bd0ce6 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_blue_fg.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_fg.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_fg.png new file mode 100644 index 0000000..7664f5c Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_fg.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film.png new file mode 100644 index 0000000..feb6ab2 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film_bg.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film_bg.png new file mode 100644 index 0000000..1c511f4 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film_bg.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film_fg.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film_fg.png new file mode 100644 index 0000000..798d930 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film_fg.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film_jj.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film_jj.png new file mode 100644 index 0000000..2580f9b Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film_jj.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film_jj_bg.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film_jj_bg.png new file mode 100644 index 0000000..1c511f4 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film_jj_bg.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film_jj_fg.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film_jj_fg.png new file mode 100644 index 0000000..ec0eaea Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_film_jj_fg.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_yellow.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_yellow.png new file mode 100644 index 0000000..c5f76ea Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_yellow.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_yellow_bg.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_yellow_bg.png new file mode 100644 index 0000000..d496804 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_yellow_bg.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_yellow_fg.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_yellow_fg.png new file mode 100644 index 0000000..7664f5c Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_yellow_fg.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 97fe7b8..42612d7 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_bg.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_bg.png new file mode 100644 index 0000000..2be7342 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_bg.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_black.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_black.png new file mode 100644 index 0000000..97fe7b8 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_black.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_black_bg.png similarity index 100% rename from android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png rename to android/app/src/main/res/mipmap-xxhdpi/ic_launcher_black_bg.png diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_black_fg.png similarity index 100% rename from android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png rename to android/app/src/main/res/mipmap-xxhdpi/ic_launcher_black_fg.png diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_blue.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_blue.png new file mode 100644 index 0000000..001cddc Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_blue.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_blue_bg.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_blue_bg.png new file mode 100644 index 0000000..5739926 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_blue_bg.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_blue_fg.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_blue_fg.png new file mode 100644 index 0000000..7de2d7c Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_blue_fg.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_fg.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_fg.png new file mode 100644 index 0000000..6c7ea89 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_fg.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film.png new file mode 100644 index 0000000..cccf608 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film_bg.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film_bg.png new file mode 100644 index 0000000..23fb1aa Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film_bg.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film_fg.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film_fg.png new file mode 100644 index 0000000..e7dfd5f Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film_fg.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film_jj.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film_jj.png new file mode 100644 index 0000000..dd359e2 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film_jj.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film_jj_bg.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film_jj_bg.png new file mode 100644 index 0000000..23fb1aa Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film_jj_bg.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film_jj_fg.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film_jj_fg.png new file mode 100644 index 0000000..637732a Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_film_jj_fg.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_yellow.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_yellow.png new file mode 100644 index 0000000..42612d7 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_yellow.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_yellow_bg.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_yellow_bg.png new file mode 100644 index 0000000..2be7342 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_yellow_bg.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_yellow_fg.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_yellow_fg.png new file mode 100644 index 0000000..6c7ea89 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_yellow_fg.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index d5cca78..2237f0e 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bg.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bg.png new file mode 100644 index 0000000..4078cf2 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bg.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_black.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_black.png new file mode 100644 index 0000000..d5cca78 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_black.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_black_bg.png similarity index 100% rename from android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png rename to android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_black_bg.png diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_black_fg.png similarity index 100% rename from android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png rename to android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_black_fg.png diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_blue.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_blue.png new file mode 100644 index 0000000..e20fcdb Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_blue.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_blue_bg.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_blue_bg.png new file mode 100644 index 0000000..a469791 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_blue_bg.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_blue_fg.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_blue_fg.png new file mode 100644 index 0000000..84e0f2a Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_blue_fg.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_fg.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_fg.png new file mode 100644 index 0000000..bf27060 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_fg.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film.png new file mode 100644 index 0000000..9cd3c42 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film_bg.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film_bg.png new file mode 100644 index 0000000..3e84ed9 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film_bg.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film_fg.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film_fg.png new file mode 100644 index 0000000..38c1132 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film_fg.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film_jj.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film_jj.png new file mode 100644 index 0000000..d1e7081 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film_jj.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film_jj_bg.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film_jj_bg.png new file mode 100644 index 0000000..3e84ed9 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film_jj_bg.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film_jj_fg.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film_jj_fg.png new file mode 100644 index 0000000..58beb90 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_film_jj_fg.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_yellow.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_yellow.png new file mode 100644 index 0000000..2237f0e Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_yellow.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_yellow_bg.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_yellow_bg.png new file mode 100644 index 0000000..4078cf2 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_yellow_bg.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_yellow_fg.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_yellow_fg.png new file mode 100644 index 0000000..bf27060 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_yellow_fg.png differ diff --git a/assets/icon/app_button_256.png b/assets/icon/app_button_256.png deleted file mode 100644 index 02337e6..0000000 Binary files a/assets/icon/app_button_256.png and /dev/null differ diff --git a/assets/icon/app_icon_1024.png b/assets/icon/app_icon_1024.png index 9606816..8f5f4ef 100644 Binary files a/assets/icon/app_icon_1024.png and b/assets/icon/app_icon_1024.png differ diff --git a/assets/icon/app_icon_small_256.png b/assets/icon/app_icon_small_256.png deleted file mode 100644 index 855eb37..0000000 Binary files a/assets/icon/app_icon_small_256.png and /dev/null differ diff --git a/assets/icon/variants/black.ico b/assets/icon/variants/black.ico new file mode 100644 index 0000000..9ea798c Binary files /dev/null and b/assets/icon/variants/black.ico differ diff --git a/assets/icon/app_icon_256.png b/assets/icon/variants/black_256.png similarity index 100% rename from assets/icon/app_icon_256.png rename to assets/icon/variants/black_256.png diff --git a/assets/icon/variants/black_button.png b/assets/icon/variants/black_button.png new file mode 100644 index 0000000..61f09cb Binary files /dev/null and b/assets/icon/variants/black_button.png differ diff --git a/assets/icon/variants/blue.ico b/assets/icon/variants/blue.ico new file mode 100644 index 0000000..31138fa Binary files /dev/null and b/assets/icon/variants/blue.ico differ diff --git a/assets/icon/variants/blue_256.png b/assets/icon/variants/blue_256.png new file mode 100644 index 0000000..22d51db Binary files /dev/null and b/assets/icon/variants/blue_256.png differ diff --git a/assets/icon/variants/blue_button.png b/assets/icon/variants/blue_button.png new file mode 100644 index 0000000..bf9db7c Binary files /dev/null and b/assets/icon/variants/blue_button.png differ diff --git a/assets/icon/variants/film.ico b/assets/icon/variants/film.ico new file mode 100644 index 0000000..0a8ae94 Binary files /dev/null and b/assets/icon/variants/film.ico differ diff --git a/assets/icon/variants/film_256.png b/assets/icon/variants/film_256.png new file mode 100644 index 0000000..eda60ab Binary files /dev/null and b/assets/icon/variants/film_256.png differ diff --git a/assets/icon/variants/film_button.png b/assets/icon/variants/film_button.png new file mode 100644 index 0000000..4069e11 Binary files /dev/null and b/assets/icon/variants/film_button.png differ diff --git a/assets/icon/variants/film_jj.ico b/assets/icon/variants/film_jj.ico new file mode 100644 index 0000000..b195933 Binary files /dev/null and b/assets/icon/variants/film_jj.ico differ diff --git a/assets/icon/variants/film_jj_256.png b/assets/icon/variants/film_jj_256.png new file mode 100644 index 0000000..7d97c95 Binary files /dev/null and b/assets/icon/variants/film_jj_256.png differ diff --git a/assets/icon/variants/film_jj_button.png b/assets/icon/variants/film_jj_button.png new file mode 100644 index 0000000..4069e11 Binary files /dev/null and b/assets/icon/variants/film_jj_button.png differ diff --git a/assets/icon/variants/yellow.ico b/assets/icon/variants/yellow.ico new file mode 100644 index 0000000..51f68b7 Binary files /dev/null and b/assets/icon/variants/yellow.ico differ diff --git a/assets/icon/variants/yellow_256.png b/assets/icon/variants/yellow_256.png new file mode 100644 index 0000000..43fe91a Binary files /dev/null and b/assets/icon/variants/yellow_256.png differ diff --git a/assets/icon/variants/yellow_button.png b/assets/icon/variants/yellow_button.png new file mode 100644 index 0000000..6583381 Binary files /dev/null and b/assets/icon/variants/yellow_button.png differ diff --git a/assets/l10n/en.json b/assets/l10n/en.json index 31d5955..79475bc 100644 --- a/assets/l10n/en.json +++ b/assets/l10n/en.json @@ -201,6 +201,7 @@ "건너뜀 (출력 폴더의 파일): {0}": "Skipped (file in the output folder): {0}", "검색": "Search", "검색 지우기": "Clear search", + "검정": "Black", "결과 폴더": "Output folder", "결과 폴더 열기 (동영상을 고르세요)": "Open output folder (choose a video)", "결과 폴더 열기: {0}": "Open output folder: {0}", @@ -217,6 +218,7 @@ "글자 · 버튼 크기입니다. 위쪽 막대의 − · + 로 그때그때 바꿀 수 있고, 가운데 숫자를 누르면 이 크기로 돌아갑니다 (지금 {0}%)": "Text · button size. Change it any time with − · + on the top bar; press the number in the middle to return to this size (now {0}%)", "기계 번역": "Machine translation", "기기": "device", + "기본": "Default", "기본 연결 프로그램": "Default app", "기본 크기 ({0}%) 로 · 기본 크기는 환경 설정 > 화면 에서": "Back to the default size ({0}%) · set the default size in Settings > Display", "기본 프로그램": "Default program", @@ -259,6 +261,7 @@ "내장 플레이어": "Built-in player", "네덜란드어": "Dutch", "네팔어": "Nepali", + "노랑": "Yellow", "노르웨이어": "Norwegian", "누르고 있으면 조정 전": "Hold to see before", "다 받는 대로 MKV 만들기의 동영상 목록에 넣고, 다운로드 목록에서는 뺍니다 (다운로드 화면의 체크 상자와 같음)": "As soon as they finish, put them in the Make MKV video list and remove them from the download list (same as the checkbox on the download screen)", @@ -485,6 +488,8 @@ "압축 파일 안에 없습니다": "Not in the archive", "압축 파일 안에 프로그램이 없습니다.": "The program is not in the archive.", "압축을 풀 수 없습니다: {0}": "Cannot extract the archive: {0}", + "앱 목록 · 홈 화면과 앱 안 왼쪽 위 버튼의 아이콘입니다. 바꾸면 홈 화면에 둔 아이콘이 없어질 수 있어 앱 목록에서 다시 끌어다 놓아야 할 수 있습니다": "Icon in the app list, home screen and the top-left button in the app. After changing it, the icon on your home screen may disappear and you may need to drag it from the app list again", + "앱 아이콘": "App icon", "앱 안 브라우저 (권장)": "In-app browser (recommended)", "앱 안 브라우저의 로그인 사용 (브라우저 화면에서 YouTube 에 로그인하세요)": "Use the in-app browser's sign-in (sign in to YouTube on the browser screen)", "앱 안 웹 브라우저: [건너뛰기] 를 대신 누르고, 건너뛸 수 없는 광고는 소리를 끄고 빨리 넘깁니다 (넘기는 동안 화면 위쪽에 \"광고 건너뛰는 중…\")": "In-app web browser: presses [Skip] for you, and mutes and fast-forwards ads that can't be skipped (\"Skipping ad…\" shows at the top while skipping)", @@ -744,6 +749,7 @@ "직접 입력…": "Custom…", "진행 중인 작업 없음": "No jobs in progress", "진행 중인 작업이 있습니다: {0}\n중지하고 끝낼까요?": "A job is in progress: {0}\nStop it and quit?", + "창 · 작업 표시줄 · 트레이와 앱 안 왼쪽 위 버튼의 아이콘입니다 (실행 파일 · 바탕 화면 바로 가기 아이콘은 그대로)": "Icon of the window, taskbar, tray and the top-left button in the app (the program file and desktop shortcut icons stay the same)", "창 보이기 / 숨기기 단축키": "Hotkey to show / hide the window", "창 숨김 → 프로세스 종료": "Window hidden → process exit", "창 종료": "Close window", @@ -752,6 +758,7 @@ "채도": "Saturation", "채우기 (가운데 잘라냄)": "Fill (crop the center)", "처음 사용 시 내려받기 {0}": "Downloads {0} on first use", + "처음 아이콘 (파랑)": "Original icon (blue)", "첫 줄 추가": "Add first line", "청각장애인용": "Hearing impaired", "체코어": "Czech", @@ -829,6 +836,8 @@ "프로그램의 모든 글자를 고른 언어로 보여 줍니다. 목록에 없는 언어는 [언어 추가] 로 이 기기의 AI 번역 모델이 자동 번역해 넣습니다.": "Shows all of the program's text in the chosen language. Languages not in the list can be added with [Add language]; this device's AI translation model translates them automatically.", "프로그램이 이미 켜져 있을 때. 새 창은 재생만 하는 창이며 닫으면 그 창만 끝납니다": "When the program is already running. The new window only plays, and closing it ends only that window", "핀란드어": "Finnish", + "필름": "Film", + "필름 + JJ": "Film + JJ", "필수 프로그램 설치": "Install required programs", "필수 프로그램 점검": "Check required programs", "필수 프로그램이 모두 설치되어 있습니다.": "All required programs are installed.", diff --git a/assets/l10n/ja.json b/assets/l10n/ja.json index 70e0cad..daac76f 100644 --- a/assets/l10n/ja.json +++ b/assets/l10n/ja.json @@ -201,6 +201,7 @@ "건너뜀 (출력 폴더의 파일): {0}": "スキップ(出力フォルダーのファイル): {0}", "검색": "検索", "검색 지우기": "検索をクリア", + "검정": "黒", "결과 폴더": "出力フォルダー", "결과 폴더 열기 (동영상을 고르세요)": "出力フォルダーを開く(動画を選んでください)", "결과 폴더 열기: {0}": "出力フォルダーを開く: {0}", @@ -217,6 +218,7 @@ "글자 · 버튼 크기입니다. 위쪽 막대의 − · + 로 그때그때 바꿀 수 있고, 가운데 숫자를 누르면 이 크기로 돌아갑니다 (지금 {0}%)": "文字・ボタンの大きさです。上部バーの − ・ + でその都度変更でき、中央の数字を押すとこの大きさに戻ります(現在 {0}%)", "기계 번역": "機械翻訳", "기기": "端末", + "기본": "既定", "기본 연결 프로그램": "既定のアプリ", "기본 크기 ({0}%) 로 · 기본 크기는 환경 설정 > 화면 에서": "既定の大きさ({0}%)に戻す・既定の大きさは 環境設定 > 画面 で", "기본 프로그램": "既定のプログラム", @@ -259,6 +261,7 @@ "내장 플레이어": "内蔵プレーヤー", "네덜란드어": "オランダ語", "네팔어": "ネパール語", + "노랑": "黄色", "노르웨이어": "ノルウェー語", "누르고 있으면 조정 전": "押している間は調整前", "다 받는 대로 MKV 만들기의 동영상 목록에 넣고, 다운로드 목록에서는 뺍니다 (다운로드 화면의 체크 상자와 같음)": "ダウンロードが終わり次第 MKV 作成の動画リストに入れ、ダウンロードリストからは外します(ダウンロード画面のチェックボックスと同じ)", @@ -485,6 +488,8 @@ "압축 파일 안에 없습니다": "圧縮ファイル内にありません", "압축 파일 안에 프로그램이 없습니다.": "圧縮ファイル内にプログラムがありません。", "압축을 풀 수 없습니다: {0}": "圧縮ファイルを展開できません: {0}", + "앱 목록 · 홈 화면과 앱 안 왼쪽 위 버튼의 아이콘입니다. 바꾸면 홈 화면에 둔 아이콘이 없어질 수 있어 앱 목록에서 다시 끌어다 놓아야 할 수 있습니다": "アプリ一覧・ホーム画面とアプリ内左上のボタンのアイコンです。変更するとホーム画面のアイコンが消えることがあり、アプリ一覧から再度ドラッグする必要がある場合があります", + "앱 아이콘": "アプリアイコン", "앱 안 브라우저 (권장)": "アプリ内ブラウザー(推奨)", "앱 안 브라우저의 로그인 사용 (브라우저 화면에서 YouTube 에 로그인하세요)": "アプリ内ブラウザーのログインを使う(ブラウザー画面で YouTube にログインしてください)", "앱 안 웹 브라우저: [건너뛰기] 를 대신 누르고, 건너뛸 수 없는 광고는 소리를 끄고 빨리 넘깁니다 (넘기는 동안 화면 위쪽에 \"광고 건너뛰는 중…\")": "アプリ内ウェブブラウザー:[スキップ]を代わりに押し、スキップできない広告は音を消して早送りします(スキップ中は画面上部に「広告をスキップ中…」)", @@ -744,6 +749,7 @@ "직접 입력…": "直接入力…", "진행 중인 작업 없음": "進行中の作業なし", "진행 중인 작업이 있습니다: {0}\n중지하고 끝낼까요?": "進行中の作業があります: {0}\n中止して終了しますか?", + "창 · 작업 표시줄 · 트레이와 앱 안 왼쪽 위 버튼의 아이콘입니다 (실행 파일 · 바탕 화면 바로 가기 아이콘은 그대로)": "ウィンドウ・タスクバー・トレイとアプリ内左上のボタンのアイコンです(実行ファイル・デスクトップのショートカットのアイコンはそのまま)", "창 보이기 / 숨기기 단축키": "ウィンドウ表示 / 非表示のショートカット", "창 숨김 → 프로세스 종료": "ウィンドウ非表示 → プロセス終了", "창 종료": "ウィンドウを閉じる", @@ -752,6 +758,7 @@ "채도": "彩度", "채우기 (가운데 잘라냄)": "フィル(中央を切り抜き)", "처음 사용 시 내려받기 {0}": "初回使用時にダウンロード {0}", + "처음 아이콘 (파랑)": "最初のアイコン(青)", "첫 줄 추가": "最初の行を追加", "청각장애인용": "聴覚障害者用", "체코어": "チェコ語", @@ -829,6 +836,8 @@ "프로그램의 모든 글자를 고른 언어로 보여 줍니다. 목록에 없는 언어는 [언어 추가] 로 이 기기의 AI 번역 모델이 자동 번역해 넣습니다.": "プログラムのすべての文字を選んだ言語で表示します。リストにない言語は[言語を追加]で、この端末の AI 翻訳モデルが自動翻訳して追加します。", "프로그램이 이미 켜져 있을 때. 새 창은 재생만 하는 창이며 닫으면 그 창만 끝납니다": "プログラムが既に起動しているとき。新しいウィンドウは再生専用で、閉じるとそのウィンドウだけ終了します", "핀란드어": "フィンランド語", + "필름": "フィルム", + "필름 + JJ": "フィルム + JJ", "필수 프로그램 설치": "必須プログラムをインストール", "필수 프로그램 점검": "必須プログラムの点検", "필수 프로그램이 모두 설치되어 있습니다.": "必須プログラムはすべてインストールされています。", diff --git a/assets/l10n/zh-Hans.json b/assets/l10n/zh-Hans.json index c398f80..030f5b1 100644 --- a/assets/l10n/zh-Hans.json +++ b/assets/l10n/zh-Hans.json @@ -201,6 +201,7 @@ "건너뜀 (출력 폴더의 파일): {0}": "已跳过(输出文件夹中的文件): {0}", "검색": "搜索", "검색 지우기": "清除搜索", + "검정": "黑色", "결과 폴더": "输出文件夹", "결과 폴더 열기 (동영상을 고르세요)": "打开输出文件夹(请选择视频)", "결과 폴더 열기: {0}": "打开输出文件夹: {0}", @@ -217,6 +218,7 @@ "글자 · 버튼 크기입니다. 위쪽 막대의 − · + 로 그때그때 바꿀 수 있고, 가운데 숫자를 누르면 이 크기로 돌아갑니다 (지금 {0}%)": "文字 · 按钮大小。可随时用顶部栏的 − · + 调整,点击中间的数字恢复为此大小(当前 {0}%)", "기계 번역": "机器翻译", "기기": "设备", + "기본": "默认", "기본 연결 프로그램": "默认打开方式", "기본 크기 ({0}%) 로 · 기본 크기는 환경 설정 > 화면 에서": "恢复默认大小({0}%)· 默认大小在 设置 > 显示 中设置", "기본 프로그램": "默认程序", @@ -259,6 +261,7 @@ "내장 플레이어": "内置播放器", "네덜란드어": "荷兰语", "네팔어": "尼泊尔语", + "노랑": "黄色", "노르웨이어": "挪威语", "누르고 있으면 조정 전": "按住查看调整前", "다 받는 대로 MKV 만들기의 동영상 목록에 넣고, 다운로드 목록에서는 뺍니다 (다운로드 화면의 체크 상자와 같음)": "下载完成后立即放入制作 MKV 的视频列表,并从下载列表移除(与下载界面的复选框相同)", @@ -485,6 +488,8 @@ "압축 파일 안에 없습니다": "压缩文件中没有", "압축 파일 안에 프로그램이 없습니다.": "压缩文件中没有程序。", "압축을 풀 수 없습니다: {0}": "无法解压: {0}", + "앱 목록 · 홈 화면과 앱 안 왼쪽 위 버튼의 아이콘입니다. 바꾸면 홈 화면에 둔 아이콘이 없어질 수 있어 앱 목록에서 다시 끌어다 놓아야 할 수 있습니다": "应用列表、主屏幕以及应用内左上角按钮的图标。更改后主屏幕上的图标可能会消失,可能需要从应用列表重新拖放", + "앱 아이콘": "应用图标", "앱 안 브라우저 (권장)": "应用内浏览器(推荐)", "앱 안 브라우저의 로그인 사용 (브라우저 화면에서 YouTube 에 로그인하세요)": "使用应用内浏览器的登录(请在浏览器界面登录 YouTube)", "앱 안 웹 브라우저: [건너뛰기] 를 대신 누르고, 건너뛸 수 없는 광고는 소리를 끄고 빨리 넘깁니다 (넘기는 동안 화면 위쪽에 \"광고 건너뛰는 중…\")": "应用内网页浏览器: 代为点击[跳过],无法跳过的广告会静音并快进(跳过时屏幕顶部显示“正在跳过广告…”)", @@ -744,6 +749,7 @@ "직접 입력…": "手动输入…", "진행 중인 작업 없음": "没有进行中的任务", "진행 중인 작업이 있습니다: {0}\n중지하고 끝낼까요?": "有正在进行的任务: {0}\n要停止并退出吗?", + "창 · 작업 표시줄 · 트레이와 앱 안 왼쪽 위 버튼의 아이콘입니다 (실행 파일 · 바탕 화면 바로 가기 아이콘은 그대로)": "窗口、任务栏、托盘以及应用内左上角按钮的图标(程序文件和桌面快捷方式的图标不变)", "창 보이기 / 숨기기 단축키": "显示 / 隐藏窗口的快捷键", "창 숨김 → 프로세스 종료": "窗口隐藏 → 进程退出", "창 종료": "关闭窗口", @@ -752,6 +758,7 @@ "채도": "饱和度", "채우기 (가운데 잘라냄)": "填充(裁切中间)", "처음 사용 시 내려받기 {0}": "首次使用时下载 {0}", + "처음 아이콘 (파랑)": "最初的图标(蓝色)", "첫 줄 추가": "添加第一行", "청각장애인용": "听障字幕", "체코어": "捷克语", @@ -829,6 +836,8 @@ "프로그램의 모든 글자를 고른 언어로 보여 줍니다. 목록에 없는 언어는 [언어 추가] 로 이 기기의 AI 번역 모델이 자동 번역해 넣습니다.": "以所选语言显示程序的所有文字。列表中没有的语言可通过[添加语言],由此设备的 AI 翻译模型自动翻译添加。", "프로그램이 이미 켜져 있을 때. 새 창은 재생만 하는 창이며 닫으면 그 창만 끝납니다": "程序已在运行时。新窗口仅用于播放,关闭后只结束该窗口", "핀란드어": "芬兰语", + "필름": "胶片", + "필름 + JJ": "胶片 + JJ", "필수 프로그램 설치": "安装必需程序", "필수 프로그램 점검": "检查必需程序", "필수 프로그램이 모두 설치되어 있습니다.": "所有必需程序均已安装。", diff --git a/assets/tray_icon.ico b/assets/tray_icon.ico index 448868c..0a48377 100644 Binary files a/assets/tray_icon.ico and b/assets/tray_icon.ico differ diff --git a/lib/app/settings.dart b/lib/app/settings.dart index 92fb7e8..5c4e530 100644 --- a/lib/app/settings.dart +++ b/lib/app/settings.dart @@ -7,6 +7,7 @@ import 'package:path_provider/path_provider.dart'; import '../core/download_detect.dart'; import '../core/encode_options.dart'; import '../core/playlist.dart'; +import '../services/app_shell.dart' show appIconOf; /// MKV 세부 정보의 이동 버튼 하나: 표시 이름 · 옮길 폴더 class MoveTarget { @@ -110,6 +111,9 @@ class AppSettings { /// 화면 언어 (ko · en · ja · zh-Hans, 또는 더한 언어) String uiLanguage = 'ko'; + /// 앱 아이콘 (appIconIds 중 하나: 'yellow' 기본 · 'black' · 'film_jj' · 'film' · 'blue') + String appIcon = 'yellow'; + /// 더한 화면 언어 (AI 로 자동 번역한 사전이 설정 폴더 l10n 에 있음) List uiLanguagesAdded = []; @@ -178,6 +182,7 @@ class AppSettings { 'openFileAction': openFileAction, 'screenOrientation': screenOrientation, 'uiLanguage': uiLanguage, + 'appIcon': appIcon, 'uiLanguagesAdded': uiLanguagesAdded, 'openFileWindow': openFileWindow, 'homeUrl': homeUrl, @@ -245,6 +250,7 @@ class AppSettings { ..uiScaleDefault = clampUiScale(j['uiScaleDefault'] as num?) ..openFileAction = j['openFileAction'] == 'add' ? 'add' : 'play' ..uiLanguage = j['uiLanguage'] as String? ?? 'ko' + ..appIcon = appIconOf(j['appIcon'] as String?) ..uiLanguagesAdded = [for (final x in (j['uiLanguagesAdded'] as List?) ?? const []) '$x'] ..screenOrientation = const ['portrait', 'auto'].contains(j['screenOrientation']) ? j['screenOrientation'] as String diff --git a/lib/main.dart b/lib/main.dart index c044eb0..e0b83b8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -188,6 +188,7 @@ Future main(List args) async { if (nav != null) unawaited(DownloadsPage.open(nav, downloads)); }, ); + await services.shell.setAppIcon(controller.settings.appIcon); // 제목 표시줄에 버전 · 지난번 창 위치에서 열기 · 동영상 목록 불러오기 (창끼리 공유) await windowManager.setTitle(appTitle); final shell = services.shell; @@ -370,6 +371,7 @@ Future runAndroid(String dataDir) async { i18n.init(controller, dataDir); await i18n.apply(controller.settings.uiLanguage, save: false); await applyScreenOrientation(controller.settings.screenOrientation); + unawaited(services.shell.setAppIcon(controller.settings.appIcon)); final bookmarks = BookmarksController(); await bookmarks.load(); final navigatorKey = GlobalKey(); diff --git a/lib/platform/android/android_shell.dart b/lib/platform/android/android_shell.dart index 37bc0e2..8eca0b7 100644 --- a/lib/platform/android/android_shell.dart +++ b/lib/platform/android/android_shell.dart @@ -29,6 +29,14 @@ class AndroidShell extends NoopShell { @override Future isFullScreen() async => _full; + /// 앱 목록 · 홈 화면 아이콘: 고른 아이콘의 activity-alias 만 켠다 (런처가 다시 그리는 데 몇 초 걸릴 수 있음) + @override + Future setAppIcon(String id) async { + try { + await _ch.invokeMethod('setAppIcon', {'id': appIconOf(id)}); + } catch (_) {} + } + /// 기기의 기본 브라우저로 (고른 브라우저는 PC 에서만) @override Future openUrl(String url, {String browser = 'system'}) async { diff --git a/lib/platform/windows/desktop_shell.dart b/lib/platform/windows/desktop_shell.dart index c6ebfb3..26a6faa 100644 --- a/lib/platform/windows/desktop_shell.dart +++ b/lib/platform/windows/desktop_shell.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:flutter/services.dart'; +import 'package:path/path.dart' as p; import 'package:hotkey_manager/hotkey_manager.dart'; import 'package:tray_manager/tray_manager.dart' as tray; import 'package:screen_retriever/screen_retriever.dart'; @@ -41,11 +42,31 @@ class DesktopShell with WindowListener implements AppShell { await setHotkey(hotkey); } + /// 고른 앱 아이콘 (init 전에 [setAppIcon] 으로 정해 둘 수 있음) + String _icon = appIconIds.first; + + tray.Image? _trayImage() => + tray.ImageAsset.fromAsset('assets/icon/variants/$_icon.ico') ?? + tray.ImageAsset.fromAsset('assets/tray_icon.ico') ?? + tray.Image.fromFile(Platform.resolvedExecutable); + + /// 창 · 작업 표시줄 · 트레이 아이콘 (exe 파일 아이콘 · 바탕 화면 바로 가기는 그대로) + @override + Future setAppIcon(String id) async { + _icon = appIconOf(id); + final ico = p.join(p.dirname(Platform.resolvedExecutable), 'data', 'flutter_assets', 'assets', 'icon', 'variants', + '$_icon.ico'); + try { + if (File(ico).existsSync()) await windowManager.setIcon(ico); + } catch (_) {} + final t = _tray; + if (t != null) t.icon = _trayImage(); + } + void _initTray() { final t = tray.TrayIcon.create(); if (t == null) return; - t.icon = tray.ImageAsset.fromAsset('assets/tray_icon.ico') ?? - tray.Image.fromFile(Platform.resolvedExecutable); + t.icon = _trayImage(); t.setTooltip('JJ_MKVMaker'); final menu = tray.Menu.create()!; diff --git a/lib/services/app_shell.dart b/lib/services/app_shell.dart index 9093a63..3e5ee20 100644 --- a/lib/services/app_shell.dart +++ b/lib/services/app_shell.dart @@ -25,6 +25,9 @@ abstract class AppShell { /// 프로그램 종료 Future quit(); + /// 앱 아이콘 바꾸기 ([appIconIds] 중 하나). Windows: 창 · 작업 표시줄 · 트레이, Android: 앱 목록 · 홈 화면 + Future setAppIcon(String id); + // ───────── 플레이어 창 ───────── Future setFullScreen(bool on); @@ -88,6 +91,8 @@ class NoopShell implements AppShell { @override Future quit() async {} @override + Future setAppIcon(String id) async {} + @override Future setFullScreen(bool on) async {} @override Future isFullScreen() async => false; @@ -119,6 +124,19 @@ class NoopShell implements AppShell { Future restart() async {} } +/// 고를 수 있는 앱 아이콘 (환경 설정 > 일반 > 앱 아이콘). 첫 번째가 기본. +/// 그림은 tool/make_icon.py 가 만든다: `assets/icon/variants/_256.png` · `_button.png` · `.ico` +const appIconIds = ['yellow', 'black', 'film_jj', 'film', 'blue']; + +/// 저장된 값이 모르는 것이면 기본 아이콘 +String appIconOf(String? id) => appIconIds.contains(id) ? id! : appIconIds.first; + +/// 앱 안 왼쪽 위 홈 버튼 그림 +String appIconButtonAsset(String? id) => 'assets/icon/variants/${appIconOf(id)}_button.png'; + +/// 설정에서 고를 때 보여 주는 큰 그림 +String appIconPreviewAsset(String id) => 'assets/icon/variants/${appIconOf(id)}_256.png'; + /// "Ctrl+Shift+X" → (수식키 목록, 키 이름). 형식이 틀리면 null. (List, String)? parseHotkey(String text) { final parts = text.split('+').map((s) => s.trim()).where((s) => s.isNotEmpty).toList(); diff --git a/lib/ui/app_actions.dart b/lib/ui/app_actions.dart index 98f6448..a814ce9 100644 --- a/lib/ui/app_actions.dart +++ b/lib/ui/app_actions.dart @@ -4,6 +4,7 @@ import '../app/app_controller.dart'; import '../app/bookmarks_controller.dart'; import '../app/download_manager.dart'; import '../app/settings.dart'; +import '../services/app_shell.dart' show appIconButtonAsset; import '../services/system_usage.dart'; import 'browser_page.dart'; import 'downloads_page.dart'; @@ -84,7 +85,8 @@ class AppNavButtons extends StatelessWidget { width: width, child: Row(children: [ btn( - Image.asset('assets/icon/app_button_256.png', width: 26, height: 26, filterQuality: FilterQuality.medium), + Image.asset(appIconButtonAsset(scope?.controller.settings.appIcon), + width: 26, height: 26, filterQuality: FilterQuality.medium), trf('홈 화면 ({0}) · 환경 설정에서 바꿈', [homeIsBrowser ? tr('웹 브라우저') : tr('MKV 화면')]), scope == null ? null : () => toHome(context), ), diff --git a/lib/ui/settings_page.dart b/lib/ui/settings_page.dart index c1687f4..4ae5364 100644 --- a/lib/ui/settings_page.dart +++ b/lib/ui/settings_page.dart @@ -278,6 +278,7 @@ class _SettingsPageState extends State { _group('general', Icons.tune, tr('일반'), [ // 화면 언어 (읽을 수 없는 언어를 골라도 바로 찾아 되돌릴 수 있게 맨 위) const LanguageSettings(), + _appIconTile(c, desk), if (c.services.updater != null) ...[ ListTile( leading: const Icon(Icons.system_update_alt, color: JjColors.accent), @@ -829,6 +830,54 @@ class _SettingsPageState extends State { ); /// 묶음 안의 작은 제목 + /// 앱 아이콘 고르기: 그림을 누르면 바로 바뀐다 + Widget _appIconTile(AppController c, bool desk) { + final now = appIconOf(c.settings.appIcon); + return ListTile( + title: Text(tr('앱 아이콘')), + subtitle: Padding( + padding: const EdgeInsets.only(top: 4), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text(desk + ? tr('창 · 작업 표시줄 · 트레이와 앱 안 왼쪽 위 버튼의 아이콘입니다 (실행 파일 · 바탕 화면 바로 가기 아이콘은 그대로)') + : tr('앱 목록 · 홈 화면과 앱 안 왼쪽 위 버튼의 아이콘입니다. 바꾸면 홈 화면에 둔 아이콘이 없어질 수 있어 앱 목록에서 다시 끌어다 놓아야 할 수 있습니다')), + const SizedBox(height: 8), + Wrap(spacing: 10, runSpacing: 10, children: [ + for (final id in appIconIds) + Tooltip( + message: id == appIconIds.first ? '${_appIconName(id)} (${tr('기본')})' : _appIconName(id), + child: InkWell( + borderRadius: BorderRadius.circular(14), + onTap: id == now + ? null + : () { + c.updateSettings((x) => x.appIcon = id); + c.services.shell.setAppIcon(id); + }, + child: Container( + padding: const EdgeInsets.all(4), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + border: Border.all(color: id == now ? JjColors.accent : Colors.transparent, width: 3), + ), + child: Image.asset(appIconPreviewAsset(id), width: 56, height: 56, filterQuality: FilterQuality.medium), + ), + ), + ), + ]), + ]), + ), + ); + } + + static String _appIconName(String id) => switch (id) { + 'yellow' => tr('노랑'), + 'black' => tr('검정'), + 'film_jj' => tr('필름 + JJ'), + 'film' => tr('필름'), + _ => tr('처음 아이콘 (파랑)'), + }; + Widget _subTitle(String t) => Padding( padding: const EdgeInsets.fromLTRB(16, 12, 16, 0), child: Text(t, style: const TextStyle(fontSize: 13, color: JjColors.accent, fontWeight: FontWeight.w600)), diff --git a/pubspec.yaml b/pubspec.yaml index 24f3bfd..b907e3b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -93,8 +93,7 @@ flutter: assets: - assets/tray_icon.ico - assets/l10n/ - - assets/icon/app_icon_256.png - - assets/icon/app_button_256.png + - assets/icon/variants/ # To add assets to your application, add an assets section, like this: # assets: diff --git a/test/app_icon_test.dart b/test/app_icon_test.dart new file mode 100644 index 0000000..802662c --- /dev/null +++ b/test/app_icon_test.dart @@ -0,0 +1,31 @@ +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:jj_mkvmaker/app/settings.dart'; +import 'package:jj_mkvmaker/services/app_shell.dart'; + +void main() { + test('앱 아이콘 설정: 기본은 노랑, 저장 · 불러오기, 모르는 값은 기본으로', () { + expect(AppSettings().appIcon, 'yellow'); + final s = AppSettings()..appIcon = 'black'; + expect(AppSettings.fromJson(s.toJson()).appIcon, 'black'); + expect(AppSettings.fromJson({'appIcon': 'nope'}).appIcon, 'yellow'); + expect(AppSettings.fromJson({}).appIcon, 'yellow'); + }); + + test('아이콘마다 그림 파일 (설정 미리보기 · 앱 안 버튼 · Windows ico · Android 런처) 이 있다', () { + for (final id in appIconIds) { + expect(File(appIconPreviewAsset(id)).existsSync(), isTrue, reason: id); + expect(File(appIconButtonAsset(id)).existsSync(), isTrue, reason: id); + expect(File('assets/icon/variants/$id.ico').existsSync(), isTrue, reason: id); + expect(File('android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_$id.xml').existsSync(), isTrue, reason: id); + expect(File('android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_$id.png').existsSync(), isTrue, reason: id); + } + // Android: 아이콘마다 activity-alias 하나 (처음에는 기본만 켜짐) + final manifest = File('android/app/src/main/AndroidManifest.xml').readAsStringSync(); + for (final id in appIconIds) { + expect(manifest, contains('@mipmap/ic_launcher_$id"'), reason: id); + } + expect('android:enabled="true"'.allMatches(manifest).length, 1); + }); +} diff --git a/tool/make_icon.py b/tool/make_icon.py index 0f8f527..6abb0f4 100644 --- a/tool/make_icon.py +++ b/tool/make_icon.py @@ -1,13 +1,18 @@ -"""JJ_MKVMaker 아이콘 만들기 (1024px 원본 → .ico / .png) +"""JJ_MKVMaker 아이콘 만들기 (환경 설정 > 일반 > 앱 아이콘 에서 고르는 여러 가지) -검은 바탕 (짙은 회색 → 검정) 에 노란 J · J 와 큰 흰 재생(▶) 버튼, 위아래 필름 구멍 띠. -(JJ 로 시작하는 다른 프로그램과 헷갈리지 않게 글자 대신 재생 버튼이 주인공) +아이콘 종류 (id): + yellow 노란 바탕 + 검은 J · J + 검은 재생 버튼 (두 가지 색만, 기본) + black 검은 바탕 + 노란 J · J + 흰 재생 버튼 + 필름 띠 + film_jj 빨강 → 주황 바탕 + 노란 J · J + 흰 재생 버튼 + 필름 띠 + film 빨강 → 주황 바탕 + 큰 흰 재생 버튼 + 필름 띠 + blue 청록 → 보라 바탕 + 흰 JJ + 재생 표시 (처음 아이콘) 사용법: python tool/make_icon.py -결과: assets/icon/app_icon_1024.png · app_icon_256.png · app_icon_small_256.png, - windows/runner/resources/app_icon.ico, assets/tray_icon.ico, - android/app/src/main/res/mipmap-*/ic_launcher.png (예전 Android), - mipmap-*/ic_launcher_foreground.png · ic_launcher_background.png + mipmap-anydpi-v26/ic_launcher.xml (Android 8 이상) +결과: assets/icon/variants/_256.png (설정 미리보기) · _button.png (앱 안 왼쪽 위 버튼) · .ico (Windows 창 · 트레이) + assets/icon/app_icon_1024.png · windows/runner/resources/app_icon.ico · assets/tray_icon.ico (기본 아이콘) + android/app/src/main/res/mipmap-*/ic_launcher[_].png · ic_launcher[_]_bg.png · _fg.png + + mipmap-anydpi-v26/ic_launcher[_].xml (Android 8 이상 adaptive icon) + build/icon_preview.png (한눈에 보기) """ from pathlib import Path @@ -15,48 +20,50 @@ from PIL import Image, ImageDraw, ImageFilter, ImageFont ROOT = Path(__file__).resolve().parent.parent S = 1024 # 원본 크기 - -TOP = (58, 58, 66) # 바탕 왼쪽 위 (짙은 회색) -BOTTOM = (6, 6, 8) # 바탕 오른쪽 아래 (검정) -FILM = (0, 0, 0) # 필름 띠 (바탕보다 더 검게) -YELLOW = (255, 214, 0) # JJ 글자 (검은 바탕에서 눈에 띄게) -OUTLINE = (0, 0, 0) # JJ 테두리 FONT = 'C:/Windows/Fonts/seguibl.ttf' # Segoe UI Black +DEFAULT = 'yellow' +IDS = ['yellow', 'black', 'film_jj', 'film', 'blue'] + +WHITE = (255, 255, 255) +BLACK = (0, 0, 0) +YELLOW = (255, 210, 0) # 노란 아이콘 바탕 +JJ_YELLOW = (255, 214, 0) # 검은 · 빨간 아이콘의 JJ 글자 def lerp(a, b, t): return tuple(round(a[i] + (b[i] - a[i]) * t) for i in range(3)) -def rounded_mask(size, radius): - m = Image.new('L', (size, size), 0) - ImageDraw.Draw(m).rounded_rectangle((0, 0, size - 1, size - 1), radius=radius, fill=255) +def rounded_mask(radius): + m = Image.new('L', (S, S), 0) + ImageDraw.Draw(m).rounded_rectangle((0, 0, S - 1, S - 1), radius=int(S * radius), fill=255) return m -def gradient(size): - """대각선 그라데이션 (왼쪽 위 짙은 회색 → 오른쪽 아래 검정) + 왼쪽 위의 은은한 빛""" - g = Image.new('RGB', (size, size)) +def gradient(c1, c2, glow=0): + """대각선 그라데이션 (왼쪽 위 c1 → 오른쪽 아래 c2) + 왼쪽 위의 은은한 빛""" + g = Image.new('RGB', (S, S)) px = g.load() - for y in range(size): - for x in range(size): - px[x, y] = lerp(TOP, BOTTOM, (x + y) / (2 * (size - 1))) - glow = Image.new('L', (size, size), 0) - ImageDraw.Draw(glow).ellipse((-size * 0.4, -size * 0.5, size * 0.8, size * 0.45), fill=22) - glow = glow.filter(ImageFilter.GaussianBlur(size * 0.15)) - return Image.composite(Image.new('RGB', (size, size), (255, 255, 255)), g, glow) + for y in range(S): + for x in range(S): + px[x, y] = lerp(c1, c2, (x + y) / (2 * (S - 1))) + if glow: + m = Image.new('L', (S, S), 0) + ImageDraw.Draw(m).ellipse((-S * 0.4, -S * 0.5, S * 0.8, S * 0.45), fill=glow) + m = m.filter(ImageFilter.GaussianBlur(S * 0.15)) + g = Image.composite(Image.new('RGB', (S, S), WHITE), g, m) + return g -def play(size, scale=1.0, cx=0.5, cy=0.5): - """흰 재생 버튼 (모서리가 둥근 굵은 삼각형 + 그림자) 만 있는 투명 그림""" - layer = Image.new('RGBA', (size, size), (0, 0, 0, 0)) - h = size * 0.46 * scale # 삼각형 높이 (세로) - w = h * 0.92 # 너비 - # 무게 중심이 가운데 오도록 약간 오른쪽으로 - x0 = size * cx - w * 0.40 - y0 = size * cy - h / 2 +def play(scale=1.0, cx=0.5, cy=0.5, color=WHITE, shadow=(0, 0, 0, 200)): + """재생 버튼 (모서리가 둥근 굵은 삼각형). shadow=None: 그림자 없음 (단색 아이콘)""" + layer = Image.new('RGBA', (S, S), (0, 0, 0, 0)) + h = S * 0.46 * scale + w = h * 0.92 + x0 = S * cx - w * 0.40 # 무게 중심이 가운데 오도록 약간 오른쪽으로 + y0 = S * cy - h / 2 pts = [(x0, y0), (x0, y0 + h), (x0 + w, y0 + h / 2)] - r = size * 0.05 * scale # 모서리 둥글기 (선 두께로) + r = S * 0.05 * scale def draw(img, fill, off=(0, 0)): d = ImageDraw.Draw(img) @@ -66,132 +73,201 @@ def play(size, scale=1.0, cx=0.5, cy=0.5): for x, y in p: d.ellipse((x - r, y - r, x + r, y + r), fill=fill) - shadow = Image.new('RGBA', (size, size), (0, 0, 0, 0)) - draw(shadow, (0, 0, 0, 200), off=(size * 0.012 * scale, size * 0.028 * scale)) - shadow = shadow.filter(ImageFilter.GaussianBlur(size * 0.025 * scale)) - layer = Image.alpha_composite(layer, shadow) - draw(layer, (255, 255, 255, 255)) + if shadow: + sh = Image.new('RGBA', (S, S), (0, 0, 0, 0)) + draw(sh, shadow, off=(S * 0.012 * scale, S * 0.028 * scale)) + layer = Image.alpha_composite(layer, sh.filter(ImageFilter.GaussianBlur(S * 0.025 * scale))) + draw(layer, color + (255,)) return layer -def jj(size, cx, top, bottom): - """왼쪽에 J 를 위 · 아래로 하나씩 (노란 굵은 글자 + 진한 테두리 + 그림자)""" - layer = Image.new('RGBA', (size, size), (0, 0, 0, 0)) +def jj(cx, top, bottom, color, outline=None, shadow=True): + """J 를 위 · 아래로 하나씩. outline: 테두리 색 (글자와 같은 색이면 더 굵게), shadow: 그림자""" + layer = Image.new('RGBA', (S, S), (0, 0, 0, 0)) h = (bottom - top) / 2 font = ImageFont.truetype(FONT, int(h * 1.18)) - stroke = max(2, int(h * 0.09)) - shadow = Image.new('RGBA', (size, size), (0, 0, 0, 0)) + # 글자와 같은 색 테두리는 살짝만 (위아래 J 가 붙지 않게) + stroke = 0 if not outline else int(h * (0.03 if outline == color else 0.09)) + sh = Image.new('RGBA', (S, S), (0, 0, 0, 0)) for i in range(2): d = ImageDraw.Draw(layer) l, t, r, b = d.textbbox((0, 0), 'J', font=font, stroke_width=stroke) x = cx - (l + r) / 2 y = top + h * i + (h - (b - t)) / 2 - t - ImageDraw.Draw(shadow).text((x + h * 0.04, y + h * 0.07), 'J', font=font, fill=(0, 0, 0, 200), + if outline and shadow: + ImageDraw.Draw(sh).text((x + h * 0.04, y + h * 0.07), 'J', font=font, fill=(0, 0, 0, 200), stroke_width=stroke, stroke_fill=(0, 0, 0, 200)) - d.text((x, y), 'J', font=font, fill=YELLOW + (255,), stroke_width=stroke, stroke_fill=OUTLINE + (255,)) - shadow = shadow.filter(ImageFilter.GaussianBlur(h * 0.05)) - return Image.alpha_composite(shadow, layer) - - -def film_bands(size, top, band, holes=6): - """위아래 필름 띠 (어두운 띠 + 흰 구멍)""" - layer = Image.new('RGBA', (size, size), (0, 0, 0, 0)) - d = ImageDraw.Draw(layer) - for y0 in (top, size - top - band): - d.rectangle((0, y0, size, y0 + band), fill=FILM + (255,)) - hw, hh = size / (holes * 2.2), band * 0.46 - gap = size / holes - for i in range(holes): - cx = gap * (i + 0.5) - cy = y0 + band / 2 - d.rounded_rectangle((cx - hw / 2, cy - hh / 2, cx + hw / 2, cy + hh / 2), radius=hh * 0.3, - fill=(235, 235, 240, 255)) + d.text((x, y), 'J', font=font, fill=color + (255,), stroke_width=stroke, + stroke_fill=(outline or color) + (255,)) + if outline and shadow: + layer = Image.alpha_composite(sh.filter(ImageFilter.GaussianBlur(h * 0.05)), layer) return layer -def make_master(small=False): - """앱 아이콘 (둥근 사각형). small=True: 16~32px 용 (필름 띠 없이 재생 버튼을 크게)""" - icon = Image.new('RGBA', (S, S), (0, 0, 0, 0)) - icon.paste(gradient(S), (0, 0), rounded_mask(S, int(S * 0.22))) - if small: - return Image.alpha_composite(icon, play(S, scale=1.25)) - bands = film_bands(S, top=int(S * 0.055), band=int(S * 0.13)) - icon = Image.alpha_composite(icon, Image.composite(bands, Image.new('RGBA', (S, S)), rounded_mask(S, int(S * 0.22)))) - # 왼쪽: J · J (위 · 아래), 재생 버튼은 오른쪽으로 - icon = Image.alpha_composite(icon, jj(S, cx=S * 0.19, top=S * 0.23, bottom=S * 0.77)) - return Image.alpha_composite(icon, play(S, scale=0.92, cx=0.6)) +def film_bands(top, band, film, hole, holes=6): + """위아래 필름 띠 (어두운 띠 + 밝은 구멍)""" + layer = Image.new('RGBA', (S, S), (0, 0, 0, 0)) + d = ImageDraw.Draw(layer) + for y0 in (top, S - top - band): + d.rectangle((0, y0, S, y0 + band), fill=film + (255,)) + hw, hh = S / (holes * 2.2), band * 0.46 + gap = S / holes + for i in range(holes): + cx, cy = gap * (i + 0.5), y0 + band / 2 + d.rounded_rectangle((cx - hw / 2, cy - hh / 2, cx + hw / 2, cy + hh / 2), radius=hh * 0.3, + fill=hole + (255,)) + return layer -def make_adaptive(): - """Android 8 이상 (adaptive icon): 108dp 바탕 (그라데이션 + 필름 띠) · 앞 (재생 버튼, 안전 영역 66dp 안)""" - bg = gradient(S).convert('RGBA') - bg = Image.alpha_composite(bg, film_bands(S, top=int(S * 0.17), band=int(S * 0.1), holes=7)) - # 안전 영역 (가운데 66/108) 안에: 왼쪽 J · J, 오른쪽 재생 버튼 - fg = Image.alpha_composite(jj(S, cx=S * 0.335, top=S * 0.31, bottom=S * 0.69), play(S, scale=0.55, cx=0.57)) - return bg, fg +def on(bg, *layers, radius=0.22): + """둥근 사각형 바탕 위에 그림들을 겹친다 (바깥으로 나간 부분은 자름)""" + mask = rounded_mask(radius) + img = Image.new('RGBA', (S, S), (0, 0, 0, 0)) + img.paste(bg, (0, 0), mask) + for l in layers: + img = Image.alpha_composite(img, Image.composite(l, Image.new('RGBA', (S, S)), mask)) + return img + + +def big_jj(color, outline=None, size=0.62): + """가운데 큰 JJ (앱 안 26px 버튼처럼 작아도 또렷하게)""" + layer = Image.new('RGBA', (S, S), (0, 0, 0, 0)) + font = ImageFont.truetype(FONT, int(S * size)) + d = ImageDraw.Draw(layer) + stroke = int(S * 0.035) if outline else 0 + l, t, r, b = d.textbbox((0, 0), 'JJ', font=font, stroke_width=stroke) + d.text(((S - (r - l)) / 2 - l, (S - (b - t)) / 2 - t), 'JJ', font=font, fill=color + (255,), + stroke_width=stroke, stroke_fill=(outline or color) + (255,)) + return layer + + +def variant(vid): + """아이콘 하나: (큰 아이콘, 16~32px 용, 앱 안 버튼, Android 바탕, Android 앞)""" + if vid == 'yellow': + # 노랑 + 검정 두 가지 색만: 그림자 · 테두리 없이 단순하게 + bg = Image.new('RGB', (S, S), YELLOW) + master = on(bg, jj(S * 0.215, S * 0.2, S * 0.8, BLACK, BLACK, shadow=False), play(0.95, cx=0.6, color=BLACK, shadow=None)) + small = on(bg, play(1.3, color=BLACK, shadow=None)) + button = on(bg, big_jj(BLACK)) + fg = Image.alpha_composite(jj(S * 0.345, S * 0.3, S * 0.7, BLACK, BLACK, shadow=False), + play(0.55, cx=0.575, color=BLACK, shadow=None)) + return master, small, button, bg.convert('RGBA'), fg + + if vid in ('black', 'film_jj', 'film'): + if vid == 'black': + bg, film, hole, outline = gradient((58, 58, 66), (6, 6, 8), glow=22), BLACK, (235, 235, 240), BLACK + else: + bg, film, hole, outline = gradient((255, 45, 85), (255, 149, 0), glow=45), (25, 10, 30), WHITE, (60, 0, 30) + bands = film_bands(int(S * 0.055), int(S * 0.13), film, hole) + if vid == 'film': + master = on(bg, bands, play(1.0)) + fg = play(0.62) + else: + master = on(bg, bands, jj(S * 0.19, S * 0.23, S * 0.77, JJ_YELLOW, outline), play(0.92, cx=0.6)) + fg = Image.alpha_composite(jj(S * 0.335, S * 0.31, S * 0.69, JJ_YELLOW, outline), play(0.55, cx=0.57)) + small = on(bg, play(1.25)) + button = on(bg, big_jj(JJ_YELLOW, outline)) + a_bg = Image.alpha_composite(bg.convert('RGBA'), film_bands(int(S * 0.17), int(S * 0.1), film, hole, holes=7)) + return master, small, button, a_bg, fg + + # blue: 처음 아이콘 (청록 → 보라 + 흰 JJ + 오른쪽 아래 흰 동그라미 재생 표시) + violet = (108, 92, 255) + bg = gradient((0, 196, 204), violet, glow=38) + + def white_jj(size, dx, dy): + layer = Image.new('RGBA', (S, S), (0, 0, 0, 0)) + font = ImageFont.truetype(FONT, int(S * size)) + l, t, r, b = ImageDraw.Draw(layer).textbbox((0, 0), 'JJ', font=font) + x, y = (S - (r - l)) / 2 - l + dx, (S - (b - t)) / 2 - t + dy + sh = Image.new('RGBA', (S, S), (0, 0, 0, 0)) + ImageDraw.Draw(sh).text((x + S * 0.012, y + S * 0.022), 'JJ', font=font, fill=(20, 10, 60, 120)) + layer = Image.alpha_composite(layer, sh.filter(ImageFilter.GaussianBlur(S * 0.018))) + ImageDraw.Draw(layer).text((x, y), 'JJ', font=font, fill=WHITE + (255,)) + return layer + + def badge(cx, cy, rad): + b = Image.new('RGBA', (S, S), (0, 0, 0, 0)) + ImageDraw.Draw(b).ellipse((cx - rad * 1.12, cy - rad * 1.12, cx + rad * 1.12, cy + rad * 1.12), + fill=(40, 20, 110, 90)) + b = b.filter(ImageFilter.GaussianBlur(S * 0.012)) + d = ImageDraw.Draw(b) + d.ellipse((cx - rad, cy - rad, cx + rad, cy + rad), fill=WHITE + (255,)) + tri = rad * 0.52 + d.polygon([(cx - tri * 0.62, cy - tri), (cx - tri * 0.62, cy + tri), (cx + tri * 0.95, cy)], + fill=violet + (255,)) + return b + + master = on(bg, white_jj(0.56, -S * 0.035, -S * 0.02), badge(S * 0.765, S * 0.765, S * 0.135), radius=0.23) + small = on(bg, white_jj(0.66, 0, 0), radius=0.23) + fg = Image.alpha_composite(white_jj(0.36, -S * 0.02, -S * 0.01), badge(S * 0.64, S * 0.64, S * 0.085)) + return master, small, small, bg.convert('RGBA'), fg + + +def save_ico(path, master, small, sizes): + imgs = [(small if s <= 32 else master).resize((s, s), Image.LANCZOS) for s in sizes] + imgs[-1].save(path, format='ICO', sizes=[(s, s) for s in sizes], append_images=imgs[:-1]) def main(): - master = make_master() - out = ROOT / 'assets' / 'icon' + icon_dir = ROOT / 'assets' / 'icon' + out = icon_dir / 'variants' out.mkdir(parents=True, exist_ok=True) - master.save(out / 'app_icon_1024.png') - master.resize((256, 256), Image.LANCZOS).save(out / 'app_icon_256.png') - - # 앱 안 왼쪽 위 홈 버튼 (26px 정도): 바탕 + 큰 노란 JJ 만 (작아도 또렷하게) - button = Image.new('RGBA', (S, S), (0, 0, 0, 0)) - button.paste(gradient(S), (0, 0), rounded_mask(S, int(S * 0.22))) - font = ImageFont.truetype(FONT, int(S * 0.62)) - d = ImageDraw.Draw(button) - stroke = int(S * 0.035) - l, t, r, b = d.textbbox((0, 0), 'JJ', font=font, stroke_width=stroke) - d.text(((S - (r - l)) / 2 - l, (S - (b - t)) / 2 - t), 'JJ', font=font, fill=YELLOW + (255,), - stroke_width=stroke, stroke_fill=OUTLINE + (255,)) - button.resize((256, 256), Image.LANCZOS).save(out / 'app_button_256.png') - - # 작은 크기 (16~32px) 는 단순 버전, 큰 크기는 필름 띠 포함 버전 - simple = make_master(small=True) - simple.resize((256, 256), Image.LANCZOS).save(out / 'app_icon_small_256.png') - - def ico(path, sizes): - imgs = [(simple if s <= 32 else master).resize((s, s), Image.LANCZOS) for s in sizes] - imgs[-1].save(path, format='ICO', sizes=[(s, s) for s in sizes], append_images=imgs[:-1]) - - ico(ROOT / 'windows' / 'runner' / 'resources' / 'app_icon.ico', [16, 20, 24, 32, 40, 48, 64, 128, 256]) - ico(ROOT / 'assets' / 'tray_icon.ico', [16, 20, 24, 32, 48]) - - # Android: 예전 기기용 아이콘 + Android 8 이상 adaptive icon (기기마다 다른 모양 틀에 꽉 차게) res = ROOT / 'android' / 'app' / 'src' / 'main' / 'res' - bg, fg = make_adaptive() - for name, px in {'mdpi': 48, 'hdpi': 72, 'xhdpi': 96, 'xxhdpi': 144, 'xxxhdpi': 192}.items(): - d = res / f'mipmap-{name}' - d.mkdir(parents=True, exist_ok=True) - master.resize((px, px), Image.LANCZOS).save(d / 'ic_launcher.png') - a = px * 108 // 48 # adaptive 는 108dp - bg.resize((a, a), Image.LANCZOS).save(d / 'ic_launcher_background.png') - fg.resize((a, a), Image.LANCZOS).save(d / 'ic_launcher_foreground.png') any_dpi = res / 'mipmap-anydpi-v26' any_dpi.mkdir(parents=True, exist_ok=True) - (any_dpi / 'ic_launcher.xml').write_text( - '\n' - '\n' - ' \n' - ' \n' - ' \n' - '\n', encoding='utf-8') + dpis = {'mdpi': 48, 'hdpi': 72, 'xhdpi': 96, 'xxhdpi': 144, 'xxxhdpi': 192} + # 예전 판에서 만든 파일 정리 + for name in ('app_icon_256.png', 'app_icon_small_256.png', 'app_button_256.png', 'preview.png'): + (icon_dir / name).unlink(missing_ok=True) + for d in res.glob('mipmap-*'): + for f in d.glob('ic_launcher*'): + f.unlink() - # 미리보기 (원형 마스크 · 둥근 사각형 마스크) - prev = Image.new('RGBA', (S * 3 + 80, S + 40), (40, 40, 48, 255)) - prev.alpha_composite(master, (20, 20)) - full = Image.alpha_composite(bg, fg) - crop = int(S * 18 / 108) - view = full.crop((crop, crop, S - crop, S - crop)).resize((S, S), Image.LANCZOS) - circle = Image.new('L', (S, S), 0) - ImageDraw.Draw(circle).ellipse((0, 0, S, S), fill=255) - prev.paste(view, (S + 40, 20), circle) - prev.paste(view, (S * 2 + 60, 20), rounded_mask(S, int(S * 0.3))) - prev.resize((prev.width // 4, prev.height // 4), Image.LANCZOS).save(out / 'preview.png') - print('아이콘 생성 완료:', out / 'app_icon_1024.png') + all_icons = {} + for vid in IDS: + master, small, button, a_bg, a_fg = all_icons[vid] = variant(vid) + master.resize((256, 256), Image.LANCZOS).save(out / f'{vid}_256.png') + button.resize((128, 128), Image.LANCZOS).save(out / f'{vid}_button.png') + save_ico(out / f'{vid}.ico', master, small, [16, 20, 24, 32, 40, 48, 64, 128, 256]) + # Android: 고를 수 있는 아이콘마다 ic_launcher_, 기본 아이콘은 ic_launcher 로도 + for name in [f'_{vid}'] + ([''] if vid == DEFAULT else []): + for dpi, px in dpis.items(): + d = res / f'mipmap-{dpi}' + d.mkdir(parents=True, exist_ok=True) + master.resize((px, px), Image.LANCZOS).save(d / f'ic_launcher{name}.png') + a = px * 108 // 48 # adaptive 는 108dp + a_bg.resize((a, a), Image.LANCZOS).save(d / f'ic_launcher{name}_bg.png') + a_fg.resize((a, a), Image.LANCZOS).save(d / f'ic_launcher{name}_fg.png') + (any_dpi / f'ic_launcher{name}.xml').write_text( + '\n' + '\n' + f' \n' + f' \n' + f' \n' + '\n', encoding='utf-8') + if vid == DEFAULT: + master.save(icon_dir / 'app_icon_1024.png') + save_ico(ROOT / 'windows' / 'runner' / 'resources' / 'app_icon.ico', master, small, + [16, 20, 24, 32, 40, 48, 64, 128, 256]) + save_ico(ROOT / 'assets' / 'tray_icon.ico', master, small, [16, 20, 24, 32, 48]) + + # 한눈에 보기: 줄마다 큰 아이콘 · Android 동그라미 · 앱 안 버튼 / 16px + prev = Image.new('RGBA', (len(IDS) * 280 + 20, 660), (128, 128, 136, 255)) + circle = Image.new('L', (256, 256), 0) + ImageDraw.Draw(circle).ellipse((0, 0, 255, 255), fill=255) + c = int(S * 18 / 108) + for i, vid in enumerate(IDS): + master, small, button, a_bg, a_fg = all_icons[vid] + x = 20 + i * 280 + prev.alpha_composite(master.resize((256, 256), Image.LANCZOS), (x, 20)) + full = Image.alpha_composite(a_bg, a_fg).crop((c, c, S - c, S - c)).resize((256, 256), Image.LANCZOS) + prev.paste(full, (x, 296), circle) + prev.alpha_composite(button.resize((52, 52), Image.LANCZOS), (x, 580)) + prev.alpha_composite(small.resize((32, 32), Image.LANCZOS), (x + 80, 590)) + prev.alpha_composite(small.resize((16, 16), Image.LANCZOS), (x + 130, 598)) + (ROOT / 'build').mkdir(exist_ok=True) + prev.save(ROOT / 'build' / 'icon_preview.png') + print('icons ok') if __name__ == '__main__': diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico index 9ea798c..51f68b7 100644 Binary files a/windows/runner/resources/app_icon.ico and b/windows/runner/resources/app_icon.ico differ