A Jetpack Compose library specifically optimized for E-Ink displays, particularly BOOX devices. This library provides a complete set of components following the "static-first" design philosophy, ensuring perfect rendering on electrophoretic displays.
- Zero Animation Design: All components are built without animations to prevent ghosting on E-Ink displays
- High Contrast Theming: Two theme variants (High Contrast & 16-level Grayscale) optimized for E-Ink readability
- E-Ink Native Components: 15 essential components designed specifically for E-Ink constraints
- Pagination Over Scrolling: HorizontalPager-based components replace scrolling patterns
- Minimum Touch Targets: All interactive elements meet accessibility and usability guidelines
- Typography Enforcement: Automatic 14sp minimum font size compliance
- Border-Based Design: Visual separation through borders instead of shadows/elevation
Since this library is not yet published to a remote repository, you'll need to include it as a local module in your project:
# Clone into your project root or as a submodule
git clone https://github.com/avishalom/eink-compose.git
# OR add as git submodule
git submodule add https://github.com/avishalom/eink-compose.git
// In your project's settings.gradle.kts
include(":app")
include(":eink-compose:library") // Add this line
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
// In your app module's build.gradle.kts
dependencies {
implementation(project(":eink-compose:library"))
// Ensure you have the required Compose dependencies
implementation(platform("androidx.compose:compose-bom:2023.10.01"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.foundation:foundation")
implementation("androidx.activity:activity-compose:1.8.0")
}
If you prefer to copy the library code directly into your project:
# Copy the library folder to your project
cp -r eink-compose/library ./eink-compose-lib
include(":app")
include(":eink-compose-lib") // Your renamed library module
// In your app's build.gradle.kts
dependencies {
implementation(project(":eink-compose-lib"))
}
Once published to Maven Central or a custom repository, installation will be simplified to:
// This will be available in future versions
dependencies {
implementation("com.eink.compose:library:1.0.0")
}
Note: The library will be published to a Gradle repository in a future release. For now, local integration is the recommended approach.
Ensure your project targets the appropriate Android version:
android {
compileSdk = 34
defaultConfig {
minSdk = 30 // Android 11 for BOOX compatibility
targetSdk = 34
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.4"
}
}
Wrap your app content with the EInkTheme
:
import com.eink.compose.theme.EInkTheme
import com.eink.compose.theme.EInkColorVariant
@Composable
fun MyApp() {
EInkTheme(
colorVariant = EInkColorVariant.HighContrast, // or EInkColorVariant.Grayscale
darkTheme = isSystemInDarkTheme()
) {
// Your app content here
MyMainScreen()
}
}
import com.eink.compose.components.*
@Composable
fun TextExample() {
EInkColumn {
EInkHeadline("Main Title")
EInkTitle("Section Title")
EInkBodyText("This is body text that will automatically be at least 14sp")
EInkLabel("Label Text")
}
}
@Composable
fun ButtonExample() {
EInkRow {
EInkButton(onClick = { /* action */ }) {
EInkText("Primary Button")
}
EInkOutlinedButton(onClick = { /* action */ }) {
EInkText("Outlined Button")
}
EInkTextButton(onClick = { /* action */ }) {
EInkText("Text Button")
}
}
}
@Composable
fun CardExample() {
EInkCard {
EInkTitle("Card Title")
EInkBodyText("Card content goes here. Cards use borders instead of shadows.")
}
// Clickable card
EInkCard(onClick = { /* handle click */ }) {
EInkTitle("Clickable Card")
EInkBodyText("This card responds to clicks")
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PaginatedListExample() {
val items = remember { (1..100).map { "Item $it" } }
SimplePaginatedList(
items = items,
itemsPerPage = 10,
showPageIndicator = true,
indicatorStyle = PageIndicatorStyle.Numbers
) { item, index ->
EInkCard {
EInkText(item)
}
}
}
@Composable
fun TextFieldExample() {
var text by remember { mutableStateOf("") }
EInkColumn {
EInkTextField(
value = text,
onValueChange = { text = it },
label = "Label",
placeholder = "Enter text here..."
)
EInkOutlinedTextField(
value = text,
onValueChange = { text = it },
label = "Outlined TextField"
)
}
}
@Composable
fun LayoutExample() {
EInkContainer {
EInkSection(title = "Settings") {
EInkText("Option 1")
EInkText("Option 2")
}
EInkVerticalSpacer(EInkSpacerSize.Large)
EInkRow {
EInkText("Left")
EInkHorizontalSpacer(EInkSpacerSize.Medium)
EInkText("Right")
}
}
}
When creating custom components, use the E-Ink utilities:
import com.eink.compose.modifiers.staticClickable
import com.eink.compose.modifiers.grayscale
import com.eink.compose.theme.eInkColorScheme
import com.eink.compose.theme.eInkTypography
@Composable
fun CustomComponent() {
Box(
modifier = Modifier
.background(eInkColorScheme().surface)
.border(1.dp, eInkColorScheme().outline)
.staticClickable { /* no ripple click */ }
.grayscale() // Force grayscale on any content
) {
Text(
text = "Custom Component",
style = eInkTypography().bodyLarge,
color = eInkColorScheme().onSurface
)
}
}
EInkText
,EInkHeadline
,EInkTitle
,EInkBodyText
,EInkLabel
EInkButton
,EInkOutlinedButton
,EInkTextButton
EInkCard
,EInkElevatedCard
,EInkOutlinedCard
EInkTextField
,EInkOutlinedTextField
EInkRow
,EInkColumn
,EInkBox
EInkSpacer
,EInkHorizontalSpacer
,EInkVerticalSpacer
EInkSurface
,EInkContainer
,EInkSection
PaginatedList
,SimplePaginatedList
,PaginatedGrid
Modifier.staticClickable()
- Click handling without ripple effectsModifier.grayscale()
- Force grayscale renderingNoRippleInteractionSource
- Disable interaction feedback
// High contrast (pure black and white only)
EInkTheme(colorVariant = EInkColorVariant.HighContrast) { ... }
// Grayscale (16-level grayscale palette)
EInkTheme(colorVariant = EInkColorVariant.Grayscale) { ... }
@Composable
fun ThemedComponent() {
val colors = eInkColorScheme()
val typography = eInkTypography()
Text(
text = "Themed text",
color = colors.primary,
style = typography.headlineMedium
)
}
- Deploy to Device: Install the sample app on your BOOX device
- Check Component Showcase: Review all components in the included sample
- Verify Readability: Ensure text meets 14sp minimum and is readable
- Test Interactions: Confirm buttons and clicks work without visual artifacts
- Validate Performance: Check for unwanted screen flashes or ghosting
Key differences when migrating from Material Design:
Material Design | EInk Compose |
---|---|
LazyColumn β |
SimplePaginatedList |
AnimatedVisibility β |
if (visible) { ... } |
Button β |
EInkButton |
Card β |
EInkCard |
TextField β |
EInkTextField |
MaterialTheme β |
EInkTheme |
Shadows/Elevation β | Borders |
Ripple effects β | Static clicks |
- Android Version: API 30+ (Android 11)
- Jetpack Compose: BOM 2023.10.01+
- Target Devices: BOOX E-Ink devices (tested compatible)
- Kotlin: 1.9.10+
See FUTURE_ENHANCEMENTS.md for planned features and component additions.
This project is licensed under the MIT License - see the LICENSE file for details.
Built for E-Ink Excellence π€π€