Repository: thefuntasty/hauler
Branch: master
Commit: 2c7cf8eaeb34
Files: 68
Total size: 69.6 KB
Directory structure:
gitextract_1jcvn5xr/
├── .editorconfig
├── .github/
│ └── workflows/
│ ├── publish_release.yml
│ ├── publish_snapshot.yml
│ └── pull_request.yml
├── .gitignore
├── Dangerfile
├── Gemfile
├── LICENSE
├── README.md
├── build.gradle.kts
├── buildSrc/
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ └── kotlin/
│ ├── Deps.kt
│ ├── ProjectSettings.kt
│ ├── Versions.kt
│ └── app/
│ └── futured/
│ └── hauler/
│ └── DependencyUpdates.kt
├── core/
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── gradle.properties
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── kotlin/
│ │ └── app/
│ │ └── futured/
│ │ └── hauler/
│ │ ├── ColorUtils.kt
│ │ ├── DragDirection.kt
│ │ ├── HaulerView.kt
│ │ ├── HaulerViewExtensions.kt
│ │ ├── LockableNestedScrollView.kt
│ │ ├── OnDragActivityListener.kt
│ │ ├── OnDragDismissedListener.kt
│ │ └── SystemBarsFader.kt
│ └── res/
│ └── values/
│ ├── attrs_hauler_view.xml
│ └── dimens.xml
├── databinding/
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── gradle.properties
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── kotlin/
│ └── app/
│ └── futured/
│ └── hauler/
│ └── databinding/
│ ├── IsDragEnabledAdapter.kt
│ ├── IsDragUpEnabledAdapter.kt
│ ├── IsScrollableAdapter.kt
│ └── OnDragDismissedListenerAdapter.kt
├── detekt.yml
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── sample/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── kotlin/
│ │ └── app/
│ │ └── futured/
│ │ └── haulersample/
│ │ ├── MainActivity.kt
│ │ └── draggable/
│ │ ├── SimpleActivity.kt
│ │ ├── SimpleJavaActivity.java
│ │ ├── advanced/
│ │ │ ├── AdvancedActivity.kt
│ │ │ └── IgnoredAreaView.kt
│ │ └── databinding/
│ │ ├── DatabindingActivity.kt
│ │ ├── DatabindingActivityState.kt
│ │ └── DatabindingActivityView.kt
│ └── res/
│ ├── anim/
│ │ ├── anim_slide_down.xml
│ │ └── anim_slide_up.xml
│ ├── drawable/
│ │ └── ic_launcher_background.xml
│ ├── drawable-v24/
│ │ └── ic_launcher_foreground.xml
│ ├── layout/
│ │ ├── activity_advanced.xml
│ │ ├── activity_databinding.xml
│ │ ├── activity_main.xml
│ │ └── activity_simple.xml
│ ├── mipmap-anydpi-v26/
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ └── values/
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
└── settings.gradle.kts
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
[*.{kt,kts}]
max_line_length=130
================================================
FILE: .github/workflows/publish_release.yml
================================================
name: Publish Release
on:
release:
types: [published]
jobs:
release:
name: Release Publish
runs-on: [ubuntu-latest]
steps:
- uses: actions/checkout@v1
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
distribution: 'zulu'
java-version: '11'
- name: Build & run unit tests
shell: bash
run: ./gradlew --continue build testRelease
- name: Publish release
run: ./gradlew publish --no-daemon --no-parallel --stacktrace -PVERSION_NAME=${{github.event.release.name}}
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_REPOSITORY_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_REPOSITORY_PASSWORD }}
ORG_GRADLE_PROJECT_SIGNING_PRIVATE_KEY: ${{ secrets.GPG_SIGNING_PRIVATE_KEY }}
ORG_GRADLE_PROJECT_SIGNING_PASSWORD: ${{ secrets.GPG_SIGNING_PASSWORD }}
================================================
FILE: .github/workflows/publish_snapshot.yml
================================================
name: Publish Snapshot
on:
push:
branches:
master
jobs:
master:
name: Snapshot Publish
runs-on: [ ubuntu-latest ]
env:
SLACK_CHANNEL: android
steps:
- uses: actions/checkout@v1
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
distribution: 'zulu'
java-version: '11'
- name: Run unit tests
shell: bash
run: ./gradlew --continue build testRelease
- name: Build & publish snapshot
run: ./gradlew publish --no-daemon --no-parallel --stacktrace
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_REPOSITORY_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_REPOSITORY_PASSWORD }}
ORG_GRADLE_PROJECT_SIGNING_PRIVATE_KEY: ${{ secrets.GPG_SIGNING_PRIVATE_KEY }}
ORG_GRADLE_PROJECT_SIGNING_PASSWORD: ${{ secrets.GPG_SIGNING_PASSWORD }}
- name: Slack Notification
if: failure()
uses: homoluctus/slatify@master
with:
type: "failure"
job_name: '*Snapshot Publish*'
username: GitHub
channel: ${{env.SLACK_CHANNEL}}
url: ${{ secrets.SLACK_WEB_HOOK }}
commit: true
token: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/pull_request.yml
================================================
name: Check PR
on: [pull_request]
jobs:
pr:
name: PR check
runs-on: [ubuntu-latest]
env:
DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
steps:
- uses: actions/checkout@v1
- uses: actions/setup-ruby@v1
with:
ruby-version: '2.6'
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
distribution: 'zulu'
java-version: '11'
- name: Run LintCheck
shell: bash
run: ./gradlew detekt ktlintCheck lint assembleRelease
- name: Run unit tests
shell: bash
run: ./gradlew --continue testRelease
- name: Danger action
uses: MeilCli/danger-action@v2
continue-on-error: true
with:
plugins_file: 'Gemfile'
danger_file: 'Dangerfile'
danger_id: 'danger-pr'
================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/*
/buildSrc/build/*
.DS_Store
/build
/captures
.externalNativeBuild
================================================
FILE: Dangerfile
================================================
is_pr_big = git.insertions > 500
has_correct_prefix = github.branch_for_head.match(/^(feature|hotfix|fix|release|housekeep)\//)
warn("Branch name should have `release/`, `hotfix/`, `fix/`, `housekeep/` or `feature/` prefix.") if !has_correct_prefix
warn("This pull request is too big.") if is_pr_big
commit_lint.check warn: :all, disable: [:subject_length]
# Utils
def report_checkstyle_for_directory(directory_name)
if Dir.exists?(directory_name)
Dir.glob("#{directory_name}/*.xml").each {|f|
report_checkstyle(f)
}
end
end
def report_checkstyle(file_name)
if File.file?(file_name)
checkstyle_format.report file_name
end
end
# Setup checkstyle
checkstyle_format.base_path = Dir.pwd
# Detekt checkstyle
report_checkstyle 'build/reports/detekt/detekt.xml'
# Ktlint checkstyle
report_checkstyle_for_directory 'library/build/reports/ktlint'
report_checkstyle_for_directory 'sample/build/reports/ktlint'
================================================
FILE: Gemfile
================================================
source 'https://rubygems.org'
gem 'danger-commit_lint'
gem 'danger-checkstyle_format'
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020 Futured apps s.r.o.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<img align="right" src="images/Hauler.svg">
# Hauler
[](https://search.maven.org/artifact/app.futured.hauler/hauler/)

[]( https://android-arsenal.com/details/1/7359 )


Hauler is an Android library containing custom layout which enables to easily create swipe to dismiss `Activity`.
Implementation is based on code from project [Plaid](https://github.com/nickbutcher/plaid).

# Installation
```groovy
dependencies {
implementation("app.futured.hauler:hauler:latestVersion")
// optional dependency with set of Data Binding adapters
implementation("app.futured.hauler:databinding:latestVersion")
}
```
### Snapshot installation
Add new maven repo to your top level gradle file.
```groovy
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
```
Snapshots are grouped based on major version, so for version 5.x.x use:
```groovy
implementation "app.futured.hauler:hauler:5.X.X-SNAPSHOT"
```
# Features
Hauler library comes with highly customizable `HaulerView` which provides swipe to dismiss functionality.
It also ships with `databinding` module which contains Binding Adapters for smoother experience with Android Data Binding implementation.
# Usage
Activity which is meant to be dismissed must contain `HaulerView` as a root view and `NestedScrollView` (or other `View` what supports nested scroll)
as its child. Make sure your `NestedScrollview`'s attribute `android:fillViewport` is set to `true` otherwise it might not behave as
expected:
```xml
<app.futured.hauler.HaulerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/haulerView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<!-- your layout-->
</androidx.core.widget.NestedScrollView>
</app.futured.hauler.HaulerView>
```
Secondly, define translucent floating Theme and assign it to the Activity you want to give dismiss ability:
```xml
<style name="AppTheme.Draggable" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowIsFloating">false</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@color/dark_gray</item>
</style>
```
```xml
<activity
android:name=".draggable.SimpleUsageActivity"
android:theme="@style/AppTheme.Draggable"/>
```
Set `onDragDismissListener` to react properly to user dismiss request. Example implementation might look like this:
```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
// ...
haulerView.setOnDragDismissedListener {
finish() // finish activity when dismissed
}
}
```
## Customization
There are few styleable attributes you might want to use to customize your `HaulerView`:
```xml
<app.futured.hauler.HaulerView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:dragDismissDistance="112dp"
app:dragDismissFraction="0.9"
app:dragElasticity="0.7"
app:dragDismissScale="0.95"/>
```
| Attribute name | Type | Default value | Description|
| -------------- | ---- | ------------- | ---------- |
| `app:dragDismissDistance` | dimen | 100dp | Distance which should be `View` swiped to consider Activity as dismissed |
| `app:dragDismissFraction` | float | unspecified | `<0;1>` - Fraction of `View`'s height we should reach swiping to consider Activity as dismissed |
| `app:dragElasticity` | float | 0.8 | `<0;1>` - Toughness of swipe. Higher value indicates more rigid feeling |
| `app:dragDismissScale` | float | 0.95 | `<0;1>` - Scale factor of `View` while performing swipe action |
| `app:dragUpEnabled` | boolean | false | Flag indicating if drag up dismiss gesture is enabled |
| `app:fadeSystemBars` | boolean | true | Flag indicating if system bars (status & navigation) fades while dismiss is in progress |
Attributes `dragDismissDistance` and `dragDismissFraction` are exclusive. Do not use them together.
# License
Hauler is available under the MIT license. See the [LICENSE file](LICENCE) for more information.
================================================
FILE: build.gradle.kts
================================================
import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath(Deps.gradlePlugin)
classpath(kotlin(Deps.Kotlin.gradlePlugin, Versions.kotlin))
classpath(Deps.Plugins.mavenPublish)
classpath(Deps.Plugins.dokka)
}
}
plugins {
idea
id(Deps.Plugins.detekt) version Versions.detekt
id(Deps.Plugins.ktlint) version Versions.ktlint
}
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}
tasks {
register<app.futured.hauler.DependencyUpdates>("dependencyUpdates")
}
subprojects {
apply(plugin = Deps.Plugins.ktlint)
ktlint {
version.set(Versions.ktlintExtension)
ignoreFailures.set(true)
android.set(true)
outputToConsole.set(true)
reporters {
reporter(ReporterType.PLAIN)
reporter(ReporterType.CHECKSTYLE)
}
}
plugins.whenPluginAdded {
if (this is SigningPlugin) {
extensions.findByType<SigningExtension>()?.apply {
val hasKey = project.hasProperty("SIGNING_PRIVATE_KEY")
val hasPassword = project.hasProperty("SIGNING_PASSWORD")
if (hasKey && hasPassword) {
useInMemoryPgpKeys(
project.properties["SIGNING_PRIVATE_KEY"].toString(),
project.properties["SIGNING_PASSWORD"].toString()
)
}
}
}
}
}
detekt {
autoCorrect = false
version = Versions.detekt
input = files("sample/src/main/kotlin", "library/src/main/kotlin")
config = files("detekt.yml")
}
================================================
FILE: buildSrc/build.gradle.kts
================================================
plugins {
`kotlin-dsl`
}
repositories {
jcenter()
}
dependencies {
implementation("com.github.ben-manes:gradle-versions-plugin:0.36.0")
}
dependencies {
implementation("com.github.ben-manes:gradle-versions-plugin:0.33.0")
}
================================================
FILE: buildSrc/src/main/kotlin/Deps.kt
================================================
object Deps {
const val gradlePlugin = "com.android.tools.build:gradle:${Versions.gradle}"
object Plugins {
const val detekt = "io.gitlab.arturbosch.detekt"
const val ktlint = "org.jlleitschuh.gradle.ktlint"
const val mavenPublish = "com.vanniktech:gradle-maven-publish-plugin:${Versions.mavenPublish}"
const val dokka = "org.jetbrains.dokka:dokka-gradle-plugin:${Versions.dokka}"
}
object Kotlin {
const val gradlePlugin = "gradle-plugin"
const val stdlib = "stdlib-jdk7"
}
object AndroidX {
const val appcompat = "androidx.appcompat:appcompat:${Versions.androidx}"
const val palette = "androidx.palette:palette:${Versions.palette}"
const val ktx = "androidx.core:core-ktx:${Versions.androidxKtx}"
}
}
================================================
FILE: buildSrc/src/main/kotlin/ProjectSettings.kt
================================================
object ProjectSettings {
const val applicationId = "app.futured.hauler"
const val targetSdk = 32
const val minSdk = 21
}
================================================
FILE: buildSrc/src/main/kotlin/Versions.kt
================================================
object Versions {
// gradle
const val gradle = "7.2.1"
// plugins
const val detekt = "1.20.0"
const val ktlint = "10.3.0"
const val ktlintExtension = "0.41.0"
const val mavenPublish = "0.21.0"
const val dokka = "1.7.0"
// kotlin
const val kotlin = "1.7.0"
const val androidx = "1.4.2"
const val androidxKtx = "1.8.0"
const val palette = "1.0.0"
}
================================================
FILE: buildSrc/src/main/kotlin/app/futured/hauler/DependencyUpdates.kt
================================================
package app.futured.hauler
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
abstract class DependencyUpdates : DependencyUpdatesTask() {
init {
group = "futured"
this.resolutionStrategy {
componentSelection {
all {
val rejected = listOf("alpha", "beta", "rc", "cr", "m", "preview", "testing")
.map { qualifier -> Regex("(?i).*[.-]$qualifier[.\\d-]*") }
.any { it.matches(candidate.version) }
if (rejected) {
reject("Release candidate")
}
}
}
}
}
}
================================================
FILE: core/.gitignore
================================================
/build
================================================
FILE: core/build.gradle.kts
================================================
import org.jetbrains.kotlin.config.KotlinCompilerVersion
plugins {
id("com.android.library")
id("kotlin-android")
id("com.vanniktech.maven.publish")
}
android {
compileSdkVersion(ProjectSettings.targetSdk)
defaultConfig {
minSdkVersion(ProjectSettings.minSdk)
targetSdkVersion(ProjectSettings.targetSdk)
}
sourceSets {
getByName("main").java.srcDir("src/main/kotlin")
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
}
dependencies {
implementation(kotlin(Deps.Kotlin.stdlib, KotlinCompilerVersion.VERSION))
implementation(Deps.AndroidX.appcompat)
implementation(Deps.AndroidX.palette)
implementation(Deps.AndroidX.ktx)
}
================================================
FILE: core/gradle.properties
================================================
POM_NAME=Library containing custom layout which enables to easily create swipe to dismiss Activity
POM_ARTIFACT_ID=hauler
POM_PACKAGING=aar
================================================
FILE: core/src/main/AndroidManifest.xml
================================================
<manifest package="app.futured.hauler"/>
================================================
FILE: core/src/main/kotlin/app/futured/hauler/ColorUtils.kt
================================================
package app.futured.hauler
import androidx.annotation.CheckResult
import androidx.annotation.ColorInt
import androidx.annotation.IntRange
internal object ColorUtils {
@CheckResult
@ColorInt
fun modifyAlpha(@ColorInt color: Int, @IntRange(from = 0, to = 255) alpha: Int): Int =
color and 0x00ffffff or (alpha shl 24)
}
================================================
FILE: core/src/main/kotlin/app/futured/hauler/DragDirection.kt
================================================
package app.futured.hauler
enum class DragDirection {
UP, DOWN
}
================================================
FILE: core/src/main/kotlin/app/futured/hauler/HaulerView.kt
================================================
package app.futured.hauler
import android.app.Activity
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.widget.FrameLayout
import androidx.core.content.withStyledAttributes
import androidx.core.view.animation.PathInterpolatorCompat
class HaulerView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
// configurable attributes
private var dragDismissDistance =
context.resources.getDimensionPixelSize(R.dimen.default_drag_dismiss_distance).toFloat()
private var dragDismissFraction = -1f
private var dragDismissScale = 0.95f
private var shouldScale = true
private var dragElasticity = 0.8f
// state
private var totalDrag: Float = 0.toFloat()
private var draggingDown = false
private var draggingUp = false
private var mLastActionEvent: Int = 0
private var onDragDismissedListener: OnDragDismissedListener? = null
private var onDragActivityListener: OnDragActivityListener? = null
private var systemBarsFader: SystemBarsFader? = null
private var isDragEnabled = true
private var dragUpEnabled = false
private var fadeSystemBars = true
init {
getContext().withStyledAttributes(set = attrs, attrs = R.styleable.HaulerView) {
val distanceAvailable = hasValue(R.styleable.HaulerView_dragDismissDistance)
val dismissFractionAvailable = hasValue(R.styleable.HaulerView_dragDismissFraction)
if (distanceAvailable && dismissFractionAvailable) {
throw IllegalStateException("Do not specify both dragDismissDistance and dragDismissFraction. Choose one.")
} else if (distanceAvailable) {
dragDismissDistance = getDimensionPixelSize(R.styleable.HaulerView_dragDismissDistance, 0).toFloat()
} else if (dismissFractionAvailable) {
dragDismissFraction = getFloat(R.styleable.HaulerView_dragDismissFraction, dragDismissFraction)
}
dragDismissScale = getFloat(R.styleable.HaulerView_dragDismissScale, dragDismissScale)
dragUpEnabled = getBoolean(R.styleable.HaulerView_dragUpEnabled, dragUpEnabled)
dragElasticity = getFloat(R.styleable.HaulerView_dragElasticity, dragElasticity)
fadeSystemBars = getBoolean(R.styleable.HaulerView_fadeSystemBars, fadeSystemBars)
}
setFadeSystemBars(fadeSystemBars)
shouldScale = dragDismissScale != 1f
}
override fun onStartNestedScroll(child: View, target: View, nestedScrollAxes: Int): Boolean =
nestedScrollAxes and View.SCROLL_AXIS_VERTICAL != 0
override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray) {
if (isDragEnabled.not()) {
return super.onNestedPreScroll(target, dx, dy, consumed)
}
// if we're in a drag gesture and the user reverses up the we should take those events
val draggingDownInProgress = draggingDown && dy > 0
val draggingUpInProgress = draggingUp && dy < 0
if (draggingDownInProgress || draggingUpInProgress) {
dragScale(dy)
consumed[1] = dy
}
}
override fun onNestedScroll(target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int) {
if (isDragEnabled.not() || (dragUpEnabled.not() && dyUnconsumed > 0)) {
return super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed)
}
dragScale(dyUnconsumed)
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
mLastActionEvent = ev.action
return super.onInterceptTouchEvent(ev)
}
override fun onStopNestedScroll(child: View) {
if (isDragEnabled.not()) {
return super.onStopNestedScroll(child)
}
val totalDragNormalized = if (dragUpEnabled) Math.abs(totalDrag) else -totalDrag
val dragDirection = if (totalDrag > 0) DragDirection.UP else DragDirection.DOWN
if (totalDragNormalized >= dragDismissDistance) {
dispatchDismissCallback(dragDirection)
} else { // settle back to natural position
if (mLastActionEvent == MotionEvent.ACTION_DOWN) {
// this is a 'defensive cleanup for new gestures',
// don't animate here
// see also https://github.com/nickbutcher/plaid/issues/185
translationY = 0f
scaleX = 1f
scaleY = 1f
} else {
animate()
.translationY(0f)
.scaleX(1f)
.scaleY(1f)
.setDuration(200L)
.setInterpolator(PathInterpolatorCompat.create(0.4f, 0f, 0.2f, 1f))
.setListener(null)
.start()
}
totalDrag = 0f
draggingUp = false
draggingDown = draggingUp
dispatchDragCallback(0f, 0f)
}
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
if (dragDismissFraction > 0f) {
dragDismissDistance = h * dragDismissFraction
}
}
/**
* Set if drag/swipe up dismiss is enabled
*/
fun setDragUpEnabled(dragUpEnabled: Boolean) {
this.dragUpEnabled = dragUpEnabled
}
/**
* Set lambda reference which is called when dismiss gesture has
* been performed
*/
fun setOnDragDismissedListener(onDragDismissedListener: OnDragDismissedListener) {
this.onDragDismissedListener = onDragDismissedListener
}
/**
* Set lambda reference to listener which is called when drag is in
* progress
*/
fun setOnDragActivityListener(onDragActivityListener: OnDragActivityListener) {
this.onDragActivityListener = onDragActivityListener
}
/**
* Set if drag gesture is enabled
*/
fun setDragEnabled(isDragEnabled: Boolean) {
this.isDragEnabled = isDragEnabled
}
/**
* Set if system bars should fade when dismiss is in progress
*/
fun setFadeSystemBars(fadeSystemBars: Boolean) {
this.fadeSystemBars = fadeSystemBars
if (fadeSystemBars) {
(context as? Activity)?.also {
systemBarsFader = SystemBarsFader(it)
}
} else {
systemBarsFader = null
}
}
private fun dragScale(scroll: Int) {
if (scroll == 0) return
totalDrag += scroll.toFloat()
// track the direction & set the pivot point for scaling
// don't double track i.e. if start dragging down and then reverse, keep tracking as
// dragging down until they reach the 'natural' position
if (scroll < 0 && !draggingUp && !draggingDown) {
draggingDown = true
if (shouldScale) pivotY = height.toFloat()
} else if (scroll > 0 && !draggingDown && !draggingUp) {
draggingUp = true
if (shouldScale) {
pivotY = 0f
}
}
// how far have we dragged relative to the distance to perform a dismiss
// (0–1 where 1 = dismiss distance). Decreasing logarithmically as we approach the limit
var dragFraction = Math.log10((1 + Math.abs(totalDrag) / dragDismissDistance).toDouble()).toFloat()
// calculate the desired translation given the drag fraction
var dragTo = dragFraction * dragDismissDistance * dragElasticity
if (draggingUp) {
// as we use the absolute magnitude when calculating the drag fraction, need to
// re-apply the drag direction
dragTo *= -1f
}
translationY = dragTo
if (shouldScale) {
val scale = 1 - (1 - dragDismissScale) * dragFraction
scaleX = scale
scaleY = scale
}
// if we've reversed direction and gone past the settle point then clear the flags to
// allow the list to get the scroll events & reset any transforms
val downSettlePointReached = draggingDown && totalDrag >= 0
val upSettlePointReached = draggingUp && totalDrag <= 0
if (downSettlePointReached || upSettlePointReached) {
dragFraction = 0f
dragTo = dragFraction
totalDrag = dragTo
draggingUp = false
draggingDown = draggingUp
translationY = 0f
scaleX = 1f
scaleY = 1f
}
dispatchDragCallback(dragTo, Math.min(1f, Math.abs(totalDrag) / dragDismissDistance))
}
private fun dispatchDragCallback(elasticOffsetPixels: Float, rawOffset: Float) {
systemBarsFader?.onDrag(elasticOffsetPixels, rawOffset)
onDragActivityListener?.onDrag(elasticOffsetPixels, rawOffset)
}
private fun dispatchDismissCallback(dragDirection: DragDirection) {
systemBarsFader?.onDismiss()
onDragDismissedListener?.onDismissed(dragDirection)
}
}
================================================
FILE: core/src/main/kotlin/app/futured/hauler/HaulerViewExtensions.kt
================================================
package app.futured.hauler
fun HaulerView.setOnDragDismissedListener(onDragDismissedListener: (DragDirection) -> Unit) {
this.setOnDragDismissedListener(object : OnDragDismissedListener {
override fun onDismissed(dragDirection: DragDirection) {
onDragDismissedListener.invoke(dragDirection)
}
})
}
fun HaulerView.setOnDragActivityListener(onDragActivityListener: (elasticOffsetPixels: Float, rawOffset: Float) -> Unit) {
this.setOnDragActivityListener(object : OnDragActivityListener {
override fun onDrag(elasticOffsetPixels: Float, rawOffset: Float) {
onDragActivityListener.invoke(elasticOffsetPixels, rawOffset)
}
})
}
================================================
FILE: core/src/main/kotlin/app/futured/hauler/LockableNestedScrollView.kt
================================================
package app.futured.hauler
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.core.widget.NestedScrollView
class LockableNestedScrollView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : NestedScrollView(context, attrs, defStyleAttr) {
private var isScrollable = true
fun setScrollEnabled(isScrollEnabled: Boolean) {
isScrollable = isScrollEnabled
}
@Suppress("ClickableViewAccessibility")
override fun onTouchEvent(ev: MotionEvent): Boolean = when (ev.action) {
MotionEvent.ACTION_DOWN ->
isScrollable && super.onTouchEvent(ev)
else ->
super.onTouchEvent(ev)
}
override fun onInterceptTouchEvent(ev: MotionEvent) = isScrollable && super.onInterceptTouchEvent(ev)
}
================================================
FILE: core/src/main/kotlin/app/futured/hauler/OnDragActivityListener.kt
================================================
package app.futured.hauler
interface OnDragActivityListener {
fun onDrag(elasticOffsetPixels: Float, rawOffset: Float)
}
================================================
FILE: core/src/main/kotlin/app/futured/hauler/OnDragDismissedListener.kt
================================================
package app.futured.hauler
interface OnDragDismissedListener {
fun onDismissed(dragDirection: DragDirection)
}
================================================
FILE: core/src/main/kotlin/app/futured/hauler/SystemBarsFader.kt
================================================
package app.futured.hauler
import android.app.Activity
import android.graphics.Color
internal class SystemBarsFader(private val activity: Activity) {
private val statusBarAlpha: Int by lazy { Color.alpha(getStatusBarColor()) }
fun onDrag(elasticOffsetPixels: Float, rawOffset: Float) {
when {
elasticOffsetPixels != 0f -> // dragging downward or upward, fade the status bar in proportion
activity.window.statusBarColor = ColorUtils.modifyAlpha(getStatusBarColor(), getNewAlpha(rawOffset))
elasticOffsetPixels == 0f ->
activity.window.statusBarColor = ColorUtils.modifyAlpha(getStatusBarColor(), statusBarAlpha)
}
}
fun onDismiss() {
// set transparent window bg and transparent navigation bar
activity.window.decorView.setBackgroundColor(0)
activity.window.navigationBarColor = ColorUtils.modifyAlpha(activity.window.navigationBarColor, 0)
}
private fun getStatusBarColor() = activity.window.statusBarColor
private fun getNewAlpha(rawOffset: Float) = ((1f - rawOffset) * statusBarAlpha).toInt()
}
================================================
FILE: core/src/main/res/values/attrs_hauler_view.xml
================================================
<resources>
<declare-styleable name="HaulerView">
<attr name="dragDismissDistance" format="dimension"/>
<attr name="dragDismissFraction" format="float"/>
<attr name="dragDismissScale" format="float"/>
<attr name="dragElasticity" format="float"/>
<attr name="dragUpEnabled" format="boolean"/>
<attr name="fadeSystemBars" format="boolean" />
</declare-styleable>
</resources>
================================================
FILE: core/src/main/res/values/dimens.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="default_drag_dismiss_distance">100dp</dimen>
</resources>
================================================
FILE: databinding/.gitignore
================================================
/build
================================================
FILE: databinding/build.gradle.kts
================================================
import org.jetbrains.kotlin.config.KotlinCompilerVersion
plugins {
id("com.android.library")
id("kotlin-android")
id("kotlin-kapt")
id("com.vanniktech.maven.publish")
}
android {
compileSdkVersion(ProjectSettings.targetSdk)
defaultConfig {
minSdkVersion(ProjectSettings.minSdk)
targetSdkVersion(ProjectSettings.targetSdk)
}
sourceSets {
getByName("main").java.srcDir("src/main/kotlin")
}
buildFeatures {
dataBinding = true
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
}
dependencies {
implementation(project(":core"))
implementation(kotlin(Deps.Kotlin.stdlib, KotlinCompilerVersion.VERSION))
implementation(Deps.AndroidX.appcompat)
}
================================================
FILE: databinding/gradle.properties
================================================
POM_NAME=Databinding extensions for core library
POM_ARTIFACT_ID=databinding
POM_PACKAGING=aar
================================================
FILE: databinding/src/main/AndroidManifest.xml
================================================
<manifest package="app.futured.hauler.databinding"/>
================================================
FILE: databinding/src/main/kotlin/app/futured/hauler/databinding/IsDragEnabledAdapter.kt
================================================
package app.futured.hauler.databinding
import androidx.databinding.BindingAdapter
import app.futured.hauler.HaulerView
@BindingAdapter("app:isDragEnabled")
fun HaulerView.isDragEnabled(isDragEnabled: Boolean) {
this.setDragEnabled(isDragEnabled)
}
================================================
FILE: databinding/src/main/kotlin/app/futured/hauler/databinding/IsDragUpEnabledAdapter.kt
================================================
package app.futured.hauler.databinding
import androidx.databinding.BindingAdapter
import app.futured.hauler.HaulerView
@BindingAdapter("app:isDragUpEnabled")
fun HaulerView.isDragUpEnabled(isDragUpEnabled: Boolean) {
this.setDragUpEnabled(isDragUpEnabled)
}
================================================
FILE: databinding/src/main/kotlin/app/futured/hauler/databinding/IsScrollableAdapter.kt
================================================
package app.futured.hauler.databinding
import androidx.databinding.BindingAdapter
import app.futured.hauler.LockableNestedScrollView
@BindingAdapter("app:isScrollable")
fun LockableNestedScrollView.isScrollable(isScrollable: Boolean) {
setScrollEnabled(isScrollable)
}
================================================
FILE: databinding/src/main/kotlin/app/futured/hauler/databinding/OnDragDismissedListenerAdapter.kt
================================================
package app.futured.hauler.databinding
import androidx.databinding.BindingAdapter
import app.futured.hauler.HaulerView
import app.futured.hauler.OnDragDismissedListener
import app.futured.hauler.setOnDragDismissedListener
@BindingAdapter("app:onDragDismissedListener")
fun HaulerView.setOnDragDismissedListener(listener: OnDragDismissedListener) {
this.setOnDragDismissedListener { listener.onDismissed(it) }
}
================================================
FILE: detekt.yml
================================================
build:
maxIssues: 100
complexity:
active: true
ComplexCondition:
active: true
ComplexMethod:
active: true
threshold: 20
ignoreSimpleWhenEntries: true
LargeClass:
threshold: 250
active: true
TooManyFunctions:
active: true
thresholdInFiles: 30
thresholdInClasses: 30
thresholdInInterfaces: 30
thresholdInObjects: 25
LongParameterList:
active: true
NestedBlockDepth:
active: true
threshold: 4
StringLiteralDuplication:
active: true
empty-blocks:
active: true
EmptyCatchBlock:
active: true
EmptyClassBlock:
active: true
EmptyDefaultConstructor:
active: true
EmptyDoWhileBlock:
active: true
EmptyElseBlock:
active: true
EmptyFinallyBlock:
active: true
EmptyForBlock:
active: true
EmptyFunctionBlock:
active: true
EmptyIfBlock:
active: true
EmptyInitBlock:
active: true
EmptyKtFile:
active: true
EmptySecondaryConstructor:
active: true
EmptyWhenBlock:
active: true
EmptyWhileBlock:
active: true
exceptions:
active: true
ExceptionRaisedInUnexpectedLocation:
active: true
InstanceOfCheckForException:
active: true
ReturnFromFinally:
active: true
TooGenericExceptionCaught:
active: false
SwallowedException:
active: true
ThrowingExceptionFromFinally:
active: true
ThrowingExceptionsWithoutMessageOrCause:
active: true
ThrowingNewInstanceOfSameException:
active: true
TooGenericExceptionThrown:
active: true
naming:
active: true
ClassNaming:
active: true
ConstructorParameterNaming:
active: true
EnumNaming:
active: true
ForbiddenClassName:
active: true
FunctionMaxLength:
active: true
maximumFunctionNameLength: 35
FunctionMinLength:
active: true
FunctionNaming:
active: true
FunctionParameterNaming:
active: true
MatchingDeclarationName:
active: true
MemberNameEqualsClassName:
active: true
ObjectPropertyNaming:
active: true
PackageNaming:
active: true
TopLevelPropertyNaming:
active: true
VariableMaxLength:
active: true
maximumVariableNameLength: 50
VariableMinLength:
active: true
VariableNaming:
active: true
performance:
ArrayPrimitive:
active: true
ForEachOnRange:
active: true
SpreadOperator:
active: true
UnnecessaryTemporaryInstantiation:
active: true
potential-bugs:
active: true
DuplicateCaseInWhenExpression:
active: true
EqualsAlwaysReturnsTrueOrFalse:
active: true
EqualsWithHashCodeExist:
active: true
ExplicitGarbageCollectionCall:
active: true
InvalidRange:
active: true
IteratorHasNextCallsNextMethod:
active: true
IteratorNotThrowingNoSuchElementException:
active: true
UnconditionalJumpStatementInLoop:
active: true
UnreachableCode:
active: true
UnsafeCallOnNullableType:
active: true
UnsafeCast:
active: true
UselessPostfixExpression:
active: true
WrongEqualsTypeParameter:
active: true
style:
active: true
MaxLineLength:
active: false
CollapsibleIfStatements:
active: true
DataClassContainsFunctions:
active: false
EqualsNullCall:
active: true
ExplicitItLambdaParameter:
active: true
ExpressionBodySyntax:
active: true
ForbiddenComment:
active: true
ForbiddenImport:
active: true
ForbiddenVoid:
active: true
FunctionOnlyReturningConstant:
active: true
LoopWithTooManyJumpStatements:
active: true
MagicNumber:
active: false
MandatoryBracesIfStatements:
active: true
MayBeConst:
active: true
ModifierOrder:
active: true
NestedClassesVisibility:
active: false
NewLineAtEndOfFile:
active: true
NoTabs:
active: true
OptionalAbstractKeyword:
active: true
OptionalUnit:
active: true
OptionalWhenBraces:
active: true
PreferToOverPairSyntax:
active: false
ProtectedMemberInFinalClass:
active: true
RedundantVisibilityModifierRule:
active: true
ReturnCount:
active: true
max: 4
SafeCast:
active: true
SerialVersionUIDInSerializableClass:
active: true
SpacingBetweenPackageAndImports:
active: true
ThrowsCount:
active: true
TrailingWhitespace:
active: true
UnnecessaryAbstractClass:
active: true
excludeAnnotatedClasses: "dagger.Module,android.arch.persistence.room.Dao"
UnnecessaryApply:
active: false # wait for fix
UnnecessaryInheritance:
active: true
UnnecessaryLet:
active: true
UnnecessaryParentheses:
active: true
UntilInsteadOfRangeTo:
active: true
UnusedImports:
active: false
UnusedPrivateMember:
active: true
UseDataClass:
active: true
UtilityClassWithPublicConstructor:
active: true
VarCouldBeVal:
active: true
WildcardImport:
active: true
excludeImports: 'kotlinx.android.synthetic.*'
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
android.useAndroidX=true
android.enableJetifier=true
### Maven publish configuration
GROUP=app.futured.hauler
VERSION_NAME=5.X.X-SNAPSHOT
POM_DESCRIPTION=Library with swipe to dismiss Activity gesture implementation.
POM_INCEPTION_YEAR=2018
POM_URL=https://github.com/futuredapp/hauler
POM_SCM_URL=https://github.com/futuredapp/hauler
POM_SCM_CONNECTION=scm:git:git://github.com/futuredapp/hauler.git
POM_SCM_DEV_CONNECTION=scm:git:ssh://github.com/futuredapp/hauler.git
POM_LICENCE_NAME=MIT
POM_LICENCE_URL=https://github.com/futuredapp/hauler/blob/master/LICENSE
POM_LICENCE_DIST=repo
POM_DEVELOPER_ID=futured
POM_DEVELOPER_NAME=Futured
POM_DEVELOPER_URL=https://futured.app
SONATYPE_HOST=DEFAULT
RELEASE_SIGNING_ENABLED=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: sample/.gitignore
================================================
/build
================================================
FILE: sample/build.gradle.kts
================================================
import org.jetbrains.kotlin.config.KotlinCompilerVersion
plugins {
id("com.android.application")
id("kotlin-android")
id("kotlin-android-extensions")
id("kotlin-kapt")
}
android {
compileSdkVersion(ProjectSettings.targetSdk)
defaultConfig {
applicationId = ProjectSettings.applicationId
minSdkVersion(ProjectSettings.minSdk)
targetSdkVersion(ProjectSettings.targetSdk)
}
sourceSets {
getByName("main").java.srcDir("src/main/kotlin")
}
dataBinding {
isEnabled = true
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
}
dependencies {
implementation(project(":core"))
implementation(project(":databinding"))
// Kotlin
implementation(kotlin(Deps.Kotlin.stdlib, KotlinCompilerVersion.VERSION))
implementation(Deps.AndroidX.appcompat)
}
================================================
FILE: sample/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.futured.haulersample">
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name="app.futured.haulersample.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="app.futured.haulersample.draggable.SimpleActivity"
android:theme="@style/AppTheme.Draggable" />
<activity
android:name="app.futured.haulersample.draggable.advanced.AdvancedActivity"
android:theme="@style/AppTheme.Draggable" />
<activity
android:name="app.futured.haulersample.draggable.databinding.DatabindingActivity"
android:theme="@style/AppTheme.Draggable" />
<activity
android:name="app.futured.haulersample.draggable.SimpleJavaActivity"
android:theme="@style/AppTheme.Draggable" />
</application>
</manifest>
================================================
FILE: sample/src/main/kotlin/app/futured/haulersample/MainActivity.kt
================================================
package app.futured.haulersample
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import app.futured.haulersample.draggable.SimpleActivity
import app.futured.haulersample.draggable.SimpleJavaActivity
import app.futured.haulersample.draggable.advanced.AdvancedActivity
import app.futured.haulersample.draggable.databinding.DatabindingActivity
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
startCommonButton.setOnClickListener {
startActivity(SimpleActivity.getStartIntent(this))
}
startAdvancedButton.setOnClickListener {
startActivity(AdvancedActivity.getStartIntent(this))
}
startBindingButton.setOnClickListener {
startActivity(DatabindingActivity.getStartIntent(this))
}
startJavaCommonButton.setOnClickListener {
startActivity(SimpleJavaActivity.getStartIntent(this))
}
}
}
================================================
FILE: sample/src/main/kotlin/app/futured/haulersample/draggable/SimpleActivity.kt
================================================
package app.futured.haulersample.draggable
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import app.futured.hauler.setOnDragActivityListener
import app.futured.hauler.setOnDragDismissedListener
import app.futured.haulersample.R
import kotlinx.android.synthetic.main.activity_simple.*
class SimpleActivity : AppCompatActivity() {
companion object {
fun getStartIntent(context: Context): Intent = Intent(context, SimpleActivity::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_simple)
commonHaulerView.setOnDragDismissedListener {
finish()
}
commonHaulerView.setOnDragActivityListener { elasticOffset, rawOffset ->
Log.d("SimpleActivity", "elasticOffset: $elasticOffset, rawOffset: $rawOffset")
}
}
}
================================================
FILE: sample/src/main/kotlin/app/futured/haulersample/draggable/SimpleJavaActivity.java
================================================
package app.futured.haulersample.draggable;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import org.jetbrains.annotations.NotNull;
import androidx.annotation.Nullable;
import app.futured.hauler.DragDirection;
import app.futured.hauler.HaulerView;
import app.futured.hauler.OnDragDismissedListener;
import app.futured.haulersample.R;
public class SimpleJavaActivity extends Activity {
public static Intent getStartIntent(Context context) {
return new Intent(context, SimpleJavaActivity.class);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple);
HaulerView hv = findViewById(R.id.commonHaulerView);
hv.setOnDragDismissedListener(new OnDragDismissedListener() {
@Override
public void onDismissed(@NotNull DragDirection dragDirection) {
finish();
}
});
}
}
================================================
FILE: sample/src/main/kotlin/app/futured/haulersample/draggable/advanced/AdvancedActivity.kt
================================================
package app.futured.haulersample.draggable.advanced
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import app.futured.hauler.DragDirection
import app.futured.hauler.setOnDragDismissedListener
import app.futured.haulersample.R
import kotlinx.android.synthetic.main.activity_advanced.*
class AdvancedActivity : AppCompatActivity() {
companion object {
fun getStartIntent(context: Context): Intent = Intent(context, AdvancedActivity::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_advanced)
advancedHaulerView.setOnDragDismissedListener { dragDirection ->
Toast.makeText(this, "Dismissed in direction: $dragDirection", Toast.LENGTH_SHORT).show()
when (dragDirection) {
DragDirection.DOWN -> {
finish()
overridePendingTransition(0, R.anim.anim_slide_down)
}
DragDirection.UP -> {
finish()
overridePendingTransition(0, R.anim.anim_slide_up)
}
}
}
ignoredAreaView.setScrollViewParent(scrollViewParent)
}
}
================================================
FILE: sample/src/main/kotlin/app/futured/haulersample/draggable/advanced/IgnoredAreaView.kt
================================================
package app.futured.haulersample.draggable.advanced
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.appcompat.widget.AppCompatTextView
import app.futured.hauler.LockableNestedScrollView
class IgnoredAreaView @kotlin.jvm.JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatTextView(context, attrs, defStyleAttr) {
private var parentScrollView: LockableNestedScrollView? = null
fun setScrollViewParent(parentScrollView: LockableNestedScrollView) {
this.parentScrollView = parentScrollView
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
return when (event.action) {
MotionEvent.ACTION_DOWN -> {
parentScrollView?.setScrollEnabled(false)
true
}
MotionEvent.ACTION_UP -> {
parentScrollView?.setScrollEnabled(true)
true
}
else -> false
}
}
}
================================================
FILE: sample/src/main/kotlin/app/futured/haulersample/draggable/databinding/DatabindingActivity.kt
================================================
package app.futured.haulersample.draggable.databinding
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import app.futured.hauler.DragDirection
import app.futured.haulersample.R
import app.futured.haulersample.databinding.ActivityDatabindingBinding
import kotlinx.android.synthetic.main.activity_databinding.*
class DatabindingActivity : AppCompatActivity(), DatabindingActivityView {
companion object {
fun getStartIntent(context: Context): Intent = Intent(context, DatabindingActivity::class.java)
}
lateinit var binding: ActivityDatabindingBinding
private var state = DatabindingActivityState(true)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_databinding)
refreshBinding()
dragEnabledCheck.setOnCheckedChangeListener { _, isChecked ->
state = state.copy(isDragEnabled = isChecked)
refreshBinding()
}
}
private fun refreshBinding() {
binding.viewState = state
binding.view = this
binding.executePendingBindings()
}
override fun onDragDismissed(dragDirection: DragDirection) {
finish()
}
}
================================================
FILE: sample/src/main/kotlin/app/futured/haulersample/draggable/databinding/DatabindingActivityState.kt
================================================
package app.futured.haulersample.draggable.databinding
data class DatabindingActivityState(
val isDragEnabled: Boolean
)
================================================
FILE: sample/src/main/kotlin/app/futured/haulersample/draggable/databinding/DatabindingActivityView.kt
================================================
package app.futured.haulersample.draggable.databinding
import app.futured.hauler.DragDirection
interface DatabindingActivityView {
fun onDragDismissed(dragDirection: DragDirection)
}
================================================
FILE: sample/src/main/res/anim/anim_slide_down.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromYDelta="0"
android:toYDelta="100%p"/>
</set>
================================================
FILE: sample/src/main/res/anim/anim_slide_up.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromYDelta="0"
android:toYDelta="-100%p"/>
</set>
================================================
FILE: sample/src/main/res/drawable/ic_launcher_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path android:fillColor="#008577"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>
================================================
FILE: sample/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0"/>
<item
android:color="#00000000"
android:offset="1.0"/>
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1"/>
</vector>
================================================
FILE: sample/src/main/res/layout/activity_advanced.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<app.futured.hauler.HaulerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context="app.futured.haulersample.draggable.advanced.AdvancedActivity"
android:id="@+id/advancedHaulerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
app:dragUpEnabled="true"
app:dragElasticity="0.4"
app:dragDismissScale="0.6"
app:fadeSystemBars="false"
app:dragDismissDistance="200dp">
<app.futured.hauler.LockableNestedScrollView
android:id="@+id/scrollViewParent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/swipe_to_dismiss"/>
<app.futured.haulersample.draggable.advanced.IgnoredAreaView
android:id="@+id/ignoredAreaView"
android:layout_width="200dp"
android:layout_marginTop="20dp"
android:layout_height="200dp"
android:background="@color/colorPrimary"
android:text="@string/ignored_area_text"
android:gravity="center"
android:textColor="@android:color/white"/>
</LinearLayout>
</app.futured.hauler.LockableNestedScrollView>
</app.futured.hauler.HaulerView>
================================================
FILE: sample/src/main/res/layout/activity_databinding.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewState"
type="app.futured.haulersample.draggable.databinding.DatabindingActivityState"/>
<variable
name="view"
type="app.futured.haulersample.draggable.databinding.DatabindingActivityView"/>
</data>
<app.futured.hauler.HaulerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:background="@android:color/white"
app:isDragEnabled="@{viewState.dragEnabled}"
app:isDragUpEnabled="@{true}"
app:onDragDismissedListener="@{(dragDirection) -> view.onDragDismissed(dragDirection)}">
<app.futured.hauler.LockableNestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:isScrollable="@{true}">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp">
<CheckBox
android:id="@+id/dragEnabledCheck"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/drag_enabled"
android:checked="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/swipe_to_dismiss"/>
</FrameLayout>
</app.futured.hauler.LockableNestedScrollView>
</app.futured.hauler.HaulerView>
</layout>
================================================
FILE: sample/src/main/res/layout/activity_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context="app.futured.haulersample.MainActivity">
<Button
android:id="@+id/startCommonButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/start_simple"
android:layout_gravity="center"/>
<Button
android:id="@+id/startAdvancedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/start_advanced"
android:layout_gravity="center" />
<Button
android:id="@+id/startBindingButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/start_binding"
android:layout_gravity="center" />
<Button
android:id="@+id/startJavaCommonButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/start_simple_java"
android:layout_gravity="center" />
</LinearLayout>
================================================
FILE: sample/src/main/res/layout/activity_simple.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<app.futured.hauler.HaulerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="app.futured.haulersample.draggable.SimpleActivity"
android:id="@+id/commonHaulerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/swipe_to_dismiss"/>
</FrameLayout>
</androidx.core.widget.NestedScrollView>
</app.futured.hauler.HaulerView>
================================================
FILE: sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>
================================================
FILE: sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>
================================================
FILE: sample/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
<color name="dark_gray">#99323232</color>
</resources>
================================================
FILE: sample/src/main/res/values/strings.xml
================================================
<resources>
<string name="app_name">Hauler</string>
<string name="start_simple">Start Simple Activity</string>
<string name="start_simple_java">Start Simple Java Activity</string>
<string name="start_advanced">Start Advanced Activity</string>
<string name="start_binding">Start Binding Activity</string>
<string name="swipe_to_dismiss">Swipe down to dismiss</string>
<string name="ignored_area_text">Ignored area</string>
<string name="drag_enabled">Drag Enabled</string>
</resources>
================================================
FILE: sample/src/main/res/values/styles.xml
================================================
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.Draggable" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowIsFloating">false</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@color/dark_gray</item>
</style>
</resources>
================================================
FILE: settings.gradle.kts
================================================
rootProject.buildFileName = "build.gradle.kts"
include(":core", ":databinding", ":sample")
gitextract_1jcvn5xr/ ├── .editorconfig ├── .github/ │ └── workflows/ │ ├── publish_release.yml │ ├── publish_snapshot.yml │ └── pull_request.yml ├── .gitignore ├── Dangerfile ├── Gemfile ├── LICENSE ├── README.md ├── build.gradle.kts ├── buildSrc/ │ ├── build.gradle.kts │ └── src/ │ └── main/ │ └── kotlin/ │ ├── Deps.kt │ ├── ProjectSettings.kt │ ├── Versions.kt │ └── app/ │ └── futured/ │ └── hauler/ │ └── DependencyUpdates.kt ├── core/ │ ├── .gitignore │ ├── build.gradle.kts │ ├── gradle.properties │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── kotlin/ │ │ └── app/ │ │ └── futured/ │ │ └── hauler/ │ │ ├── ColorUtils.kt │ │ ├── DragDirection.kt │ │ ├── HaulerView.kt │ │ ├── HaulerViewExtensions.kt │ │ ├── LockableNestedScrollView.kt │ │ ├── OnDragActivityListener.kt │ │ ├── OnDragDismissedListener.kt │ │ └── SystemBarsFader.kt │ └── res/ │ └── values/ │ ├── attrs_hauler_view.xml │ └── dimens.xml ├── databinding/ │ ├── .gitignore │ ├── build.gradle.kts │ ├── gradle.properties │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── kotlin/ │ └── app/ │ └── futured/ │ └── hauler/ │ └── databinding/ │ ├── IsDragEnabledAdapter.kt │ ├── IsDragUpEnabledAdapter.kt │ ├── IsScrollableAdapter.kt │ └── OnDragDismissedListenerAdapter.kt ├── detekt.yml ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── sample/ │ ├── .gitignore │ ├── build.gradle.kts │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── kotlin/ │ │ └── app/ │ │ └── futured/ │ │ └── haulersample/ │ │ ├── MainActivity.kt │ │ └── draggable/ │ │ ├── SimpleActivity.kt │ │ ├── SimpleJavaActivity.java │ │ ├── advanced/ │ │ │ ├── AdvancedActivity.kt │ │ │ └── IgnoredAreaView.kt │ │ └── databinding/ │ │ ├── DatabindingActivity.kt │ │ ├── DatabindingActivityState.kt │ │ └── DatabindingActivityView.kt │ └── res/ │ ├── anim/ │ │ ├── anim_slide_down.xml │ │ └── anim_slide_up.xml │ ├── drawable/ │ │ └── ic_launcher_background.xml │ ├── drawable-v24/ │ │ └── ic_launcher_foreground.xml │ ├── layout/ │ │ ├── activity_advanced.xml │ │ ├── activity_databinding.xml │ │ ├── activity_main.xml │ │ └── activity_simple.xml │ ├── mipmap-anydpi-v26/ │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ └── values/ │ ├── colors.xml │ ├── strings.xml │ └── styles.xml └── settings.gradle.kts
SYMBOL INDEX (3 symbols across 1 files)
FILE: sample/src/main/kotlin/app/futured/haulersample/draggable/SimpleJavaActivity.java
class SimpleJavaActivity (line 16) | public class SimpleJavaActivity extends Activity {
method getStartIntent (line 18) | public static Intent getStartIntent(Context context) {
method onCreate (line 22) | @Override
Condensed preview — 68 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (80K chars).
[
{
"path": ".editorconfig",
"chars": 35,
"preview": "[*.{kt,kts}]\n\nmax_line_length=130\n\n"
},
{
"path": ".github/workflows/publish_release.yml",
"chars": 957,
"preview": "name: Publish Release\n\non:\n release:\n types: [published]\n\njobs:\n release:\n name: Release Publish\n runs-on: [u"
},
{
"path": ".github/workflows/publish_snapshot.yml",
"chars": 1311,
"preview": "name: Publish Snapshot\non:\n push:\n branches:\n master\n\njobs:\n master:\n name: Snapshot Publish\n runs-on: ["
},
{
"path": ".github/workflows/pull_request.yml",
"chars": 859,
"preview": "name: Check PR\non: [pull_request]\n\njobs:\n pr:\n name: PR check\n runs-on: [ubuntu-latest]\n env:\n DANGER_GIT"
},
{
"path": ".gitignore",
"chars": 107,
"preview": "*.iml\n.gradle\n/local.properties\n/.idea/*\n/buildSrc/build/*\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n"
},
{
"path": "Dangerfile",
"chars": 933,
"preview": "is_pr_big = git.insertions > 500\nhas_correct_prefix = github.branch_for_head.match(/^(feature|hotfix|fix|release|houseke"
},
{
"path": "Gemfile",
"chars": 87,
"preview": "source 'https://rubygems.org'\n\ngem 'danger-commit_lint'\ngem 'danger-checkstyle_format'\n"
},
{
"path": "LICENSE",
"chars": 1076,
"preview": "MIT License\n\nCopyright (c) 2020 Futured apps s.r.o.\n\nPermission is hereby granted, free of charge, to any person obtaini"
},
{
"path": "README.md",
"chars": 5036,
"preview": "<img align=\"right\" src=\"images/Hauler.svg\">\n\n# Hauler\n\n[\n jc"
},
{
"path": "buildSrc/build.gradle.kts",
"chars": 243,
"preview": "plugins {\n `kotlin-dsl`\n}\n\nrepositories {\n jcenter()\n}\n\ndependencies {\n implementation(\"com.github.ben-manes:gr"
},
{
"path": "buildSrc/src/main/kotlin/Deps.kt",
"chars": 806,
"preview": "object Deps {\n const val gradlePlugin = \"com.android.tools.build:gradle:${Versions.gradle}\"\n\n object Plugins {\n "
},
{
"path": "buildSrc/src/main/kotlin/ProjectSettings.kt",
"chars": 133,
"preview": "object ProjectSettings {\n const val applicationId = \"app.futured.hauler\"\n const val targetSdk = 32\n const val m"
},
{
"path": "buildSrc/src/main/kotlin/Versions.kt",
"chars": 402,
"preview": "object Versions {\n // gradle\n const val gradle = \"7.2.1\"\n\n // plugins\n const val detekt = \"1.20.0\"\n const"
},
{
"path": "buildSrc/src/main/kotlin/app/futured/hauler/DependencyUpdates.kt",
"chars": 696,
"preview": "package app.futured.hauler\n\nimport com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask\n\nabstract class Dep"
},
{
"path": "core/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "core/build.gradle.kts",
"chars": 871,
"preview": "import org.jetbrains.kotlin.config.KotlinCompilerVersion\n\nplugins {\n id(\"com.android.library\")\n id(\"kotlin-android"
},
{
"path": "core/gradle.properties",
"chars": 140,
"preview": "POM_NAME=Library containing custom layout which enables to easily create swipe to dismiss Activity\nPOM_ARTIFACT_ID=haule"
},
{
"path": "core/src/main/AndroidManifest.xml",
"chars": 41,
"preview": "<manifest package=\"app.futured.hauler\"/>\n"
},
{
"path": "core/src/main/kotlin/app/futured/hauler/ColorUtils.kt",
"chars": 341,
"preview": "package app.futured.hauler\n\nimport androidx.annotation.CheckResult\nimport androidx.annotation.ColorInt\nimport androidx.a"
},
{
"path": "core/src/main/kotlin/app/futured/hauler/DragDirection.kt",
"chars": 70,
"preview": "package app.futured.hauler\n\nenum class DragDirection {\n UP, DOWN\n}\n"
},
{
"path": "core/src/main/kotlin/app/futured/hauler/HaulerView.kt",
"chars": 9208,
"preview": "package app.futured.hauler\n\nimport android.app.Activity\nimport android.content.Context\nimport android.util.AttributeSet\n"
},
{
"path": "core/src/main/kotlin/app/futured/hauler/HaulerViewExtensions.kt",
"chars": 697,
"preview": "package app.futured.hauler\n\nfun HaulerView.setOnDragDismissedListener(onDragDismissedListener: (DragDirection) -> Unit) "
},
{
"path": "core/src/main/kotlin/app/futured/hauler/LockableNestedScrollView.kt",
"chars": 872,
"preview": "package app.futured.hauler\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.MotionEv"
},
{
"path": "core/src/main/kotlin/app/futured/hauler/OnDragActivityListener.kt",
"chars": 126,
"preview": "package app.futured.hauler\n\ninterface OnDragActivityListener {\n fun onDrag(elasticOffsetPixels: Float, rawOffset: Flo"
},
{
"path": "core/src/main/kotlin/app/futured/hauler/OnDragDismissedListener.kt",
"chars": 116,
"preview": "package app.futured.hauler\n\ninterface OnDragDismissedListener {\n fun onDismissed(dragDirection: DragDirection)\n}\n"
},
{
"path": "core/src/main/kotlin/app/futured/hauler/SystemBarsFader.kt",
"chars": 1128,
"preview": "package app.futured.hauler\n\nimport android.app.Activity\nimport android.graphics.Color\n\ninternal class SystemBarsFader(pr"
},
{
"path": "core/src/main/res/values/attrs_hauler_view.xml",
"chars": 432,
"preview": "<resources>\n\n <declare-styleable name=\"HaulerView\">\n <attr name=\"dragDismissDistance\" format=\"dimension\"/>\n "
},
{
"path": "core/src/main/res/values/dimens.xml",
"chars": 126,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <dimen name=\"default_drag_dismiss_distance\">100dp</dimen>\n</resou"
},
{
"path": "databinding/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "databinding/build.gradle.kts",
"chars": 904,
"preview": "import org.jetbrains.kotlin.config.KotlinCompilerVersion\n\nplugins {\n id(\"com.android.library\")\n id(\"kotlin-android"
},
{
"path": "databinding/gradle.properties",
"chars": 95,
"preview": "POM_NAME=Databinding extensions for core library\nPOM_ARTIFACT_ID=databinding\nPOM_PACKAGING=aar\n"
},
{
"path": "databinding/src/main/AndroidManifest.xml",
"chars": 53,
"preview": "<manifest package=\"app.futured.hauler.databinding\"/>\n"
},
{
"path": "databinding/src/main/kotlin/app/futured/hauler/databinding/IsDragEnabledAdapter.kt",
"chars": 254,
"preview": "package app.futured.hauler.databinding\n\nimport androidx.databinding.BindingAdapter\nimport app.futured.hauler.HaulerView\n"
},
{
"path": "databinding/src/main/kotlin/app/futured/hauler/databinding/IsDragUpEnabledAdapter.kt",
"chars": 264,
"preview": "package app.futured.hauler.databinding\n\nimport androidx.databinding.BindingAdapter\nimport app.futured.hauler.HaulerView\n"
},
{
"path": "databinding/src/main/kotlin/app/futured/hauler/databinding/IsScrollableAdapter.kt",
"chars": 275,
"preview": "package app.futured.hauler.databinding\n\nimport androidx.databinding.BindingAdapter\nimport app.futured.hauler.LockableNes"
},
{
"path": "databinding/src/main/kotlin/app/futured/hauler/databinding/OnDragDismissedListenerAdapter.kt",
"chars": 417,
"preview": "package app.futured.hauler.databinding\n\nimport androidx.databinding.BindingAdapter\nimport app.futured.hauler.HaulerView\n"
},
{
"path": "detekt.yml",
"chars": 4905,
"preview": "build:\n maxIssues: 100\n\ncomplexity:\n active: true\n ComplexCondition:\n active: true\n ComplexMethod:\n active: tr"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 200,
"preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
},
{
"path": "gradle.properties",
"chars": 1546,
"preview": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will ov"
},
{
"path": "gradlew",
"chars": 5296,
"preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n## Gradle start up"
},
{
"path": "gradlew.bat",
"chars": 2260,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
},
{
"path": "sample/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "sample/build.gradle.kts",
"chars": 1013,
"preview": "import org.jetbrains.kotlin.config.KotlinCompilerVersion\n\nplugins {\n id(\"com.android.application\")\n id(\"kotlin-and"
},
{
"path": "sample/src/main/AndroidManifest.xml",
"chars": 1436,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package="
},
{
"path": "sample/src/main/kotlin/app/futured/haulersample/MainActivity.kt",
"chars": 1123,
"preview": "package app.futured.haulersample\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport app.fu"
},
{
"path": "sample/src/main/kotlin/app/futured/haulersample/draggable/SimpleActivity.kt",
"chars": 1000,
"preview": "package app.futured.haulersample.draggable\n\nimport android.content.Context\nimport android.content.Intent\nimport android."
},
{
"path": "sample/src/main/kotlin/app/futured/haulersample/draggable/SimpleJavaActivity.java",
"chars": 1055,
"preview": "package app.futured.haulersample.draggable;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android"
},
{
"path": "sample/src/main/kotlin/app/futured/haulersample/draggable/advanced/AdvancedActivity.kt",
"chars": 1352,
"preview": "package app.futured.haulersample.draggable.advanced\n\nimport android.content.Context\nimport android.content.Intent\nimport"
},
{
"path": "sample/src/main/kotlin/app/futured/haulersample/draggable/advanced/IgnoredAreaView.kt",
"chars": 1141,
"preview": "package app.futured.haulersample.draggable.advanced\n\nimport android.annotation.SuppressLint\nimport android.content.Conte"
},
{
"path": "sample/src/main/kotlin/app/futured/haulersample/draggable/databinding/DatabindingActivity.kt",
"chars": 1381,
"preview": "package app.futured.haulersample.draggable.databinding\n\nimport android.content.Context\nimport android.content.Intent\nimp"
},
{
"path": "sample/src/main/kotlin/app/futured/haulersample/draggable/databinding/DatabindingActivityState.kt",
"chars": 126,
"preview": "package app.futured.haulersample.draggable.databinding\n\ndata class DatabindingActivityState(\n val isDragEnabled: Bool"
},
{
"path": "sample/src/main/kotlin/app/futured/haulersample/draggable/databinding/DatabindingActivityView.kt",
"chars": 189,
"preview": "package app.futured.haulersample.draggable.databinding\n\nimport app.futured.hauler.DragDirection\n\ninterface DatabindingAc"
},
{
"path": "sample/src/main/res/anim/anim_slide_down.xml",
"chars": 270,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <translate\n "
},
{
"path": "sample/src/main/res/anim/anim_slide_up.xml",
"chars": 271,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <translate\n "
},
{
"path": "sample/src/main/res/drawable/ic_launcher_background.xml",
"chars": 4887,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n "
},
{
"path": "sample/src/main/res/drawable-v24/ic_launcher_foreground.xml",
"chars": 1969,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:aapt=\"http://schemas.android.com/aapt\"\n"
},
{
"path": "sample/src/main/res/layout/activity_advanced.xml",
"chars": 2021,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<app.futured.hauler.HaulerView\n xmlns:android=\"http://schemas.android.com/"
},
{
"path": "sample/src/main/res/layout/activity_databinding.xml",
"chars": 2098,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:"
},
{
"path": "sample/src/main/res/layout/activity_main.xml",
"chars": 1442,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
},
{
"path": "sample/src/main/res/layout/activity_simple.xml",
"chars": 1114,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<app.futured.hauler.HaulerView\n xmlns:android=\"http://schemas.android.com/"
},
{
"path": "sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
"chars": 271,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <b"
},
{
"path": "sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
"chars": 271,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <b"
},
{
"path": "sample/src/main/res/values/colors.xml",
"chars": 254,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"colorPrimary\">#008577</color>\n <color name=\"color"
},
{
"path": "sample/src/main/res/values/strings.xml",
"chars": 517,
"preview": "<resources>\n <string name=\"app_name\">Hauler</string>\n <string name=\"start_simple\">Start Simple Activity</string>\n "
},
{
"path": "sample/src/main/res/values/styles.xml",
"chars": 853,
"preview": "<resources>\n\n <!-- Base application theme. -->\n <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar"
},
{
"path": "settings.gradle.kts",
"chars": 92,
"preview": "rootProject.buildFileName = \"build.gradle.kts\"\n\ninclude(\":core\", \":databinding\", \":sample\")\n"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the thefuntasty/hauler GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 68 files (69.6 KB), approximately 20.1k tokens, and a symbol index with 3 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.