![landscapist](https://user-images.githubusercontent.com/24237865/127760344-bb042fe8-23e1-4014-b208-b7b549d32086.png)

🌻  Landscapist is a highly optimized, pluggable Jetpack Compose and Kotlin Multiplatform image loading solution that fetches and displays network images with  Glide,  Coil, and  Fresco. This library supports tracing image loading states, composing custom implementations, and some valuable animations, such as crossfades, blur transformation, and circular reveals. You can also configure and attach image-loading behaviors easily and fast with image plugins.

 See official documentation for Landscapist

## Who's using Landscapist?
👉 [Check out who's using Landscapist](https://skydoves.github.io/landscapist/#whos-using-landscapist).

Landscapist hits **+300,000 downloads every month** around the globe! 🚀

![globe](https://user-images.githubusercontent.com/24237865/196018576-a9c87534-81a2-4618-8519-0024b67964bf.png)

## Why Landscapist?

Landscapist is built with a lot of consideration to improve the performance of image loadings in Jetpack Compose. Most composable functions of Landscapist are Restartable and Skippable, which indicates fairly improved recomposition performance according to the Compose compiler metrics. Also, the library performance was improved with [Baseline Profiles](https://android-developers.googleblog.com/2022/01/improving-app-performance-with-baseline.html) and it supports many pluggable features, such as [ImageOptions](https://github.com/skydoves/landscapist#imageoptions), [listening image state changes](https://github.com/skydoves/landscapist#listening-image-state-changes), [custom composables](https://github.com/skydoves/landscapist#custom-composables), [preview on Android Studio](https://github.com/skydoves/landscapist#preview-on-android-studio), [ImageComponent and ImagePlugin](https://github.com/skydoves/landscapist#imagecomponent-and-imageplugin), [placeholder](https://github.com/skydoves/landscapist#placeholder), [animations (circular reveal, crossfade)](https://github.com/skydoves/landscapist#animation), [transformation (blur)](https://github.com/skydoves/landscapist#transformation), and [palette](https://github.com/skydoves/landscapist#palette).

 See the Compose compiler metrics for Landscapist

![metrics](https://user-images.githubusercontent.com/24237865/201906004-f4490bdf-7af9-4ad6-b586-7dcc6f07d0c8.png)

## Documentation

For comprehensive information about Landscapist, please refer to the **[official documentation](https://skydoves.github.io/landscapist)**.

## Demo projects
You can see the use cases of this library in the repositories below:
- [google/modernstorage](https://github.com/google/modernstorage/tree/e62cda539ca75884dd49df3bcf8629751f0a91e6/sample): ModernStorage is a group of libraries that provide an abstraction layer over storage on Android to simplify its interactions.
- [GetStream/WhatsApp-Clone-Compose](https://github.com/getStream/whatsApp-clone-compose): 📱 WhatsApp clone project demonstrates modern Android development built with Jetpack Compose and Stream Chat SDK for Compose.
- [android/storage-samples](https://github.com/android/storage-samples/tree/main/ScopedStorage): Multiple samples showing the best practices in storage APIs on Android.
- [skydoves/DisneyCompose](https://github.com/skydoves/disneycompose): 🧸 A demo Disney app using Jetpack Compose and Hilt based on modern Android tech-stacks and MVVM architecture.
- [skydoves/MovieCompose](https://github.com/skydoves/MovieCompose): 🎞 A demo movie app using Jetpack Compose and Hilt based on modern Android tech stacks.

## SNAPSHOT

 See how to import the snapshot

### Including the SNAPSHOT
[![Landscapist](https://img.shields.io/static/v1?label=snapshot&message=landscapist&logo=apache%20maven&color=C71A36)](https://oss.sonatype.org/content/repositories/snapshots/com/github/skydoves/landscapist/)

Snapshots of the current development version of Landscapist are available, which track [the latest versions](https://oss.sonatype.org/content/repositories/snapshots/com/github/skydoves/landscapist/).

To import snapshot versions on your project, add the code snippet below on your gradle file:
```Gradle
repositories {
   maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}
```

Next, add the dependency below to your **module**'s `build.gradle` file:
```gradle
dependencies {
    implementation "com.github.skydoves:landscapist-glide:2.3.2-SNAPSHOT"
}
```

Glide

[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/landscapist.svg?label=Maven%20Central)](https://central.sonatype.com/search?q=skydoves%2520landscapist)

Add the codes below to your **root** `build.gradle` file (not your module-level build.gradle file):
```gradle
allprojects {
    repositories {
        mavenCentral()
    }
}
```

Next, add the dependency below to your **module**'s `build.gradle` file:
```gradle
dependencies {
    implementation "com.github.skydoves:landscapist-glide:2.3.4"
}
```

> **Note**: `Landscapist-Glide` includes version `4.16.0` of [Glide](https://github.com/bumptech/glide) internally. So please make sure your project is using the same Glide version or exclude the Glide dependency to adapt yours. Also, please make sure the Jetpack Compose version on the [release page](https://github.com/skydoves/Landscapist/releases).

### GlideImage
You can load images simply by using `GlideImage` composable function as the following example below:

```kotlin
GlideImage(
  imageModel = { imageUrl }, // loading a network image using an URL.
  imageOptions = ImageOptions(
    contentScale = ContentScale.Crop,
    alignment = Alignment.Center
  )
)
```

### More Details for GlideImage

 👉 Read further for more details

### Custom RequestOptions and TransitionOptions
You can customize your request-options with your own [RequestOptions](https://bumptech.github.io/glide/doc/options.html#requestoptions) and [TransitionOptions](https://bumptech.github.io/glide/doc/options.html#transitionoptions) for applying caching strategies, loading transformations like below:

```kotlin
GlideImage(
  imageModel = { imageUrl },
  requestOptions = {
    RequestOptions()
        .override(256, 256)
        .diskCacheStrategy(DiskCacheStrategy.ALL)
        .centerCrop()
  }
)
```

### Custom RequestBuilder
You can request image with your own [RequestBuilder](https://bumptech.github.io/glide/doc/options.html#requestbuilder), which is the backbone of the request in Glide and is responsible for bringing your options together with your requested url or model to start a new load.

```kotlin
GlideImage(
  imageModel = { imageUrl },
  requestBuilder = { Glide.with(LocalContext.current.applicationContext).asDrawable() },
  modifier = Modifier.constrainAs(image) {
    centerHorizontallyTo(parent)
    top.linkTo(parent.top)
  }.aspectRatio(0.8f)
)
```

### Custom RequestListener
You can register your own [RequestListener](https://bumptech.github.io/glide/javadocs/440/com/bumptech/glide/request/RequestListener.html), which allows you to trace the status of a request while images load.

```kotlin
GlideImage(
  imageModel = { imageUrl },
  requestListener = object: RequestListener<Drawable> {
    override fun onLoadFailed(
      e: GlideException?,
      model: Any?,
      target: Target<Drawable>?,
      isFirstResource: Boolean
    ): Boolean {
      // do something
      return false
    }

    override fun onResourceReady(
      resource: Drawable?,
      model: Any?,
      target: Target<Drawable>?,
      dataSource: DataSource?,
      isFirstResource: Boolean
    ): Boolean {
      // do something
      return true
    }
  }
)
```

### LocalGlideRequestOptions
You can pass the same instance of your `RequestOptions` down through the Composition in your composable hierarchy as following the example below:

```kotlin
val requestOptions = RequestOptions()
    .override(300, 300)
    .circleCrop()

CompositionLocalProvider(LocalGlideRequestOptions provides requestOptions) {
  // Loads images with the custom `requestOptions` without explicit defines.
  GlideImage(
    imageModel = ...
  )
}
```

Coil

[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/landscapist.svg?label=Maven%20Central)](https://central.sonatype.com/search?q=skydoves%2520landscapist)

Add the dependency below to your **module**'s `build.gradle` file:

```gradle
dependencies {
    implementation "com.github.skydoves:landscapist-coil:$version"
}
```

If you're targeting on Kotlin Multiplatform, add the dependency below to your module's `build.gradle.kts` file:

```kotlin
sourceSets {
    val commonMain by getting {
        dependencies {
            implementation("com.github.skydoves:landscapist-coil3:$version")
        }
    }
}
```

The `coil3-landscapist` package functions identically to the `coil-landscapist` package, with the key distinction being its focus on Kotlin Multiplatform. This enables the use of Coil3 across various platforms, including Android, iOS, and Desktop (JVM), facilitating a unified image loading experience across different environments.

> **Note**: Please make sure your project uses the same Jetpack Compose version on the [release page](https://github.com/skydoves/Landscapist/releases).

### CoilImage
You can load images by using the `CoilImage` composable function as the following example below:

```kotlin
CoilImage(
  imageModel = { imageUrl }, // loading a network image or local resource using an URL.
  imageOptions = ImageOptions(
    contentScale = ContentScale.Crop,
    alignment = Alignment.Center
  )
)
```

### More Details for CoilImage

 👉 Read further for more details

### Custom ImageRequest and ImageLoader
You can load images with your own [ImageRequest](https://coil-kt.github.io/coil/image_requests/) and [ImageLoader](https://coil-kt.github.io/coil/image_loaders/), which provides all the necessary information for loading images like caching strategies and transformations.

```kotlin
CoilImage(
  imageRequest = {
      ImageRequest.Builder(LocalContext.current)
        .data(poster.poster)
        .crossfade(true)
        .build() },
  imageLoader = {
      ImageLoader.Builder(LocalContext.current)
        .availableMemoryPercentage(0.25)
        .crossfade(true)
        .build() },
  modifier = modifier,
)
```

 ### LocalCoilImageLoader
 You can pass the same instance of your `ImageLoader` down through the Composition in your composable hierarchy as following the example below:

 ```kotlin
 val imageLoader = ImageLoader.Builder(context).build()
CompositionLocalProvider(LocalCoilImageLoader provides imageLoader) {

   // This will automatically use the value of current imageLoader in the hierarchy.
   CoilImage(
     imageModel = ...
   )
 }
 ```

## Animated Image Supports (GIF, Webp)
You can load animated GIFs and WebP Images with your `ImageLoader`.

```kotlin
val context = LocalContext.current
val imageLoader = ImageLoader.Builder(context)
  .components {
    if (SDK_INT >= 28) {
      add(ImageDecoderDecoder.Factory())
    } else {
      add(GifDecoder.Factory())
    }
  }
  .build()

CoilImage(
    imageModel = { poster.gif }, // URL of an animated image.
    imageLoader = { imageLoader },
    shimmerParams = ShimmerParams(
      baseColor = background800,
      highlightColor = shimmerHighLight
    ),
    modifier = Modifier
      .fillMaxWidth()
      .padding(8.dp)
      .height(500.dp)
      .clip(RoundedCornerShape(8.dp))
  )
```

Fresco

[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/landscapist.svg?label=Maven%20Central)](https://central.sonatype.com/search?q=skydoves%2520landscapist)

Add the dependency below to your **module**'s `build.gradle` file:
```gradle
dependencies {
    implementation "com.github.skydoves:landscapist-fresco:$version"
}
```
> **Note**: `Landscapist-Fresco` includes version `3.1.0` of Fresco. So please make sure your project is using the same Fresco version or exclude the Fresco dependency to adapt yours. Also, please make sure the Jetpack Compose version on the [release page](https://github.com/skydoves/Landscapist/releases).

### Setup
To get started, you should set up `Fresco` with [ImagePipelineConfig](https://frescolib.org/docs/configure-image-pipeline.html) in your `Application` class. Generally, it's recommended initializing with  `OkHttpImagePipelineConfigFactory`. Also, you can customize caching, networking, and thread pool strategies with your own `ImagePipelineConfig`. For more details, you can check out [Using Other Network Layers](https://frescolib.org/docs/using-other-network-layers.html#using-okhttp).
```kotlin
class App : Application() {

  override fun onCreate() {
    super.onCreate()

    val pipelineConfig =
      OkHttpImagePipelineConfigFactory
        .newBuilder(this, OkHttpClient.Builder().build())
        .setDiskCacheEnabled(true)
        .setDownsampleEnabled(true)
        .setResizeAndRotateEnabledForNetwork(true)
        .build()

    Fresco.initialize(this, pipelineConfig)
  }
}
```

### FrescoImage
You can load images by using the `FrescoImage` composable function as the following example below:

```kotlin
FrescoImage(
  imageUrl = stringImageUrl, // loading a network image using an URL.
  imageOptions = ImageOptions(
    contentScale = ContentScale.Crop,
    alignment = Alignment.Center
  )
)
```

### More Details for FrescoImage

 👉 Read further for more details

### Custom ImageRequest
You can load images with your own [ImageRequest](https://frescolib.org/docs/image-requests.html), which provides some necessary information for loading images like decoding strategies and resizing.

```kotlin
val imageRequest = ImageRequestBuilder
  .newBuilderWithSource(uri)
  .setImageDecodeOptions(decodeOptions)
  .setLocalThumbnailPreviewsEnabled(true)
  .setLowestPermittedRequestLevel(RequestLevel.FULL_FETCH)
  .setProgressiveRenderingEnabled(false)
  .setResizeOptions(ResizeOptions(width, height))
  .build()

FrescoImage(
  imageUrl = stringImageUrl,
  imageRequest = { imageRequest }
)
```

### LocalFrescoImageRequest
You can pass the same instance of your `imageRequestBuilder` down through the Composition in your composable hierarchy as following the example below:

```kotlin
// customize the ImageRequest as needed
val imageRequestBuilder = ImageRequestBuilder
  .newBuilderWithSource(uri)
  .setImageDecodeOptions(decodeOptions)
  .setLocalThumbnailPreviewsEnabled(true)
  .setLowestPermittedRequestLevel(RequestLevel.FULL_FETCH)
  .setProgressiveRenderingEnabled(false)
  .setResizeOptions(ResizeOptions(width, height))

CompositionLocalProvider(LocalFrescoImageRequest provides imageRequestBuilder) {
  // This will automatically use the value of current ImageRequest in the hierarchy.
  FrescoImage(
    imageUrl = ...
  )
}
```

## Fresco Animated Image Support (GIF, Webp)
[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/landscapist.svg?label=Maven%20Central)](https://central.sonatype.com/search?q=skydoves%2520landscapist)

Add the below dependency to your **module**'s `build.gradle` file.

```gradle
dependencies {
  implementation "com.github.skydoves:landscapist-fresco-websupport:$version"
}
```

You can load animated GIFs and WebP Images with `FrescoWebImage` composable function. You should pass the `AbstractDraweeController` like the following example below:

```kotlin
FrescoWebImage(
  controllerBuilder = {
      Fresco.newDraweeControllerBuilder()
          .setUri(poster.gif) // GIF or Webp image url.
          .setAutoPlayAnimations(true)
  },
  modifier = Modifier
    .fillMaxWidth()
    .height(300.dp)
)
```

For more details, check out [DraweeController](https://frescolib.org/docs/animations.html), and [Supported URIs](https://frescolib.org/docs/supported-uris.html) for setting URI addresses. Also, you can load general images (jpeg, png, etc) which can be loaded with `FrescoImage` by using `FrescoWebImage` and your custom controller.

## ImageOptions

You can give image options to your image composable functions by passing `ImageOptions` instance like the below:

```kotlin
GlideImage(
  ..
  imageOptions = ImageOptions(
      contentScale = ContentScale.Crop,
      alignment = Alignment.Center,
      contentDescription = "main image",
      colorFilter = null,
      alpha = 1f
    )
)
```

### RequestSize

You can set the request size of your image by giving `requestSize` property as seen in the below:

```kotlin
GlideImage(
  ..
  imageOptions = ImageOptions(requestSize = IntSize(800, 600)),
)
```

## Listening image state changes

You can listen the image state changes by giving `onImageStateChanged` parameter to your image composable functions like the below:

```kotlin
GlideImage(
  ..
  onImageStateChanged = {
    when (it) {
      GlideImageState.None -> ..
      GlideImageState.Loading -> ..
      is GlideImageState.Success -> ..
      is GlideImageState.Failure -> ..
    }
  }
)
```

> **Note**: You can use `CoilImageState` for `CoilImage` and `FrescoImageState` for `FrescoImage`.

### DataSource

For the success state, you can trace the origin of the image with the `DataSource` parameter. `DataSource` represents the following source origins below:

- **Memory**: Represents an in-memory data source or cache (e.g. bitmap, ByteBuffer).
- **Disk**: Represents a disk-based data source (e.g. drawable resource, or File).
- **Network**: Represents a network-based data source.
- **Unknown**: Represents an unknown data source.

## Custom Composables
You can execute your own composable functions depending on the three request states below:

- **loading**: Executed while loading an image.
- **success**: Executed if loading an image successfully.
- **failure**: Executed if fails to load an image (e.g. network error, wrong destination).

```kotlin
GlideImage( // CoilImage, FrescoImage
  imageModel = { imageUrl },
  modifier = modifier,
  // shows an indicator while loading an image.
  loading = {
    Box(modifier = Modifier.matchParentSize()) {
      CircularProgressIndicator(
        modifier = Modifier.align(Alignment.Center)
      )
    }
  },
  // shows an error text if fail to load an image.
  failure = {
    Text(text = "image request failed.")
  }
)
```

Also, you can customize the image content with our own composable function like the example below:

```kotlin
GlideImage( // CoilImage, FrescoImage
  imageModel = { imageUrl },
  success = { state, painter ->
    Image(
      painter = painter,
      modifier = Modifier.size(128.dp), // draw a resized image.
      contentDescription = "Image"
    )
  },
  ..
)
```
> **Note**: You can also use the custom Composables for **`CoilImage`** and **`FrescoImage`**.

## Preview on Android Studio
Landscapist supports preview mode for each image library; **Glide**, **Coil**, and **Fresco**. You can show the preview image on your editor with a `previewPlaceholder` parameter as following:

```kotlin
GlideImage(
  imageModel = { imageUrl },
  modifier = Modifier.aspectRatio(0.8f),
  previewPlaceholder = painterResource(id = R.drawable.poster)
)
```
> **Note**: You can also use the the `previewPlaceholder` parameter for **`CoilImage`** and **`FrescoImage`**.

## ImageComponent and ImagePlugin

You can compose supported image plugins by Landscapist or you can create your own image plugin that will be composed following the image loading state.
`ImagePlugin` is a pluggable compose interface that will be executed for loading images. `ImagePlugin` provides following types below:

- **PainterPlugin**: A pinter plugin interface to be composed with the given `Painter`.
- **LoadingStatePlugin**: A pluggable state plugin that will be composed while the state is `ImageLoadState.Loading`.
- **SuccessStatePlugin**: A pluggable state plugin that will be composed when the state is `ImageLoadState.Success`.
- **FailureStatePlugin**: A pluggable state plugin that will be composed when the state is `ImageLoadState.Failure`.

For example, you can implement your own `LoadingStatePlugin` that will be composed while loading an image like the below:

```kotlin
data class LoadingPlugin(val source: Any?) : ImagePlugin.LoadingStatePlugin {

  @Composable
  override fun compose(
    modifier: Modifier,
    imageOptions: ImageOptions?
  ): ImagePlugin = apply {
    if (source != null && imageOptions != null) {
      ImageBySource(
        source = source,
        modifier = modifier,
        alignment = imageOptions.alignment,
        contentDescription = imageOptions.contentDescription,
        contentScale = imageOptions.contentScale,
        colorFilter = imageOptions.colorFilter,
        alpha = imageOptions.alpha
      )
    }
  }
}
```

Next, you can compose plugins by adding them in the `rememberImageComponent` like the below:

```kotlin
GlideImage(
  imageModel = { poster.image },
  component = rememberImageComponent {
    add(CircularRevealPlugin())
    add(LoadingPlugin(source))
  },
)
```

or you can just add plugins by using the **+** expression like the below:

```kotlin
GlideImage(
  imageModel = { poster.image },
  component = rememberImageComponent {
    +CircularRevealPlugin()
    +LoadingPlugin(source)
  },
)
```

### LocalImageComponent

You can provide the same `ImageComponent` instance in the composable hierarchy by using `imageComponent` extension and `LocalImageComponent` like the below:

```kotlin
val component = imageComponent {
  +CrossfadePlugin()
  +PalettePlugin()
}

CompositionLocalProvider(LocalImageComponent provides component) {
  ..
}
```

## Placeholder

[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/landscapist.svg?label=Maven%20Central)](https://central.sonatype.com/search?q=skydoves%2520landscapist)

The `landscapist-placeholder` package provides useful image plugins, such as loading & failure placeholder supports and shimmering animation.
To use placeholder supports, add the dependency below:

```groovy
dependencies {
    implementation "com.github.skydoves:landscapist-placeholder:$version"
}
```

### ShimmerPlugin
You can implement a shimmering effect while loading an image by using the `ShimmerPlugin` as following the example below:

```kotlin
GlideImage( // CoilImage, FrescoImage
  imageModel = { imageUrl },
  modifier = modifier,
  component = rememberImageComponent {
    // shows a shimmering effect when loading an image.
    +ShimmerPlugin(
      Shimmer.Flash(
        baseColor = Color.White,
        highlightColor = Color.LightGray,
      ),
    )
  },
  // shows an error text message when request failed.
  failure = {
    Text(text = "image request failed.")
  }
)
 ```
 > **Note**: You can also use the Shimmer effect for **`CoilImage`** and **`FrescoImage`**.

`Shimmer` sealed class provides following the three different types: `Resonate`, `Fade`, and `Flash`.

|                                                               Resonate                                                                |                                                                 Fade                                                                  |                                                                 Flash                                                                 |
|:-------------------------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------------------:|
|  |  |  |

### PlaceholderPlugin

You can show your own placeholder while loading an image or when fails to load an image with `PlaceholderPlugin.Loading` and `PlaceholderPlugin.Failure`.

```kotlin
GlideImage(
  ..
  component = rememberImageComponent {
      +PlaceholderPlugin.Loading(painterResource(id = R.drawable.placeholder_loading))
      +PlaceholderPlugin.Failure(painterResource(id = R.drawable.placeholder_failure))
    },
)
```

> **Note**: The source should be one of `ImageBitmap`, `ImageVector`, or `Painter`.

### ThumbnailPlugin

Landscapist supports the thumbnail feature, which pre-loads and displays small sizes of images while loading the original image. So you can make users feel images loading faster and give images a nature loading effect while displaying an original image.
To show thumbnail, add the image plugin into your image component like the example below:

```kotlin
GlideImage(
  ..,
  component = rememberImageComponent {
      +ThumbnailPlugin()
  },
)
```

You can also adjust the request sizes by giving the `requestSize` parameter:

```kotlin
component = rememberImageComponent {
    +ThumbnailPlugin(IntSize(30 ,30))
},
```

> **Note**: It's highly recommended to use a small size of the request size on the thumbnail plugin to load the pre-load images process faster.

## Animation

[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/landscapist.svg?label=Maven%20Central)](https://central.sonatype.com/search?q=skydoves%2520landscapist)

The `landscapist-animation` package provides useful image plugins related to animations, such as crossfade and circular reveal animation.
To use animation supports, add the dependency below:

```groovy
dependencies {
    implementation "com.github.skydoves:landscapist-animation:$version"
}
```

### Preview

|                                                                Circular Reveal                                                                 |                                                                   Crossfade                                                                    |
|:----------------------------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------------------------------------------------:|
|  |  |

### Crossfade Animation

You can implement the crossfade animation while drawing images with `CrossfadePlugin` as the following:

```kotlin
GlideImage(
  imageModel = { poster.image },
  component = rememberImageComponent {
    +CrossfadePlugin(
      duration = 550
    )
  }
)
```

 > **Note**: You can also use the crossfade animation for **`CoilImage`** and **`FrescoImage`**.

### Circular Reveal Animation
You can implement the circular reveal animation while drawing images with `CircularRevealplugin` as the following:

```kotlin
GlideImage(
  imageModel = { poster.image },
  component = rememberImageComponent {
    +CircularRevealPlugin(
      duration = 350
    )
  }
)
```

 > **Note**: You can also use the Circular Reveal animation for **`CoilImage`** and **`FrescoImage`**.

 ## Transformation

[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/landscapist.svg?label=Maven%20Central)](https://central.sonatype.com/search?q=skydoves%2520landscapist)

The `landscapist-transformation` package provides useful image transformation plugins, such as the blur effect.
To use transformation supports, add the dependency below:

```groovy
dependencies {
    implementation "com.github.skydoves:landscapist-transformation:$version"
}
```

### BlurTransformationPlugin

You can implement the blur effect with `BlurTransformationPlugin` as the following:

```kotlin
GlideImage( // CoilImage, FrescoImage also can be used.
  imageModel = { poster.image },
  component = rememberImageComponent {
      +BlurTransformationPlugin(radius = 10) // between 0 to Int.MAX_VALUE.
  }
)
```

>**Note**: Landscapist's blur transformation falls back onto a CPU-based implementation to support older API levels. So you don't need to worry about API compatibilities and performance issues.

## Palette

[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/landscapist.svg?label=Maven%20Central)](https://central.sonatype.com/search?q=skydoves%2520landscapist)

The `landscapist-palette` package provides useful image plugins related to palette, such as extracting primary color sets.
To use palette supports, add the dependency below:

```groovy
dependencies {
    implementation "com.github.skydoves:landscapist-palette:$version"
}
```

You can extract primary (theme) color profiles with `PalettePlugin`. You can check out [Extract color profiles](https://developer.android.com/training/material/palette-colors#extract-color-profiles) to see what kinds of colors can be extracted.

```kotlin
var palette by rememberPaletteState(null)

GlideImage( // CoilImage, FrescoImage also can be used.
  imageModel = { poster.image },
  component = rememberImageComponent {
      +PalettePlugin { palette = it }
  }
)

Crossfade(
  targetState = palette,
  modifier = Modifier
    .padding(horizontal = 8.dp)
    .size(45.dp)
) {
  Box(
    modifier = Modifier
      .background(color = Color(it?.lightVibrantSwatch?.rgb ?: 0))
      .fillMaxSize()
  )
}
```

Also, you can customize attributes of `PalettePlugin` like the example below:

```kotlin
var palette by remember { mutableStateOf<Palette?>(null) }

GlideImage( // CoilImage, FrescoImage also can be used.
  imageModel = { poster.image },
  component = rememberImageComponent {
    +PalettePlugin(
      imageModel = poster.image,
      useCache = true, // use cache strategies for the same image model.
      interceptor = {
        it.addFilter { rgb, hsl ->
          // here edit to add the filter colors.
          false
        }
      },
      paletteLoadedListener = {
        palette = it
      }
    )
  }
)
```
 > **Note**: You can also use the Palette for **`CoilImage`** and **`FrescoImage`**.

 ## BOM

 [![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/landscapist.svg?label=Maven%20Central)](https://central.sonatype.com/search?q=skydoves%2520landscapist)

 The landscapist Bill of Materials (BOM) lets you manage all of your landscapist library versions by specifying only the BOM’s version.

 ```groovy
dependencies {
    // Import the landscapist BOM
    implementation "com.github.skydoves:landscapist-bom:$version"

    // Import landscapist libraries
    implementation "com.github.skydoves:landscapist-glide" // fresco or coil
    implementation "com.github.skydoves:landscapist-placeholder"
    implementation "com.github.skydoves:landscapist-palette"
    implementation "com.github.skydoves:landscapist-transformation"
}
 ```

 ## Taking Snapshot Images With Paparazzi

[Paparazzi](https://github.com/cashapp/paparazzi) allows you to take snapshot images of your Composable functions without running them on physical devices. You can take proper snapshots images about your images with Paparazzi like the below:

```kotlin
paparazzi.snapshot {
  CompositionLocalProvider(LocalInspectionMode provides true) {
    GlideImage(
      modifier = Modifier.fillMaxSize(),
      imageModel = { ".." },
      previewPlaceholder = painterResource(R.drawable.placeholder)
  )
 }
}
```

## Who's using Landscapist?
If your project uses Landscapist, please let me know by creating a new issue! 🤗

## [Twitter for Android](https://user-images.githubusercontent.com/24237865/125583736-f0ffa76f-8f87-433b-a9fd-192231dc5e63.jpg)

[![twitter](https://user-images.githubusercontent.com/24237865/125583182-9527dd48-433e-4e17-ae52-3f2bb544a847.jpg)](https://play.google.com/store/apps/details?id=com.twitter.android&hl=ko&gl=US)

## Inspiration
This library was mostly inspired by [Accompanist](https://github.com/chrisbanes/accompanist).

> Accompanist is a group of libraries that contains some utilities which I've found myself copying around projects which use Jetpack Compose. Currently, it contains image loading and insets. You can get more variety and recent systems from the library maintained by Google.

## Find this repository useful? :heart:
Support it by joining __[stargazers](https://github.com/skydoves/Landscapist/stargazers)__ for this repository. :star:

Also __[follow](https://github.com/skydoves)__ me for my next creations! 🤩

# License
```xml
Designed and developed by 2020 skydoves (Jaewoong Eum)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```


<picture>
  <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/arkivanov/Decompose/master/docs/media/logo/titled-1.png">
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/arkivanov/Decompose/master/docs/media/logo/logo-titled-dark.png">

</picture>

[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0)
[![Twitter URL](https://img.shields.io/badge/Twitter-@arkann1985-blue.svg?style=social&logo=twitter)](https://twitter.com/arkann1985)

---

Decompose is a Kotlin Multiplatform library for breaking down your code into tree-structured lifecycle-aware business logic components (aka BLoC), with routing functionality and pluggable UI (Jetpack/Multiplatform Compose, Android Views, SwiftUI, Kotlin/React, etc.).

Please see the [project website](https://arkivanov.github.io/Decompose/) for documentation and APIs.

Should you have any questions or ideas - there is [Discussions](https://github.com/arkivanov/Decompose/discussions) section. Also welcome to the Kotlin Slack channel - [#decompose](https://kotlinlang.slack.com/archives/C03H3N51SKT)!

## ⚡⚡⚡ Where are all the stars, issues, discussions, pull requests, etc?

Having spent 5 years working on a variety of projects for Badoo/Bumble, I’m now off to another adventure. As part of that transition I was asked to transfer this repository to [Badoo GitHub account](https://github.com/badoo).

Now I **continue my work** on this project **as a copy**.

There should be no breaking changes related to this transfer. Most of the external links should not be broken. The repository link is also the same: [arkivanov/Decompose](https://github.com/arkivanov/Decompose). Please file an issue in this repository, if you think something is broken or does not work properly.

Here is what is mostly affected by the transfer:

- All the stars were transferred
- All the issues and discussions were transferred as well. I will do all my best to fill the gap here.
- All pull requests with all the comment history are also gone.

I will continue doing all my best for this project and for the community! Business as usual!

Additional resources:

- [A thread on Hacker News](https://news.ycombinator.com/item?id=29447854)

## Why Decompose?

* Decompose breaks the code down into small and independent components and organizes them into trees. Each parent component is only aware of its immediate children.
* Decompose draws clear boundaries between UI and non-UI code, which gives the following benefits:
  * Better separation of concerns
  * Pluggable platform-specific UI (Compose, SwiftUI, Kotlin/React, etc.)
  * Business logic code is testable with pure multiplatform unit tests
* Navigation state is fully exposed - plug any UI you want, animate as you like using your favourite UI framework's API or use predefined API.
* Navigation is a pure function from the old state to a new one - navigate without limits.
* Proper dependency injection (DI) and inversion of control (IoC) via constructor, including but not limited to type-safe arguments.
* Shared navigation logic
* Lifecycle-aware components
* Components in the back stack are not destroyed, they continue working in background without UI
* State preservation (automatically on Android, manually on all other targets via `kotlinx-serialization`)
* Instances retaining (aka ViewModels) over configuration changes (mostly useful in Android)

## Setup

Please check the [Installation](https://arkivanov.github.io/Decompose/getting-started/installation/) section of the documentation.

### Supported platforms

In general, Decompose supports the following targets: `android`, `jvm`, `ios`, `watchos`, `tvos`, `macos`, `wasmJs`, `js`. However, some modules do not support all targets or the support depends on the Decompose version. Please see the Installation docs for details.

## Overview

Here are some key concepts of the library, more details can be found in the documentation.

* [Component](https://arkivanov.github.io/Decompose/component/overview/) - every component represents a piece of logic with its own lifecycle, the UI is optional and is plugged externally
* [ComponentContext](https://arkivanov.github.io/Decompose/component/overview/#componentcontext) - every component has its own [ComponentContext], which makes components lifecycle-aware and allows state preservation, instances retaining (aka AndroidX `ViewModel`) and back button handling
* [Child Stack](https://arkivanov.github.io/Decompose/navigation/stack/overview/) - enables navigation between child components, nested navigation is also supported
* [Child Slot](https://arkivanov.github.io/Decompose/navigation/slot/overview/) - allows only one child component at a time, or none
* [Child Pages](https://arkivanov.github.io/Decompose/navigation/pages/overview/) - a list of child components with one selected component (e.g. pager-like navigation)
* [Generic Navigation](https://arkivanov.github.io/Decompose/navigation/children/overview/) - provides a way to create your own custom navigation model, when none of the predefined models fit your needs
* [Lifecycle](https://arkivanov.github.io/Decompose/component/lifecycle/) - provides a way to listen for lifecycle events in components
* [StateKeeper](https://arkivanov.github.io/Decompose/component/state-preservation/) - makes it possible to preserve state or data in a component when it gets destroyed
* [InstanceKeeper](https://arkivanov.github.io/Decompose/component/instance-retaining/) - retains instances in your components (similar to AndroidX `ViewModel`)
* [BackPressedHandler](https://arkivanov.github.io/Decompose/component/back-button/) - provides a way to handle and intercept back button presses

### Component hierarchy

### Pluggable UI hierarchy

### Typical component structure

## Quick start

Please refer to the [Quick start](https://arkivanov.github.io/Decompose/getting-started/quick-start/) section of the docs.

## Samples

Check out the [Samples](https://arkivanov.github.io/Decompose/samples/) section of the docs for a full description of each sample.

## Template

Check out the [template repository](https://github.com/arkivanov/decompose-multiplatform-template) which may be used to kick-start a project for you.

## Articles

- [Decompose — experiments with Kotlin Multiplatform lifecycle-aware components and navigation](https://proandroiddev.com/decompose-experiments-with-kotlin-multiplatform-lifecycle-aware-components-and-navigation-a04ef3c7f6a3?source=friends_link&sk=f7d289cc329b6c8a765fc049e36c313f)
- [Fully cross-platform Kotlin applications (almost)](https://proandroiddev.com/fully-cross-platform-kotlin-applications-almost-29c7054f8f28?source=friends_link&sk=4619fdcb17912fde589bc4fca83efbbd)
- [A comprehensive thirty-line navigation for Jetpack/Multiplatform Compose](https://medium.com/p/5b723c4f256e) - if you find Decompose verbose and would prefer something built on top of Compose.

- "Component-based Approach" series by [Artur Artikov](https://twitter.com/artik_artikov)
  - Part 1: [Fighting Complexity in Android Applications](https://medium.com/@a.artikov/component-based-approach-fighting-complexity-in-android-applications-2eaf5e8c5fad)
  - Part 2: [Implementing Screens with the Decompose Library](https://medium.com/@a.artikov/component-based-approach-implementing-screens-with-the-decompose-library-19c41d8ed087)

## Author

Twitter: [@arkann1985](https://twitter.com/arkann1985)

If you like this project you can always  ;-)


# workflow

[![Kotlin CI](https://github.com/square/workflow-kotlin/workflows/Kotlin%20CI/badge.svg)](https://github.com/square/workflow-kotlin/actions?query=branch%3Amain)
[![Maven Central](https://img.shields.io/maven-central/v/com.squareup.workflow1/workflow-core-jvm.svg?label=Maven%20Central)](https://central.sonatype.com/namespace/com.squareup.workflow1)
[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](https://www.apache.org/licenses/LICENSE-2.0)
[![Kotlinlang slack](https://img.shields.io/static/v1?label=kotlinlang&message=squarelibraries&color=brightgreen&logo=slack)](https://kotlinlang.slack.com/archives/C5HT9AL7Q)

Workflow is an application framework that provides architectural primitives.

Workflow is:

* Written in and used for Kotlin and Swift
* A unidirectional data flow library that uses immutable data within each Workflow.
  Data flows in a single direction from source to UI, and events in a single direction
  from the UI to the business logic.
* A library that supports writing business logic and complex UI navigation logic as
  state machines, thereby enabling confident reasoning about state and validation of
  correctness.
* Optimized for composability and scalability of features and screens.
* Corresponding UI frameworks that bind Rendering data classes for “views”
  (including event callbacks) to Mobile UI frameworks for Android and iOS.
* A corresponding testing framework that facilitates simple-to-write unit
  tests for all application business logic and helps ensure correctness.

_**1.0.0-rc is ready and the core is stable. There are still experimental /**
**under construction areas of the API for UI integration however.**
These classes and functions are marked with `@WorkflowUIExperimentalApi`.
They are suitable for production use (we've been shipping them for months
at the very heart of our flagship app), but may require signature tweaks as
we iterate a bit more on Dialog management, and configuring transition effects.
If they do change, we will take care to minimize the impact via deprecation, etc._

## Using Workflows in your project

### Maven Artifacts

Artifacts are hosted on Maven Central. If you're using Gradle, ensure `mavenCentral()` appears in
your `repositories` block, and then add dependencies on the following artifacts:

    Maven Coordinates
    Depend on this if…

    com.squareup.workflow1:workflow-core-jvm:x.y.z
    You are writing a library module/project that uses Workflows, but you don't need to interact
    with the runtime from the outside.

    com.squareup.workflow1:workflow-rx2:x.y.z
    You need to interact with RxJava2 from your Workflows.

    com.squareup.workflow1:workflow-testing-jvm:x.y.z
    You are writing tests. This should only be included as a test dependency.

    com.squareup.workflow1:workflow-ui-core-android:x.y.z
    You're writing an Android app that uses Workflows.

### Lower-level Artifacts

Most code shouldn't need to depend on these directly. They should generally only be used to build
higher-level integrations with UI frameworks.

    Maven Coordinates
    Depend on this if…

    com.squareup.workflow1:workflow-runtime-jvm:x.y.z
    You need to interact directly with the runtime, i.e. streams of renderings and outputs.

    com.squareup.workflow1:workflow-ui-core-common-jvm:x.y.z
    You are writing workflow-ui-android for another UI framework. Defines the core types used by
    that artifact.

### Jetpack Compose support

[Jetpack Compose](https://developer.android.com/jetpack/compose) is the new UI toolkit for Android.
It is comparable to SwiftUI for iOS. The main UI artifacts in this repository support standard
Android Views, but various types of Compose integrations are provided under the
**[compose](/workflow-ui/compose)** folder.

You'll find workflow + compose info and documentation there.

## Resources

* Wondering why to use Workflow? See
  ["Why Workflow"](https://square.github.io/workflow/userguide/whyworkflow/)
* There is a [Glossary of Terms](https://square.github.io/workflow/glossary/)
* We have a [User Guide](https://square.github.io/workflow/userguide/concepts/)
  describing core concepts.
* For Kotlin (and Android), there is a codelab style
  [tutorial](https://github.com/square/workflow-kotlin/tree/main/samples/tutorial) in the repo.
* For Swift (and iOS), there is also a Getting Started
  [tutorial](https://github.com/square/workflow-swift/tree/main/Samples/Tutorial) in the repo.
* There are also a number of
  [Kotlin samples](https://github.com/square/workflow-kotlin/tree/main/samples)
  and [Swift samples](https://github.com/square/workflow-swift/tree/main/Samples).

### Support & Contact

Workflow discussion happens in the Workflow Community slack. Use this [open invitation](https://join.slack.com/t/workflow-community/shared_invite/zt-a2wc0ddx-4bvc1royeZ7yjGqEkW1CsQ).

Workflow maintainers also hang out in the [#squarelibraries](https://kotlinlang.slack.com/messages/C5HT9AL7Q)
channel on the [Kotlin Slack](https://surveys.jetbrains.com/s3/kotlin-slack-sign-up?_ga=2.93235285.916482233.1570572671-654176432.1527183673).

## Releasing and Deploying

See [RELEASING.md](RELEASING.md).

## License

Copyright 2019 Square Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


[中文README](https://github.com/lihenggui/blocker/blob/main/README.zh-CN.md)

## Blocker
[![release](https://img.shields.io/github/v/release/lihenggui/blocker?label=release&color=red)](https://github.com/lihenggui/blocker/releases)
[![download](https://shields.io/github/downloads/lihenggui/blocker/total?label=download)](https://github.com/lihenggui/blocker/releases/latest)
[![translation](https://weblate.sanmer.app/widget/blocker/svg-badge.svg)](https://weblate.sanmer.app/engage/blocker/)
[![follow](https://img.shields.io/badge/follow-Telegram-blue.svg?label=follow)](https://t.me/blockerandroid)
[![license](https://img.shields.io/github/license/lihenggui/blocker)](LICENSE)

Blocker is a component controller for Android applications that currently supports using
PackageManager and Intent Firewall to manage the state of components. For bloated applications, many
components within the application are redundant. Blocker provides a convenient control button to
manage the corresponding components, enabling the disabling of unnecessary functions and saving
application runtime resources.
Blocker can be seamlessly switched between these controllers, and for application rules, you can
export and import them. Additionally, it is compatible with backup files generated by
MyAndroidTools, and you can effortlessly convert them to Intent Firewall rules. The application also
has the potential to be extended in the future.

[](https://f-droid.org/packages/com.merxury.blocker/)
[](https://play.google.com/store/apps/details?id=com.merxury.blocker)

## Advantages
1. Lightweight, don't burden the system
2. Easy to use
3. Supports multiple control types

## Introduction to different component controllers
### Package Manager

The Android system provides a tool called PackageManager, which is used for managing installed
applications or obtaining information about them. One of its
methods, ```setComponentEnabledSetting(ComponentName, int, int)```, allows an application to control
the state of its own components. If attempting to use this method to control components in other
applications, a signature permission is required, or the call will fail.
Fortunately, Android provides another tool called "pm" that allows users to control component states in command-line mode. However, the "pm" tool requires root permission to run. The following command can be used to disable a specific package or component:

```
pm disable [PackageName/ComponmentName]
```

Whether using PackageManager in the code or "pm" in command-line mode, the configurations will be written to ```/data/system/users/0/package_restrictions.xml```.

### Intent Firewall Mode
Intent Firewall was introduced in Android 4.4.2 (API 19) and is still effective in the latest Android systems. It is integrated into the Android Framework to filter the intents sent by applications or systems.

#### What Intent Firewall can do
Each intent sent by an application is filtered by the Intent Firewall, with rules stored in XML files. The Intent Firewall updates rules immediately if changes occur in the configuration file.

#### Limitations of Intent Firewall
Only system applications can read and write the directory where the configuration file is stored, and third-party applications do not have permission to access it.

#### Differences between Intent Firewall and Package Manager
Intent Firewall, indeed it is a firewall, it has no impact on component status. The application detects the component is on, but it just cannot start the component.

For the components disabled by PackageManager, if an application starts it, an exception will be thrown. Developers can catch this exception to know whether the component is disabled or not, so they could re-enable this component. That's the reason why the components will be enabled unexpectedly. If you are using an Intent Firewall controller, there will be no problems.
#### References
[Intent Firewall](https://carteryagemann.com/pages/android-intent-firewall.html)

### Shizuku/Sui Mode
Shizuku is an application developed by Rikka, [RikkaApps/Shizuku](https://github.com/RikkaApps/Shizuku)

Starting from Android O, if we install a Test-Only application, users could use pm command to control the command status. We could modify the install package to set it into Test-Only mode, using APIs provided by Shizuku to control the component status.

Tutorial for modifying APKs (Chinese Only) [[实验性功能] [开发者向]如何免Root控制应用程序组件](https://github.com/lihenggui/blocker/wiki/%5B%E5%AE%9E%E9%AA%8C%E6%80%A7%E5%8A%9F%E8%83%BD%5D-%5B%E5%BC%80%E5%8F%91%E8%80%85%E5%90%91%5D%E5%A6%82%E4%BD%95%E5%85%8DRoot%E6%8E%A7%E5%88%B6%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E7%BB%84%E4%BB%B6)

Please note: For normal applications, the Shell permission in Shizuku mode is not sufficient to
change the switch status of components. In other words, unmodified APKs do not support non-root
modification. If you want to use Shizuku to modify the component status of normal applications,
please start Shizuku with Root privileges.
Implementation of this restriction in
AOSP: [frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java;l=3750;drc=02a77ed61cbeec253a1b49e732d1f27a9ff4b303;bpv=0;bpt=1)

## Screenshot tests

**Blocker** uses [Roborazzi](https://github.com/takahirom/roborazzi) to do screenshot tests
of certain screens and components. To run these tests, run the `verifyRoborazziFossDebug` or
`recordRoborazziFossDebug` tasks. Note that screenshots are recorded on CI, using Linux, and other
platforms might generate slightly different images, making the tests fail.

## Architecture
The architecture of the Blocker app is based on the architecture described in [Now in Android](https://github.com/android/nowinandroid/blob/main/docs/ArchitectureLearningJourney.md), and it also adheres to the [official architecture guidance](https://developer.android.com/topic/architecture).
For information on modularization, please refer to this article: [Modularization Learning Journey](https://github.com/android/nowinandroid/blob/main/docs/ModularizationLearningJourney.md).

## UI
The app was designed using [Material 3 guidelines](https://m3.material.io/). Learn more about the design process and obtain the [design files in Figma](https://www.figma.com/file/T903MNmXtahDVf1yoOgXoI/Blocker).
Huge thanks to our UI designer: [@COPtimer](https://github.com/COPtimer)
The Screens and UI elements are built entirely using Jetpack Compose.

The app has two themes:

* Dynamic color - uses colors based on the user's current color theme (if supported)
* Default theme - uses predefined colors when dynamic color is not supported

Each theme also supports dark mode.

## Translation Contributions
Default string resources for the Blocker app are located here:

`app-compose/src/main/res/values/strings.xml`
`core/[module]/src/main/res/values/strings.xml`
`sync/[module]/src/main/res/values/strings.xml`
`feature/[module]/src/main/res/values/strings.xml`

Translate each and place them in the respective locations ([module]/src/main/res/values-[lang]/strings.xml).

Alternatively, you can use [Weblate](https://weblate.sanmer.app/projects/blocker/) to translate this project. (Thanks [@SanmerDev](https://github.com/SanmerDev) for providing the service)

## Frequently Asked Questions

1. When clicking the button in Shizuku mode, the component state cannot be controlled, and an error
   pops up: SecurityException: Shell cannot change component state for 'xx' to state 'xx'.

* The Shell permission of Shizuku cannot disable unmodified application components. Please restart
  Shizuku with Root privileges or try modifying the APK.

2. What's the difference between the `foss` version and the `market` version?

* You can view the detailed changes in [#619 Build & Check F-Droid version](https://github.com/lihenggui/blocker/pull/619/files). The `foss` version eliminates closed-source libraries such as Firebase and GMS. Conversely, the `market` version integrates Firebase Crashlytics and Firebase Analytics services, enabling the developer to receive crash reports and usage data. This feedback makes it easier for developers to address issues or adjust features based on their usage. The version available on Google Play is the market version. If you prefer not to use analytics services, you can download the `foss` version from the [GitHub release page](https://github.com/lihenggui/blocker/releases) or [F-Droid](https://f-droid.org/packages/com.merxury.blocker/).


# FlorisBoard [![Crowdin](https://badges.crowdin.net/florisboard/localized.svg)](https://crowdin.florisboard.patrickgold.dev) [![Matrix badge](https://img.shields.io/badge/chat-%23florisboard%3amatrix.org-blue)](https://matrix.to/#/#florisboard:matrix.org) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md) ![FlorisBoard CI](https://github.com/florisboard/florisboard/workflows/FlorisBoard%20CI/badge.svg?event=push)

**FlorisBoard** is a free and open-source keyboard for Android 7.0+
devices. It aims at being modern, user-friendly and customizable while
fully respecting your privacy. Currently in early-beta state.

*Note: Due to various reasons development has been quite stuck in the past year, but things are slowly improving again and new releases/features will follow in the near future, please see the [roadmap](ROADMAP.md) for details!*

Stable

Beta

Major versions only

Updates are more polished, new features are matured and tested through to ensure a stable experience.

Alpha/Beta versions

Updates contain new features that may not be fully matured yet and bugs are more likely to occur. Allows you to give early feedback.

**Google Play**: Join the [FlorisBoard Test Group](https://groups.google.com/g/florisboard-public-alpha-test), then visit the [testing page](https://play.google.com/apps/testing/dev.patrickgold.florisboard). Once joined and installed, updates will be delivered like for any other app. ([Store entry](https://play.google.com/store/apps/details?id=dev.patrickgold.florisboard))

**Manual**: Download and install the APK from the release page.

**Google Play**: Join the [FlorisBoard Test Group](https://groups.google.com/g/florisboard-public-alpha-test), then visit the [beta testing page](https://play.google.com/apps/testing/dev.patrickgold.florisboard.beta). Once joined and installed, updates will be delivered like for any other app. ([Store entry](https://play.google.com/store/apps/details?id=dev.patrickgold.florisboard.beta))

**Manual**: Download and install the APK from the release page.

Beginning with v0.4.0 FlorisBoard will follow [SemVer](https://semver.org/#summary) versioning scheme.
Beginning with v0.6.0 FlorisBoard will enter the public beta on Google Play.

## Highlighted features
- Integrated clipboard manager / history
- Advanced theming support and customization
- Integrated extension support (still evolving)
- Emoji keyboard

Word suggestions/spell checking are not included in the current releases and are a major goal for the v0.5.0 milestone.

Feature roadmap: See [ROADMAP.md](ROADMAP.md)

## Contributing
Want to contribute to FlorisBoard? That's great to hear! There are lots of
different ways to help out, please see the [contribution guidelines](CONTRIBUTING.md) for more info.

## List of permissions FlorisBoard requests
Please refer to this [page](https://github.com/florisboard/florisboard/wiki/List-of-permissions-FlorisBoard-requests)
to get more information on this topic.

## Used libraries, components and icons
* [AndroidX libraries](https://github.com/androidx/androidx) by
  [Android Jetpack](https://github.com/androidx)
* [Accompanist Compose UI libraries](https://github.com/google/accompanist/) by
  [Google](https://github.com/google)
* [AboutLibraries](https://github.com/mikepenz/AboutLibraries) by
  [mikepenz](https://github.com/mikepenz)
* [Google Material icons](https://github.com/google/material-design-icons) by
  [Google](https://github.com/google)
* [JetPref preference library](https://github.com/patrickgold/jetpref) by
  [patrickgold](https://github.com/patrickgold)
* [KotlinX coroutines library](https://github.com/Kotlin/kotlinx.coroutines) by
  [Kotlin](https://github.com/Kotlin)
* [KotlinX serialization library](https://github.com/Kotlin/kotlinx.serialization) by
  [Kotlin](https://github.com/Kotlin)
* [ICU4C](https://github.com/unicode-org/icu) by
  [The Unicode Consortium](https://github.com/unicode-org)

Many thanks to [Nikolay Anzarov](https://www.behance.net/nikolayanzarov) ([@BloodRaven0](https://github.com/BloodRaven0)) for designing and providing the main app icons to this project!

## License
```
Copyright 2020-2024 Patrick Goldinger

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```


# Simple

An Android app for recording blood pressure measurements.

## Pre-requisites

The application currently requires JDK 17 to build. If you already have JDK 17 installed, skip this step.

**Check if the right JDK is already available**

Run the command `java -version`. If you have the right version of the JDK installed, you should see something like:

```sh
openjdk 17.0.7 2023-04-18 LTS
OpenJDK Runtime Environment Zulu17.42+19-CA (build 17.0.7+7-LTS)
OpenJDK 64-Bit Server VM Zulu17.42+19-CA (build 17.0.7+7-LTS, mixed mode, sharing)
```

If this command has an error, or shows a different version, you can follow the instructions below to install the JDK.

**Install the JDK**

We recommend using [jEnv](https://www.jenv.be/) to manage your JDK installations. Here are instructions to setup a working JDK 17 installation (macOS
only):

1. Setup up [Homebrew](https://brew.sh/).

2. Install `jEnv` using Homebrew.

```sh
brew install jenv
```

3. Add the following lines to your shell configuration file (`~/.bash_profile` if you're using bash, or `~/.zshrc` if you're using zsh).

```sh
export PATH="$HOME/.jenv/bin:$PATH"
eval "$(jenv init -)"
```

4. Once this is done, you'll need to restart the terminal or reload the configuration file in order for the `jenv` command to be recognised.

```sh
source <path to shell configuration file>
```

5. Install the JDK using Homebrew.

```sh
brew tap mdogan/zulu
brew install zulu-jdk17
```

6. Add the installed JDK to `jEnv`

```sh
jenv add /Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home/
```

7. Run the command `jenv versions`. You should see something like:

```sh
  system
* 17.0
  17.0.7
  zulu64-17.0.7
```

## How to build

**Clone the project using git.**

Run the following command in a terminal.

 ```
 $ git clone git@github.com:simpledotorg/simple-android.git
 ```

**Install Android Studio**

Download and install Android Studio from [their website](https://developer.android.com/studio/).

**Import the project into Android Studio.**

When Android Studio starts up, it will prompt you to create a new project or import an existing project. Select the option to import an existing
project, navigate to the `simple-android` directory you cloned earlier, and select it.

When building for the first time, gradle will download all dependencies so it'll take a few minutes to complete. Subsequent builds will be faster.

If during the build process you see the message:
"Warning: License for package Android SDK Build-Tools 30.0.2 not accepted."
Then you may need to install the Google Play Licensing Library:

* Open the SDK Manager through Tools -> SDK Manager
* Select Appearance & Behavior -> System Settings -> Android SDK in the left sidebar
* Select the SDK Tools tab in the main window
* Activate Google Play Licensing Library and click Apply

## Running locally

The Simple App can be run locally on an Android emulator using Android Studio. To do this,

**Install the NDK library**

The NDK library is currently required by the project to enable an SQLite extension. To install it:

* Open the SDK Manager through Tools -> SDK Manager
* Select Appearance & Behavior -> System Settings -> Android SDK in the left sidebar
* Select the SDK Tools tab in the main window
* Activate NDK (Side by Side) and click Apply

NDK will now be installed.

**Create a Run/Debug configuration**

* Open the Run/Debug configurations window through Run -> Edit Configurations ([ref](https://developer.android.com/studio/run/rundebugconfig))
* Create a new configuration using the `Android App` template
* Set the module to `app`, and finish creating the configuration

**Create a virtual device**

* Create an Android Virtual Device (AVD) using the AVD Manager, usually found in Tools -> AVD
  Manager. ([ref](https://developer.android.com/studio/run/managing-avds))
* Select a device and operating system
* Note: You will have to download one of the available OS options the first time you create an AVD

**Set the right build variant**

* Open the Build Variants window through View -> Tool Windows -> Build Variants, or clicking the item in the lower left corner of the main window
* Set the Build Variant of the app module to `qaDebug`

**Run the app**

* Click "Run", either through Run -> Run, or the green play button in the top toolbar.

## Code styles

The code styles which the project uses have been exported as an IntelliJ code style XML file and are saved as
`quality/code-style.xml`. To import them into Android Studio,

1. Open the Android Studio preferences page, and navigate to Editor -> Code Style.
1. Click on the gear/settings button next to the "Scheme" label.
1. In the drop-down menu, select "Import scheme".
1. In the file picker, navigate to  `<project>/quality/code-style.xml`.
1. Import the `Simple` scheme into the IDE and set it as the project code style.

## Tooling

An Android Studio plugin that provides some quality of life improvements like live templates can be
found [HERE](https://github.com/simpledotorg/simple-android-idea-plugin).

## Building an APK with a different build variant

There are currently 2 ways to build an app pointing to different environments:

1. Changing the `qa` API URL in `gradle.properties` file to point to the environment you want. These builds will be debuggable and require us to clone
   the project and build it using [Android Studio](https://developer.android.com/studio). [*
   Warning*: These changes should not be commited back to `master` branch]
2. Use Bitrise workflows to build APKs of different build variants. These builds will not be debuggable, unless for `build-debuggable-sandbox-apk`.

## Build and deploy Simple Server

Simple Server is in a separate repository, and you should follow
the [instructions there](https://github.com/simpledotorg/simple-server/blob/master/README.md).

## Execute SQL Queries

You can use [Flipper](https://fbflipper.com/) to run SQL queries on Simple:

1. Install Flipper using brew or download from their [website](https://fbflipper.com/).

```sh
brew install Flipper
```

2. Launch Flipper (you might have to allow Flipper to launch from System Preferences > Security > General as it’s from an unknown developer to Apple).
3. Run the Simple app in an emulator or your physical device(as Flipper loads the data from your device's local database).
4. In the Plugins section in the sidebar menu click on Disabled and enable the Database plugin.
5. Click on Databases, select `red-db` and choose whichever table’s data you want to inspect.
6. Click on SQL at the top to execute SQL queries.

## Resources

Check out the following documents for more information.

* [Quirks That You Should Probably Be Aware Of](doc/QUIRKS.md)
* [More Documentation](doc)
* [Recipes](doc/recipes.md)
* [Integration Test Heroku Setup](doc/integration-test-suite-setup.md)


# kaml

[![Pipeline](https://github.com/charleskorn/kaml/actions/workflows/build.yml/badge.svg)](https://github.com/charleskorn/kaml/actions/workflows/build.yml)
[![Coverage](https://img.shields.io/codecov/c/github/charleskorn/kaml.svg)](https://codecov.io/gh/charleskorn/kaml)
[![License](https://img.shields.io/github/license/charleskorn/kaml.svg)](https://opensource.org/licenses/Apache-2.0)
[![Maven Central](https://img.shields.io/maven-central/v/com.charleskorn.kaml/kaml.svg?label=maven%20central)](https://search.maven.org/search?q=g:%22com.charleskorn.kaml%22%20AND%20a:%22kaml%22)

## What is this?

This library adds YAML support to [kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization/).

Currently, only Kotlin/JVM is fully supported.

Kotlin/JS and Kotlin/Wasm support are considered highly experimental. It is not yet fully functional, and may be removed or modified at any time.

(Follow [this issue](https://github.com/charleskorn/kaml/issues/232) for a discussion of adding support for other targets.)

YAML version 1.2 is supported.

## Usage samples

### Parsing from YAML to a Kotlin object

```kotlin
@Serializable
data class Team(
    val leader: String,
    val members: List<String>
)

val input = """
        leader: Amy
        members:
          - Bob
          - Cindy
          - Dan
    """.trimIndent()

val result = Yaml.default.decodeFromString(Team.serializer(), input)

println(result)
```

### Serializing from a Kotlin object to YAML

```kotlin
@Serializable
data class Team(
    val leader: String,
    val members: List<String>
)

val input = Team("Amy", listOf("Bob", "Cindy", "Dan"))

val result = Yaml.default.encodeToString(Team.serializer(), input)

println(result)
```

### Parsing into YamlNode

It is possible to parse a string or an InputStream directly into a YamlNode, for example
the following code prints `Cindy`.
```kotlin
val input = """
        leader: Amy
        members:
          - Bob
          - Cindy
          - Dan
    """.trimIndent()

val result = Yaml.default.parseToYamlNode(input)

println(
    result
        .yamlMap.get<YamlList>("members")!![1]
        .yamlScalar
        .content
)
```

## Referencing kaml

Add the following to your Gradle build script:

**Groovy DSL**

```groovy
plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.4.20'
    id 'org.jetbrains.kotlin.plugin.serialization' version '1.4.20'
}

dependencies {
  implementation "com.charleskorn.kaml:kaml:<version number here>" // Get the latest version number from https://github.com/charleskorn/kaml/releases/latest
}
```

**Kotlin DSL**

```kotlin
plugins {
    kotlin("jvm") version "1.4.20"
    kotlin("plugin.serialization") version "1.4.20"
}

dependencies {
  implementation("com.charleskorn.kaml:kaml:<version number here>") // Get the latest version number from https://github.com/charleskorn/kaml/releases/latest
}
```

Check the [releases page](https://github.com/charleskorn/kaml/releases) for the latest release information,
and the [Maven Central page](https://search.maven.org/artifact/com.charleskorn.kaml/kaml) for examples of how
to reference the library in other build systems.

## Features

* Supports most major YAML features:
  * Scalars, including strings, booleans, integers and floats
  * [Sequences (lists)](https://yaml.org/type/seq.html)
  * [Maps](https://yaml.org/type/map.html)
  * [Nulls](https://yaml.org/type/null.html)
  * [Aliases and anchors](https://yaml.org/spec/1.2/spec.html#id2765878), including [merging aliases to form one map](https://yaml.org/type/merge.html)

* Supports parsing YAML to Kotlin objects (deserializing) and writing Kotlin objects as YAML (serializing)

* Supports [kotlinx.serialization's polymorphism](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md) for sealed and unsealed types

  Two styles are available (set `YamlConfiguration.polymorphismStyle` when creating an instance of `Yaml`):

  * using [YAML tags](https://yaml.org/spec/1.2/spec.html#id2761292) to specify the type:

    ```yaml
    servers:
      - !<frontend>
        hostname: a.mycompany.com
      - !<backend>
        database: db-1
    ```

  * using a `type` property to specify the type:

    ```yaml
    servers:
      - type: frontend
        hostname: a.mycompany.com
      - type: backend
        database: db-1
    ```

  The fragments above could be generated with:

  ```kotlin
  @Serializable
  sealed class Server {
    @SerialName("frontend")
    @Serializable
    data class Frontend(val hostname: String) : Server()

    @SerialName("backend")
    @Serializable
    data class Backend(val database: String) : Server()
  }

  @Serializable
  data class Config(val servers: List<Server>)

  val config = Config(listOf(
    Frontend("a.mycompany.com"),
    Backend("db-1")
  ))

  val result = Yaml.default.encodeToString(Config.serializer(), config)

  println(result)
  ```

* Supports [Docker Compose-style extension fields](https://medium.com/@kinghuang/docker-compose-anchors-aliases-extensions-a1e4105d70bd)

  ```yaml
  x-common-labels: &common-labels
    labels:
      owned-by: myteam@mycompany.com
      cost-centre: myteam

  servers:
    server-a:
      <<: *common-labels
      kind: frontend

    server-b:
      <<: *common-labels
      kind: backend

    # server-b and server-c are equivalent
    server-c:
      labels:
        owned-by: myteam@mycompany.com
        cost-centre: myteam
      kind: backend
  ```

  Specify the extension prefix by setting `YamlConfiguration.extensionDefinitionPrefix` when creating an instance of `Yaml` (eg. `"x-"` for the example above).

  Extensions can only be defined at the top level of a document, and only if the top level element is a map or object. Any key starting with the extension prefix must have an anchor defined (`&...`) and will not be included in the deserialised value.

## Contributing to kaml

Pull requests and bug reports are always welcome!

kaml uses Gradle for builds and testing:

* To build the library: `./gradlew assemble`
* To run the tests and static analysis tools: `./gradlew check`
* To run the tests and static analysis tools continuously: `./gradlew --continuous check`

## Reference links

* [YAML 1.2 Specification](http://yaml.org/spec/1.2/spec.html)
* [snakeyaml-engine](https://bitbucket.org/snakeyaml/snakeyaml-engine/wiki/Home), the YAML parser this library is based on


[![Build Status](https://app.travis-ci.com/theopenconversationkit/tock.png)](https://app.travis-ci.com/github/theopenconversationkit/tock)
[![Maven Central](https://img.shields.io/maven-central/v/ai.tock/tock-root.svg)](https://search.maven.org/search?q=tock)
[![Release Date](https://img.shields.io/github/release-date/theopenconversationkit/tock)](https://github.com/theopenconversationkit/tock/releases)

[![Gitter](https://badges.gitter.im/tockchat/Lobby.svg)](https://gitter.im/tockchat/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge)
[![Contributors](https://img.shields.io/github/contributors-anon/theopenconversationkit/tock)](https://github.com/theopenconversationkit/tock/graphs/contributors)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/theopenconversationkit/tock)](https://github.com/theopenconversationkit/tock/pulse/monthly)

[![Home](https://img.shields.io/website?label=home&down_message=offline&up_message=doc.tock.ai&url=https%3A%2F%2Fdoc.tock.ai)](https://doc.tock.ai)
[![Demo](https://img.shields.io/badge/demo-live-blue)](https://demo.tock.ai)
[![License](https://img.shields.io/github/license/theopenconversationkit/tock)](https://github.com/theopenconversationkit/tock/blob/master/LICENSE)

# Tock - The Open Conversation Kit

_Curious about what Tock is or, who is using it? Check out our [website](https://doc.tock.ai)!_

Open Conversational AI platform to build Bots:

* _Natural Language Processing_ open source stack, compatible with OpenNLP, Stanford, Rasa and more
* _Tock Studio_ user interface to build stories and analytics
* _Conversational DSL_ for Kotlin, Nodejs, Python and REST API
* _Built-in connectors_ for numerous text/voice channels: Messenger, WhatsApp, Google Assistant, Alexa, Twitter and more
* _Provided toolkits_ for custom Web/Mobile integration with React and Flutter
* _Deploy anywhere_ in the Cloud or On-Premise with Docker

🏠 Home: [https://doc.tock.ai](https://doc.tock.ai)

🕮 Documentation: [https://doc.tock.ai/tock](https://doc.tock.ai/tock/index.html)

🐋 Docker configurations: [https://github.com/theopenconversationkit/tock-docker](https://github.com/theopenconversationkit/tock-docker)

▶ Live demo: [https://demo.tock.ai](https://demo.tock.ai)

💬 Contact: [https://gitter.im/tockchat/Lobby](https://gitter.im/tockchat/Lobby)


# Kord

[![Discord](https://img.shields.io/discord/556525343595298817.svg?color=&label=Kord&logo=discord&style=for-the-badge)](https://discord.gg/6jcx5ev)
[![Download](https://img.shields.io/maven-central/v/dev.kord/kord-core.svg?label=Maven%20Central&style=for-the-badge)](https://central.sonatype.com/artifact/dev.kord/kord-core)
[![Github CI status (branch)](https://img.shields.io/github/actions/workflow/status/kordlib/kord/deployment-ci.yml?branch=main&label=CI&style=for-the-badge)]()

Kord is a [coroutine-based](https://kotlinlang.org/docs/reference/coroutines-overview.html), modularized implementation
of the Discord API, written 100% in [Kotlin](https://kotlinlang.org/).

If you have any feedback, we'd love to hear it, hit us up on discord or write up an issue if you have any suggestions!

## Why use Kord

Kord was created as an answer to the frustrations of writing Discord bots with other JVM libraries, which either use
thread-blocking code or verbose and scope restrictive reactive systems. We believe an API written from the ground up in
Kotlin with coroutines can give you the best of both worlds: The conciseness of imperative code with the concurrency of
reactive code.

Aside from coroutines, we also wanted to give the user full access to lower level APIs. Sometimes you have to do some
unconventional things, and we want to allow you to do those in a safe and supported way.

## Status of Kord

* [X] [Discord Gateway](gateway)
* [x] [Discord Rest API](rest)
* [X] [High level abstraction + caching](core)
* [X] [Discord Voice](voice)
* [ ] Support for multiple processes [#7](https://github.com/kordlib/kord/issues/7)

Right now, Kord *should* provide a full mapping of the non-voice API on Kotlin/JVM and Kotlin/JS and an experimental
mapping of the Voice API on Kotlin/JVM

## Documentation

* [Dokka docs](https://kordlib.github.io/kord/)
* [Wiki](https://github.com/kordlib/kord/wiki)

## Modules

| Module                   | Docs                                                    | Artifact          | JVM | JS | Native² |
|--------------------------|---------------------------------------------------------|-------------------|-----|----|---------|
| [common](common)         | [common](https://kordlib.github.io/kord/common)         | `kord-common`¹    | ✅   | ✅  | ❌       |
| [rest](rest)             | [rest](https://kordlib.github.io/kord/rest)             | `kord-rest`¹      | ✅   | ✅  | ❌       |
| [gateway](gateway)       | [gateway](https://kordlib.github.io/kord/gateway)       | `kord-gateway`¹   | ✅   | ✅  | ❌       |
| [core](core)             | [core](https://kordlib.github.io/kord/core)             | `kord-core`¹      | ✅   | ✅  | ❌       |
| [voice](voice)           | [voice](https://kordlib.github.io/kord/voice)           | `kord-voice`      | ✅   | ❌³ | ❌       |
| [core-voice](core-voice) | [core-voice](https://kordlib.github.io/kord/core-voice) | `kord-core-voice` | ✅   | ❌  | ❌       |

¹ These artifacts only supports Gradle Version 5.3 or higher, for older Gradle versions and Maven please append `-jvm`
² For Native Support please see #69
³ For Voice JS please see #69

## Installation

Replace `{version}` with the latest version number on maven central.

For Snapshots replace `{version}` with `{branch}-SNAPSHOT`

e.g: `feature-amazing-thing-SNAPSHOT` for the branch `feature/amazing-thing`

For Snapshots for the branch `main` replace `{version}` with `{nextPlannedVersion}-SNAPSHOT` (see `nextPlannedVersion`
in [`gradle.properties`](gradle.properties))

[![Download](https://img.shields.io/maven-central/v/dev.kord/kord-core.svg?label=Maven%20Central&style=for-the-badge)](https://central.sonatype.com/artifact/dev.kord/kord-core)

### Gradle (Kotlin)

```kotlin
repositories {
    mavenCentral()
    // Kord Snapshots Repository (Optional):
    maven("https://oss.sonatype.org/content/repositories/snapshots")
}

dependencies {
    implementation("dev.kord:kord-core:{version}")
}
```

### Gradle (Groovy)

```groovy
repositories {
    mavenCentral()
    // Kord Snapshots Repository (Optional):
    maven {
        url "https://oss.sonatype.org/content/repositories/snapshots"
    }
}

dependencies {
    implementation("dev.kord:kord-core:{version}")
}
```

### Maven

##### Kord Snapshots Repository (Optional):

```xml

<repository>
    <id>snapshots-repo</id>
    <url>https://oss.sonatype.org/content/repositories/snapshots</url>
    <releases>
        <enabled>false</enabled>
    </releases>
    <snapshots>
        <enabled>true</enabled>
    </snapshots>
</repository>
```

---

```xml

<dependency>
    <groupId>dev.kord</groupId>
    <artifactId>kord-core-jvm</artifactId>
    <version>{version}</version>
</dependency>
```

## FAQ

## Will you support kotlin multi-platform

Currently we're supporting both Kotlin/JVM and Kotlin/JS for the majority of our API, for more information check
[Modules](#modules) and #69

## When will you document your code?

Yes.

# This project is supported by JetBrains

[![JetBrains Logo (Main) logo](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://jb.gg/OpenSourceSupport)


# Unstoppable Wallet

We dream of a world… A world where private property is untouchable and market access is unconditional.

That obsession led us to engineer a crypto wallet that is equally open to all, lives online forever and unconditionally protects your assets. Only the user is in control of the money.

Unstoppable is a powerful non-custodial multi-wallet for Bitcoin, Ethereum, Binance Smart Chain, Avalanche, Solana, Zcash, The Open Network several and other blockchains. It provides non-custodial crypto storage, on-chain decentralized swaps, institutional grade analytics for cryptocurrency markets, extensive privacy controls and human oriented design.

It is built with care and adheres to best programming practices and implementation standards in cryptocurrency world. Fully implemented on Kotlin.

More at [https://unstoppable.money](https://unstoppable.money)

## Supported Android Versions

Devices with Android versions 8.1 and above

## Download

[](https://f-droid.org/packages/io.horizontalsystems.bankwallet/)
[](https://play.google.com/store/apps/details?id=io.horizontalsystems.bankwallet)

## Source Code

[https://github.com/horizontalsystems/unstoppable-wallet-android](https://github.com/horizontalsystems/unstoppable-wallet-android)

## License

This wallet is open source and available under the terms of the MIT License.


[![Travis CI Build Status](https://app.travis-ci.com/EventFahrplan/EventFahrplan.svg?branch=master)](https://app.travis-ci.com/EventFahrplan/EventFahrplan) [![GitHub Actions Build Status](https://github.com/EventFahrplan/EventFahrplan/actions/workflows/build.yaml/badge.svg)](https://github.com/EventFahrplan/EventFahrplan/actions/workflows/build.yaml) [![Crowdin](https://badges.crowdin.net/eventfahrplan/localized.svg)](https://crowdin.com/project/eventfahrplan) [![Apache License](http://img.shields.io/badge/license-Apache%20License%202.0-lightgrey.svg)](http://choosealicense.com/licenses/apache-2.0/)

# EventFahrplan

EventFahrplan is a viewer for event schedules such as
[Chaos Computer Club e.V. events][ccc-events] like [Chaos Communication Camp][camp-website],
[Chaos Communication Congress][congress-website] or [JahresEndVeranstaltungen][jev-website].

- Chaos Communication Camp Schedule app: [F-Droid][camp-app-fdroid], [Google Play][camp-app-google-play]
- Chaos Communication Congress Schedule app: [F-Droid][congress-app-fdroid], [Google Play][congress-app-google-play]
- JahresEndVeranstaltungen Schedule app: [F-Droid][jev-app-fdroid], [Google Play][jev-app-google-play]

The app is available for Android devices.

Bug reports, feature requests can be submitted as an [issue][issues-github].
Please read the instructions in the [contribution guide](CONTRIBUTING.md) in order to contribute to this project.

![Picture of the Camp2023 Schedule on tablet and phone](gfx/EventFahrplan-Camp2023-tablet-phone.png)

## Table of contents

- [Features](#features)
- [Android versions](#android-versions)
- [Event data](#event-data)
- [Build instructions](#build-instructions)
- [Translations](#translations)
- [History](#history)
- [Funding](#funding)
- [Licenses](#licenses)

## Features

* View program by day and rooms (side by side)
* Custom grid layout for smartphones (**try landscape mode**) and tablets
* Read detailed descriptions (speaker names, start time, room name, links, ...) of events
* Add events to favorites list
* Export favorites list
* Setup alarms for individual events
* Add events to your personal calendar
* Share a link to an event with others
* Keep track of program changes
* Automatic program updates (configurable in settings)

### Supported languages
*Event descriptions excluded*
- Danish 🇩🇰
- Dutch 🇳🇱
- English 🇺🇸
- Finnish 🇫🇮
- French 🇫🇷
- German 🇩🇪
- Italian 🇮🇹
- Japanese 🇯🇵
- Lithuanian 🇱🇹
- Polish 🇵🇱
- Portuguese 🇵🇹
- Russian 🇷🇺
- Spanish 🇪🇸
- Swedish 🇸🇪
- Turkish 🇹🇷

### Optional integration

* The feedback system of [Frab][frab-website] and [Pretalx][pretalx-website]
* [c3nav][c3nav-github] - An indoor navigation project
* [Engelsystem][engelsystem-website] - Online tool for coordinating helpers and shifts at large events
* [Chaosflix][chaosflix-github] - Android app for media.ccc.de, share Fahrplan favorites with Chaosflix to import them as bookmarks

## Android versions

The application is designed to work on both smartphones and on tablets.
Android 5.0 (Lollipop) and newer versions are supported.

## Event data

* The app is designed to consume event data published in a [specific format][frab-schedule-xml-spec]
as provided by [Frab][frab-website], [Pretalx][pretalx-website] or [Wafer][wafer-website].
The file format produced by the predecessor software, [Pentabarf][pentabarf-github],
cannot be consumed out of the box.
* In general, it is possible to re-deploy the app for other events which
provide the same event file format.

## Build instructions

To begin development, open the project in Android Studio.

The [customization guide][customization-guide] explains in detail how to create an app for your event.

If you want to create your own signed release builds, copy the `gradle.properties.example` file in the `app` folder
and rename it to `gradle.properties`. This file contains the signing information which is used in the build process.
Be sure to edit the contents of the file to point to your own keystore files.

## Translations

Text translations are managed on [Crowdin][crowdin-eventfahrplan-website]. Contributions are welcome!

Sources (English) and translations are synchronized via the [Crowdin CLI tool][crowdin-cli-tool-website].
German is excluded from being managed on Crowdin as long as the maintainer is a native German speaker.
New languages must be configured in the Crowdin configuration file `crowdin.yaml` before translations
can be downloaded. The file also contains usage examples.

## History

* The project was started in 2011 as ["CampFahrplan"][campfahrplan-github] and was developed
by [Daniel Dorau][tuxmobil-github]. He released the app for the Chaos Communication Camp
and the Chaos Communication Congress in the following years. The app served as a digital
schedule for thousands of users.
* In 2013, [Tobias Preuss][johnjohndoe-github] started contributing. Soon after he
started to re-deploy the app for other events like FOSSGIS, FrOSCon, MRMCD and
other conferences.
* In August 2017 the project moved to a new location and was renamed to
["EventFahrplan"][eventfahrplan-github] to acknowledge its broader use.

## Funding

In 2023 & 2024, this project was funded by the [NGI0 Entrust Fund](https://nlnet.nl/entrust), a fund
established by [NLnet](https://nlnet.nl) with financial support from the European Commission's
[Next Generation Internet](https://ngi.eu) program, under the aegis of DG Communications Networks,
Content and Technology under grant agreement No. 101069594.

![Logo NLnet: abstract logo of four people seen from above](gfx/nlnet-banner-160x60.png)

![Logo NGI Zero: letterlogo shaped like a tag](gfx/ngi0entrust-banner-191x60.png)

## Licenses

Portions Copyright 2008-2011 The K-9 Dog Walkers and 2006-2011 the Android Open Source Project.

```
Copyright 2013-2024 johnjohndoe
Copyright 2011-2015 Daniel Dorau
Contributions from 0x5ubt13, Adriano Pereira Junior, Akarsh Seggemu,
Александр Рознятовский, aligoush, Andrea Marziali, Andy Scherzinger,
Andreas Schildbach, Animesh Verma, bashtian, bjoernb, Björn Olsson Jarl,
ButterflyOfFire, cacarrara, Caio Volpato, Chase, cketti, codingcatgirl,
Dominik Stadler, entropynil, erebion, ideadapt, isi_ko404, IsoLinearCHiP,
Jasper van der Graaf, Joergi, Julius Vitkauskas, koelnkalkverbot, Larissa Yasin,
lepawa, ligi, lucadelu, Luis Azcuaga, María Arias de Reyna, Mateus Baptista,
Matthias Geisler, Matthias Hunstock, Matthias Mair, MichaelRocks, Miguel Beltran,
mtpa, Nghiem Xuan Hien, NiciDieNase, Noemis, Omicron, Poschi, Qwertele,
Róbert Papp, rotrot, Sjors van Mierlo, Stefan Medack, SubOptimal, Su Jade,
taseret, Teeranai.P, Torsten Grote, Victor Herasme, Vladimir Alabov, Yanicka

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```

[c3nav-github]: https://github.com/c3nav
[campfahrplan-github]: https://github.com/tuxmobil/CampFahrplan
[camp-app-fdroid]: https://f-droid.org/packages/info.metadude.android.cccamp.schedule
[camp-app-google-play]: https://play.google.com/store/apps/details?id=info.metadude.android.cccamp.schedule
[camp-website]: https://events.ccc.de/camp/
[ccc-events]: http://events.ccc.de
[chaosflix-github]: https://github.com/NiciDieNase/chaosflix
[congress-app-fdroid]: https://f-droid.org/packages/info.metadude.android.congress.schedule
[congress-app-google-play]: https://play.google.com/store/apps/details?id=info.metadude.android.congress.schedule
[congress-website]: https://events.ccc.de/congress/
[customization-guide]: docs/CUSTOMIZING.md
[crowdin-eventfahrplan-website]: https://crowdin.com/project/eventfahrplan
[crowdin-cli-tool-website]: https://crowdin.github.io/crowdin-cli/
[engelsystem-website]: https://engelsystem.de
[eventfahrplan-github]: https://github.com/EventFahrplan/EventFahrplan
[frab-schedule-xml-spec]: https://github.com/voc/schedule/tree/master/validator/xsd
[frab-website]: https://frab.github.io/frab/
[issues-github]: https://github.com/EventFahrplan/EventFahrplan/issues
[johnjohndoe-github]: https://github.com/johnjohndoe
[pentabarf-github]: https://github.com/nevs/pentabarf
[pretalx-website]: https://pretalx.com
[jev-app-fdroid]: https://f-droid.org/packages/info.metadude.android.rc3.schedule
[jev-app-google-play]: https://play.google.com/store/apps/details?id=info.metadude.android.rc3.schedule
[jev-website]: https://events.ccc.de/2022/11/28/dezentral-2022/
[tuxmobil-github]: https://github.com/tuxmobil
[wafer-website]: https://wafer.readthedocs.io