Skip to content

Commit 35589ef

Browse files
committed
Add image conversion script and update build configurations
- Introduced a new script `convert_images_to_gif.py` for converting PNG images to an animated GIF. - Updated `build_publish.yml` to allow the publish step to continue on error. - Updated Gradle wrapper to version 8.14.1 and Kotlin to version 2.1.21. - Modified AndroidManifest.xml and other files for improved theme handling and navigation. - Added preview functions for various composables to enhance UI testing.
1 parent 3fae99a commit 35589ef

42 files changed

Lines changed: 678 additions & 120 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build_publish.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,5 @@ jobs:
2929
- name: Build
3030
run: ./gradlew build
3131
- name: Publish
32+
continue-on-error: true
3233
run: ./gradlew publishAllPublicationsToSonatypeRepository --max-workers 1 closeAndReleaseRepository

README.md

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,52 @@ Supported platforms:
99
- iOS
1010
- Desktop JVM (MacOS, Linux, Windows)
1111

12-
![Android screenshot](screenshots/android.gif)
13-
![Desktop screenshot](screenshots/desktop.gif)
12+
![Android screenshot](doc/screenshots/gif/android/material_design_3/demo.gif)
13+
![iOS screenshot](doc/screenshots/gif/iOS/material_design_3/demo.gif)
14+
![Desktop screenshot](doc/screenshots/gif/desktop/material_design_3/demo.gif)
15+
<details>
16+
<summary>More…</summary>
17+
<p><img src="doc/screenshots/gif/android/material_design_2/demo.gif"> <img src="doc/screenshots/gif/iOS/material_design_2/demo.gif"> <img src="doc/screenshots/gif/desktop/material_design_2/demo.gif"></p>
18+
</details>
1419

1520
## Usage
16-
Call composable functions for material theme wrap, preferences items on settings screen, showing dialog for select theme.
21+
Call composable functions to wrap your app, show theme preference items, and handle dialog state manually.
22+
23+
**Show dialog directly:**
24+
Use `ThemeAlertDialog` to show a dialog with theme preferences:
1725
```kotlin
1826
@Composable
19-
fun App() = PreferableMaterialTheme { // provides composition locals
20-
SettingsScaffold { // includes TopAppBar
21-
Box {
22-
Column {
23-
ThemePreferencesCategory() // subtitle
24-
ThemePreferenceItem() // menu item
25-
}
26-
themePrefs.showDialogIfNeed() // shows when menu item clicked
27+
fun App() = PreferableMaterialTheme {
28+
var showDialog by remember { mutableStateOf(false) }
29+
SettingsScaffold(onThemeClick = { showDialog = true }) {
30+
Column {
31+
ThemePreferencesCategory() // subtitle
32+
ThemePreferenceItem(onClick = { showDialog = true }) // menu item
2733
}
2834
}
35+
if (showDialog) {
36+
ThemeAlertDialog(dismissDialog = { showDialog = false })
37+
}
2938
}
3039
```
40+
**With navigation frameworks:**
41+
Use `ThemeDialogContent` as the content of a dialog destination:
42+
```kotlin
43+
@Composable
44+
fun App() = PreferableMaterialTheme {
45+
val navController = rememberNavController()
46+
NavHost(navController = navController) {
47+
composable<AppNavGraph.Settings> {
48+
SettingsBody(onThemeClick = { navController.navigate(route = AppNavGraph.ThemeDialog) })
49+
}
50+
dialog<AppNavGraph.ThemeDialog> {
51+
ThemeDialogContent(dismissDialog = navController::popBackStack)
52+
}
53+
}
54+
}
55+
```
56+
Check the [sample app](sample/src/commonMain/kotlin/com/softartdev/shared/App.kt) for using with Material Design versions 2 & 3.
57+
3158
The [NoteDelight](https://github.com/softartdev/NoteDelight/blob/master/shared-compose-ui/src/commonMain/kotlin/com/softartdev/notedelight/ui/SettingsScreen.kt#L104) app is a real example.
3259
## Installation
3360
The latest release is available on [Maven Central](https://repo1.maven.org/maven2/io/github/softartdev/theme-material/).
@@ -49,7 +76,7 @@ commonMain {
4976
}
5077
```
5178
## Implementation
52-
Used [moko-resources](https://github.com/icerockdev/moko-resources) library for many languages (currently Russian and English are supported).
79+
Used [compose-multiplatform-resources](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-resources.html) library for many languages (currently Russian and English are supported).
5380

5481
Persisting preferences is implemented using [SharedPreferences](https://developer.android.com/reference/android/content/SharedPreferences) on Android, and [Java Preference API](https://docs.oracle.com/javase/7/docs/api/java/util/prefs/Preferences.html) on JVM Desktop.
5582
```kotlin
@@ -69,6 +96,13 @@ private var preferences: Preferences = Preferences.userNodeForPackage(ThemeEnum:
6996
actual var themeEnum: ThemeEnum
7097
get() = preferences.getInt(THEME_KEY, ThemeEnum.SystemDefault.ordinal).let(ThemeEnum.values()::get)
7198
set(value) = preferences.putInt(THEME_KEY, value.ordinal)
99+
100+
// ios:
101+
private val preferences: NSUserDefaults = NSUserDefaults.standardUserDefaults
102+
103+
actual var themeEnum: ThemeEnum
104+
get() = preferences.integerForKey(THEME_KEY).let(ThemeEnum.values()::get)
105+
set(value) = preferences.setInteger(value.ordinal, THEME_KEY)
72106
```
73107
Also used [composition local](https://developer.android.com/jetpack/compose/compositionlocal) for access from theme-scoped as an implicit way:
74108
```kotlin

build.gradle.kts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
buildscript {
2-
extra["kotlin_version"] = "2.1.20"
2+
extra["kotlin_version"] = "2.1.21"
33
extra["android_min_sdk_version"] = 21
4-
extra["android_compile_sdk_version"] = 35
4+
extra["android_compile_sdk_version"] = 36
55
extra["jdk_version"] = 17
66
repositories {
77
gradlePluginPortal()
@@ -11,8 +11,9 @@ buildscript {
1111
dependencies {
1212
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${rootProject.extra["kotlin_version"]}")
1313
classpath("org.jetbrains.kotlin:compose-compiler-gradle-plugin:${rootProject.extra["kotlin_version"]}")
14-
classpath("com.android.tools.build:gradle:8.9.1")
15-
classpath("org.jetbrains.compose:compose-gradle-plugin:1.8.0-beta01")
14+
classpath("com.android.tools.build:gradle:8.10.1")
15+
classpath("org.jetbrains.compose:compose-gradle-plugin:1.8.1")
16+
classpath("org.jetbrains.kotlin:kotlin-serialization:${rootProject.extra["kotlin_version"]}")
1617
}
1718
}
1819
allprojects {

doc/claude.md

Whitespace-only changes.
15 KB
Loading
18 KB
Loading
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#!/usr/bin/env python3
2+
"""
3+
convert_images_to_gif.py
4+
5+
This script takes all PNG files in the current directory, sorts them alphabetically,
6+
resizes each image to a fixed height (default 500px), and combines them into a single
7+
animated GIF called 'demo.gif', displaying each frame for 1 second (1 fps).
8+
"""
9+
10+
import os
11+
from PIL import Image
12+
13+
# -----------------------------------------------------------------------------
14+
# CONSTANTS
15+
# -----------------------------------------------------------------------------
16+
# Height of the resulting GIF, in pixels (maintains aspect ratio for width)
17+
HEIGHT = 500
18+
19+
# Name of the output GIF file
20+
OUTPUT_GIF = "demo.gif"
21+
22+
# File extension filter (only PNG files in this example)
23+
EXTENSION = ".png"
24+
25+
26+
def main():
27+
# 1) List all files with the specified extension in the current directory
28+
files = [fname for fname in os.listdir(".") if fname.lower().endswith(EXTENSION)]
29+
files.sort() # Sort alphabetically
30+
31+
if not files:
32+
print(f"No '{EXTENSION}' files found in the current directory.")
33+
return
34+
35+
frames = []
36+
for fname in files:
37+
# 2) Open each image
38+
img = Image.open(fname)
39+
40+
# 3) Resize to the specified HEIGHT, maintaining aspect ratio
41+
original_width, original_height = img.size
42+
new_height = HEIGHT
43+
new_width = int(original_width * new_height / original_height)
44+
img_resized = img.resize((new_width, new_height), Image.LANCZOS)
45+
46+
# 4) Convert to paletted ("P") mode for GIF compatibility
47+
frames.append(img_resized.convert("P", palette=Image.ADAPTIVE))
48+
49+
# 5) Save frames as an animated GIF
50+
# - duration=1000ms per frame (1 second)
51+
# - loop=0 (infinite loop)
52+
try:
53+
frames[0].save(
54+
OUTPUT_GIF,
55+
format="GIF",
56+
save_all=True,
57+
append_images=frames[1:],
58+
duration=1000, # milliseconds per frame
59+
loop=0
60+
)
61+
print(f"Saved GIF as '{OUTPUT_GIF}'.")
62+
except Exception as e:
63+
print(f"Error while saving GIF: {e}")
64+
65+
66+
if __name__ == "__main__":
67+
main()
28.3 KB
Loading
32.2 KB
Loading
14.9 KB
Loading

0 commit comments

Comments
 (0)