Repository: wayfair-incubator/panel-layout
Branch: master
Commit: f9fbb291a784
Files: 76
Total size: 113.6 KB
Directory structure:
gitextract_i0r_bc1v/
├── .github/
│ └── workflows/
│ ├── ci.yml
│ └── release.yml
├── .gitignore
├── .idea/
│ ├── .name
│ ├── codeStyles
│ ├── gradle.xml
│ ├── jarRepositories.xml
│ ├── misc.xml
│ ├── runConfigurations.xml
│ └── vcs.xml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── MAINTAINERS.md
├── README.md
├── RELEASING.md
├── SECURITY.md
├── app/
│ ├── .gitignore
│ ├── build.gradle
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── wayfair/
│ │ └── panellayout/
│ │ └── ExampleInstrumentedTest.kt
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── wayfair/
│ │ └── panellayout/
│ │ ├── MainActivity.kt
│ │ └── MaterialCardViewExt.kt
│ └── res/
│ ├── drawable/
│ │ ├── ic_ballpoint_pen.xml
│ │ ├── ic_baseline_visibility_off.xml
│ │ ├── ic_baseline_visibility_on.xml
│ │ ├── ic_circle.xml
│ │ ├── ic_circle_outline.xml
│ │ ├── ic_color_fill.xml
│ │ ├── ic_eraser.xml
│ │ ├── ic_fountain_pen.xml
│ │ ├── ic_grease_pencil.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_pencil.xml
│ │ ├── ic_resize.xml
│ │ ├── ic_select.xml
│ │ ├── ic_show_hide_button.xml
│ │ ├── ic_square.xml
│ │ ├── ic_square_outline.xml
│ │ ├── ic_text.xml
│ │ ├── ic_triangle.xml
│ │ └── ic_triangle_outline.xml
│ ├── drawable-v24/
│ │ └── ic_launcher_foreground.xml
│ ├── layout/
│ │ ├── activity_main.xml
│ │ ├── colors.xml
│ │ ├── shapes.xml
│ │ └── tools.xml
│ ├── mipmap-anydpi-v26/
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ └── values/
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── panellayout/
│ ├── .gitignore
│ ├── build.gradle
│ ├── gradle.properties
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── wayfair/
│ │ └── panellayout/
│ │ ├── PanelLayout.kt
│ │ ├── PanelLayoutCommands.kt
│ │ ├── PanelPosition.kt
│ │ └── PanelState.kt
│ └── res/
│ ├── layout/
│ │ ├── panel_view_default_snap_bottom.xml
│ │ ├── panel_view_default_snap_left.xml
│ │ ├── panel_view_default_snap_right.xml
│ │ └── panel_view_default_snap_top.xml
│ └── values/
│ ├── panel_attrs.xml
│ ├── panel_colors.xml
│ ├── panel_dimens.xml
│ └── panel_integers.xml
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
push:
branches: [ master ]
pull_request:
paths-ignore:
- 'docs/**'
- '*.md'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1
- name: Build
run: ./gradlew build -s
================================================
FILE: .github/workflows/release.yml
================================================
name: Publish a release
on:
push:
tags:
- '*'
jobs:
plugin-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout the repo
uses: actions/checkout@v2
- name: Publish
run: ./gradlew publish
env:
BINTRAY_API_KEY: ${{ secrets.BINTRAY_API_KEY }}
BINTRAY_USER: ${{ secrets.BINTRAY_USER }}
================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
================================================
FILE: .idea/.name
================================================
Panel Layout
================================================
FILE: .idea/codeStyles
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
</code_scheme>
</component>
</project>
================================================
FILE: .idea/gradle.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="PLATFORM" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="1.8" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/panellayout" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>
================================================
FILE: .idea/jarRepositories.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
</component>
</project>
================================================
FILE: .idea/misc.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeSettings">
<configurations>
<configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
</configurations>
</component>
<component name="MarkdownProjectSettings" wasCopied="true">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true" openRemoteLinks="true" replaceUnicodeEmoji="false" lastLayoutSetsDefault="false">
<PanelProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
</PanelProvider>
</PreviewSettings>
<ParserSettings gitHubSyntaxChange="false" emojiShortcuts="1" emojiImages="0">
<PegdownExtensions>
<option name="ABBREVIATIONS" value="false" />
<option name="ANCHORLINKS" value="true" />
<option name="ASIDE" value="false" />
<option name="ATXHEADERSPACE" value="true" />
<option name="AUTOLINKS" value="true" />
<option name="DEFINITIONS" value="false" />
<option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="FOOTNOTES" value="false" />
<option name="HARDWRAPS" value="false" />
<option name="HTML_DEEP_PARSER" value="false" />
<option name="INSERTED" value="false" />
<option name="QUOTES" value="false" />
<option name="RELAXEDHRULES" value="true" />
<option name="SMARTS" value="false" />
<option name="STRIKETHROUGH" value="true" />
<option name="SUBSCRIPT" value="false" />
<option name="SUPERSCRIPT" value="false" />
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
<option name="SUPPRESS_INLINE_HTML" value="false" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
<option name="TOC" value="false" />
<option name="WIKILINKS" value="false" />
</PegdownExtensions>
<ParserOptions>
<option name="ADMONITION_EXT" value="false" />
<option name="ATTRIBUTES_EXT" value="false" />
<option name="COMMONMARK_LISTS" value="true" />
<option name="DUMMY" value="false" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="ENUMERATED_REFERENCES_EXT" value="false" />
<option name="FLEXMARK_FRONT_MATTER" value="false" />
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="GITBOOK_URL_ENCODING" value="false" />
<option name="GITHUB_LISTS" value="false" />
<option name="GITHUB_WIKI_LINKS" value="false" />
<option name="GITLAB_EXT" value="false" />
<option name="GITLAB_MATH_EXT" value="false" />
<option name="GITLAB_MERMAID_EXT" value="false" />
<option name="HEADER_ID_NON_ASCII_TO_LOWERCASE" value="false" />
<option name="HEADER_ID_NO_DUPED_DASHES" value="false" />
<option name="JEKYLL_FRONT_MATTER" value="false" />
<option name="MACROS_EXT" value="false" />
<option name="NO_TEXT_ATTRIBUTES" value="false" />
<option name="PARSE_HTML_ANCHOR_ID" value="false" />
<option name="PLANTUML_FENCED_CODE" value="false" />
<option name="PUML_FENCED_CODE" value="false" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true" embedImages="false" embedHttpImages="false" imageUriSerials="false" addDocTypeHtml="true" noParaTags="false" plantUmlConversion="0" mathConversion="0">
<GeneratorProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
<bodyTop />
<bodyBottom />
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="true" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
</StylesheetProvider>
<ScriptProviders />
<cssText />
<cssUriHistory />
</CssSettings>
<HtmlExportSettings updateOnSave="false" parentDir="" targetDir="" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetPathType="2" targetExt="" useTargetExt="false" noCssNoScripts="false" useElementStyleAttribute="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
================================================
FILE: .idea/runConfigurations.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>
================================================
FILE: .idea/vcs.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
================================================
FILE: CHANGELOG.md
================================================
### v1.0.0-alpha02
- Fixes a bug that prevents resizing of the panel.
### v1.0.0-alpha01
- The very first release!
================================================
FILE: CONTRIBUTING.md
================================================
## Contributing to Panel Layout
First off, thank you for your interest in contributing to Panel Layout!
### Issue first
Found a bug 🐛, have an idea 💡, or want to improve something 🔧?
Please open an issue first describing the problem, your idea or possible improvements. Getting early feedback on your idea will save a ton of time before writing code in advance and realizing the idea is not feasible or not aligned with the goals of the project.
### Pull Requests
Please try to cover added / modified functions with tests as much as possible and make tests a part of your PR.
Writing a good description with screenshots / screen captures when necessary will help your PR get accepted / merged quickly.
================================================
FILE: LICENSE.md
================================================
BSD 2-Clause License
Copyright (c) 2020 Wayfair GmbH
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: MAINTAINERS.md
================================================
mkojadinovic [at] wayfair.com
================================================
FILE: README.md
================================================
*ARCHIVED* -- Wayfair's technology team is now focused on other endeavors. Please consider this project archived / available as-is until further notice.
## Panel Layout

Panel Layout is a UI library for Android that allows you to display a floating and resizable panel that can also snap to the edges.
Panel Layout makes use of [ConstraintLayout](https://developer.android.com/training/constraint-layout) to lay out panel with rest of the content.
This library is inspired by a good iOS UI framework: [PanelKit](https://github.com/louisdh/panelkit)

### Importing Panel Layout
[  ](https://bintray.com/wayfair/PanelLayout/PanelLayout/_latestVersion)
```kotlin
dependencies {
implementation("com.wayfair.panellayout:panellayout:<latest-version>")
}
```
**Note:** Panel Layout is currently in alpha and the public API it offers is __subject to heavy changes__.
### Panel Layout API
Define if the Panel Layout is visible
```kotlin
var panelVisible: Boolean
```
The command that put Panel Layout in one of the predefine Panel Positions.
Possible panel positions are: `LEFT_EDGE`, `RIGHT_EDGE`, `TOP_EDGE`, `BOTTOM_EDGE`, `NO_EDGE`.
```kotlin
fun snapPanelTo(panelPosition: PanelPosition)
```
The command that put the Panel Layout in absolute position with coordinates `x` and `y`.
```kotlin
fun popPanelTo(x: Int, y: Int)
```
Define a listener to define actions on different kinds of events.
```kotlin
var panelLayoutCallbacks: PanelLayout.Callbacks?
interface Callbacks {
fun beforeSnap(position: PanelPosition)
fun afterSnap(position: PanelPosition)
fun beforePop(popToX: Int, popToY: Int)
fun afterPop(popToX: Int, popToY: Int)
fun afterClose()
}
```
### Panel Layout attributes
`panel_content` - Resource id of view where is placed the Panel Layout
`panel_view` - Resource id of view inside the Panel Layout
`panel_move_handle` - Resource id of view used for moving the Panel Layout inside of content view
`panel_resize_enabled` - Flag that defines if the Panel Layout is resizable
`panel_snap_to_edges` - Define edges where the Panel Layout could be snapped. Possible values: `all`, `none`, `left`, `top`, `right` and `bottom`
`panel_start_height` - Start height
`panel_start_width` - Start width
### How to Use Panel Layout
Add Panel Layout in your layout:
```xml
<com.wayfair.panellayout.PanelLayout
android:id="@+id/panelLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:panel_content="@id/content"
app:panel_move_handle="@id/moveHandle"
app:panel_resize_enabled="true"
app:panel_resize_handle="@id/resizeHandle"
app:panel_snap_to_edges="all"
app:panel_start_height="300dp"
app:panel_start_width="300dp"
app:panel_view="@id/panel">
<FrameLayout
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#f3e5f5">
</FrameLayout>
<com.google.android.material.card.MaterialCardView
android:id="@+id/panel"
android:layout_width="300dp"
android:layout_height="300dp"
app:cardBackgroundColor="@color/white">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/moveHandle"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="#e1bee7" />
<ImageView
android:id="@+id/resizeHandle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="bottom|end"
android:padding="4dp"
android:src="@drawable/ic_resize"
app:tint="@color/divider" />
</FrameLayout>
</com.google.android.material.card.MaterialCardView>
</com.wayfair.panellayout.PanelLayout>
```
Controls and listeners in the code:
```kotlin
panelLayout.panelVisible = !panelLayout.panelVisible
```
```kotlin
panelLayout.popPanelTo(100, 100)
```
```kotlin
panelLayout.snapPanelTo(PanelPosition.RIGHT_EDGE)
```
```kotlin
panelLayout.panelLayoutCallbacks = object : PanelLayout.Callbacks {
override fun beforeSnap(position: PanelPosition) {
TODO("Not yet implemented")
}
override fun afterSnap(position: PanelPosition) {
TODO("Not yet implemented")
}
override fun beforePop(popToX: Int, popToY: Int) {
TODO("Not yet implemented")
}
override fun afterPop(popToX: Int, popToY: Int) {
TODO("Not yet implemented")
}
override fun afterClose() {
TODO("Not yet implemented")
}
}
```
### LICENSE
Panel Layout is licensed under 2-clause BSD License
See [LICENSE.md](LICENSE.md) for details.
### CONTRIBUTION
Panel Layout is open to contribution. See [CONTRIBUTING.md](CONTRIBUTING.md) for details
================================================
FILE: RELEASING.md
================================================
Releasing
========
1. Change the version in `gradle.properties` to a new version.
1. Update the `CHANGELOG.md` for the impending release.
1. `git commit -am "Prepare for release X.Y.Z."` (where X.Y.Z is the new version)
1. `git tag -a X.Y.X -m "Version X.Y.Z"` (where X.Y.Z is the new version)
1. `git push && git push --tags`
1. Wait for CI to publish and verify the version in `README.md`
================================================
FILE: SECURITY.md
================================================
If you have discovered a security vulnerability, please email opensource@wayfair.com
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
defaultConfig {
applicationId "com.wayfair.panellayout.sample"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildFeatures {
buildConfig = false
resValues = false
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.material:material:1.1.0'
implementation project(':panellayout')
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
================================================
FILE: app/src/androidTest/java/com/wayfair/panellayout/ExampleInstrumentedTest.kt
================================================
package com.wayfair.panellayout
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.wayfair.panellayout", appContext.packageName)
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.wayfair.panellayout">
<application
android:allowBackup="true"
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=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
================================================
FILE: app/src/main/java/com/wayfair/panellayout/MainActivity.kt
================================================
package com.wayfair.panellayout
import android.content.res.ColorStateList
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.children
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.colors.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
addShowHideClickListener()
addPanelElevationAndRadiusAnimations()
addColorSelectorClickListener()
}
private fun addShowHideClickListener() {
showHideButton.setOnClickListener {
panelLayout.panelVisible = !panelLayout.panelVisible
showHideButton.isSelected = !showHideButton.isSelected
}
}
private fun addColorSelectorClickListener() {
colorSelector.children.forEach { child ->
child.setOnClickListener {
it.backgroundTintList?.defaultColor?.let { color ->
art.imageTintList = ColorStateList.valueOf(color)
}
}
}
}
private fun addPanelElevationAndRadiusAnimations() {
panelLayout.panelLayoutCallbacks = object : PanelLayout.Callbacks {
override fun beforeSnap(position: PanelPosition) {
panel.animateRadius(
from = resources.getDimension(R.dimen.panel_corner_radius),
to = resources.getDimension(R.dimen.zero)
)
panel.animateElevation(
from = resources.getDimension(R.dimen.panel_elevation),
to = resources.getDimension(R.dimen.zero)
)
}
override fun afterSnap(position: PanelPosition) {}
override fun beforePop(popToX: Int, popToY: Int) {
panel.animateRadius(
from = resources.getDimension(R.dimen.zero),
to = resources.getDimension(R.dimen.panel_corner_radius)
)
panel.animateElevation(
from = resources.getDimension(R.dimen.zero),
to = resources.getDimension(R.dimen.panel_elevation)
)
}
override fun afterPop(popToX: Int, popToY: Int) {}
override fun afterClose() {}
}
}
}
================================================
FILE: app/src/main/java/com/wayfair/panellayout/MaterialCardViewExt.kt
================================================
package com.wayfair.panellayout
import android.animation.ValueAnimator
import com.google.android.material.card.MaterialCardView
fun MaterialCardView.animateRadius(from: Float, to: Float) =
ValueAnimator.ofFloat(from, to).apply {
addUpdateListener {
radius = it.animatedValue as Float
}
}.start()
fun MaterialCardView.animateElevation(from: Float, to: Float) =
ValueAnimator.ofFloat(from, to).apply {
addUpdateListener {
elevation = it.animatedValue as Float
}
}.start()
================================================
FILE: app/src/main/res/drawable/ic_ballpoint_pen.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M20.71,7.04C20.37,7.38 20.04,7.71 20.03,8.04C20,8.36 20.34,8.69 20.66,9C21.14,9.5 21.61,9.95 21.59,10.44C21.57,10.93 21.06,11.44 20.55,11.94L16.42,16.08L15,14.66L19.25,10.42L18.29,9.46L16.87,10.87L13.12,7.12L16.96,3.29C17.35,2.9 18,2.9 18.37,3.29L20.71,5.63C21.1,6 21.1,6.65 20.71,7.04M3,17.25L12.56,7.68L16.31,11.43L6.75,21H3V17.25Z" />
</vector>
================================================
FILE: app/src/main/res/drawable/ic_baseline_visibility_off.xml
================================================
<vector android:height="24dp"
android:tint="#FFFFFF"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="@android:color/white"
android:pathData="M12,7c2.76,0 5,2.24 5,5 0,0.65 -0.13,1.26 -0.36,1.83l2.92,2.92c1.51,-1.26 2.7,-2.89 3.43,-4.75 -1.73,-4.39 -6,-7.5 -11,-7.5 -1.4,0 -2.74,0.25 -3.98,0.7l2.16,2.16C10.74,7.13 11.35,7 12,7zM2,4.27l2.28,2.28 0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 6,7.5 11,7.5 1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22 21,20.73 3.27,3 2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.66 1.34,3 3,3 0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53 -2.76,0 -5,-2.24 -5,-5 0,-0.79 0.2,-1.53 0.53,-2.2zM11.84,9.02l3.15,3.15 0.02,-0.16c0,-1.66 -1.34,-3 -3,-3l-0.17,0.01z" />
</vector>
================================================
FILE: app/src/main/res/drawable/ic_baseline_visibility_on.xml
================================================
<vector android:height="24dp"
android:tint="#FFFFFF"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="@android:color/white"
android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z" />
</vector>
================================================
FILE: app/src/main/res/drawable/ic_circle.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />
</vector>
================================================
FILE: app/src/main/res/drawable/ic_circle_outline.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />
</vector>
================================================
FILE: app/src/main/res/drawable/ic_color_fill.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M19,11.5C19,11.5 17,13.67 17,15A2,2 0 0,0 19,17A2,2 0 0,0 21,15C21,13.67 19,11.5 19,11.5M5.21,10L10,5.21L14.79,10M16.56,8.94L7.62,0L6.21,1.41L8.59,3.79L3.44,8.94C2.85,9.5 2.85,10.47 3.44,11.06L8.94,16.56C9.23,16.85 9.62,17 10,17C10.38,17 10.77,16.85 11.06,16.56L16.56,11.06C17.15,10.47 17.15,9.5 16.56,8.94Z" />
</vector>
================================================
FILE: app/src/main/res/drawable/ic_eraser.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M16.24,3.56L21.19,8.5C21.97,9.29 21.97,10.55 21.19,11.34L12,20.53C10.44,22.09 7.91,22.09 6.34,20.53L2.81,17C2.03,16.21 2.03,14.95 2.81,14.16L13.41,3.56C14.2,2.78 15.46,2.78 16.24,3.56M4.22,15.58L7.76,19.11C8.54,19.9 9.8,19.9 10.59,19.11L14.12,15.58L9.17,10.63L4.22,15.58Z" />
</vector>
================================================
FILE: app/src/main/res/drawable/ic_fountain_pen.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M6.95,14.93L11.19,9.27L19.68,2.2C20.07,1.81 20.72,1.81 21.09,2.2L21.8,2.91C22.19,3.28 22.19,3.93 21.8,4.32L14.73,12.81L9.07,17.05L6.95,14.93M8.36,17.76L6.24,15.64L3.41,17.05L2,21.29L4.12,19.17C4.32,19 4.63,19 4.83,19.17C5,19.37 5,19.68 4.83,19.88L2.71,22L6.95,20.59L8.36,17.76Z" />
</vector>
================================================
FILE: app/src/main/res/drawable/ic_grease_pencil.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M18.62,1.5C18.11,1.5 17.6,1.69 17.21,2.09L10.75,8.55L14.95,12.74L21.41,6.29C22.2,5.5 22.2,4.24 21.41,3.46L20.04,2.09C19.65,1.69 19.14,1.5 18.62,1.5M9.8,9.5L3.23,16.07L3.93,16.77C3.4,17.24 2.89,17.78 2.38,18.29C1.6,19.08 1.6,20.34 2.38,21.12C3.16,21.9 4.42,21.9 5.21,21.12C5.72,20.63 6.25,20.08 6.73,19.58L7.43,20.27L14,13.7" />
</vector>
================================================
FILE: app/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:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
================================================
FILE: app/src/main/res/drawable/ic_pencil.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M20.71,7.04C21.1,6.65 21.1,6 20.71,5.63L18.37,3.29C18,2.9 17.35,2.9 16.96,3.29L15.12,5.12L18.87,8.87M3,17.25V21H6.75L17.81,9.93L14.06,6.18L3,17.25Z" />
</vector>
================================================
FILE: app/src/main/res/drawable/ic_resize.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M22,22H20V20H22V22M22,18H20V16H22V18M18,22H16V20H18V22M18,18H16V16H18V18M14,22H12V20H14V22M22,14H20V12H22V14Z" />
</vector>
================================================
FILE: app/src/main/res/drawable/ic_select.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M4,3H5V5H3V4A1,1 0 0,1 4,3M20,3A1,1 0 0,1 21,4V5H19V3H20M15,5V3H17V5H15M11,5V3H13V5H11M7,5V3H9V5H7M21,20A1,1 0 0,1 20,21H19V19H21V20M15,21V19H17V21H15M11,21V19H13V21H11M7,21V19H9V21H7M4,21A1,1 0 0,1 3,20V19H5V21H4M3,15H5V17H3V15M21,15V17H19V15H21M3,11H5V13H3V11M21,11V13H19V11H21M3,7H5V9H3V7M21,7V9H19V7H21Z" />
</vector>
================================================
FILE: app/src/main/res/drawable/ic_show_hide_button.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_baseline_visibility_on" android:state_selected="true" />
<item android:drawable="@drawable/ic_baseline_visibility_off" />
</selector>
================================================
FILE: app/src/main/res/drawable/ic_square.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M3,3V21H21V3" />
</vector>
================================================
FILE: app/src/main/res/drawable/ic_square_outline.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M3,3H21V21H3V3M5,5V19H19V5H5Z" />
</vector>
================================================
FILE: app/src/main/res/drawable/ic_text.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M9.62,12L12,5.67L14.37,12M11,3L5.5,17H7.75L8.87,14H15.12L16.25,17H18.5L13,3H11Z" />
</vector>
================================================
FILE: app/src/main/res/drawable/ic_triangle.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M1,21H23L12,2" />
</vector>
================================================
FILE: app/src/main/res/drawable/ic_triangle_outline.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M12,2L1,21H23M12,6L19.53,19H4.47" />
</vector>
================================================
FILE: app/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:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
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="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.wayfair.panellayout.PanelLayout
android:id="@+id/panelLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:panel_content="@id/content"
app:panel_move_handle="@id/moveHandle"
app:panel_resize_enabled="true"
app:panel_resize_handle="@id/resizeHandle"
app:panel_snap_to_edges="all"
app:panel_start_height="300dp"
app:panel_start_width="300dp"
app:panel_view="@id/panel">
<FrameLayout
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#f3e5f5">
<FrameLayout
android:id="@+id/canvas"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
android:background="@color/white"
android:elevation="2dp">
<ImageView
android:id="@+id/art"
android:scaleType="centerCrop"
android:layout_gravity="center"
android:layout_width="96dp"
android:layout_height="96dp"
android:tint="@color/colorAccent"
android:src="@drawable/ic_triangle_outline" />
</FrameLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/showHideButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="32dp"
android:src="@drawable/ic_show_hide_button"
app:backgroundTint="@color/colorAccent"
app:fabSize="normal"
app:tint="@color/white" />
</FrameLayout>
<com.google.android.material.card.MaterialCardView
android:id="@+id/panel"
android:layout_width="300dp"
android:layout_height="300dp"
app:cardBackgroundColor="@color/white">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="48dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tools"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
android:textColor="@color/charcoal" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginVertical="16dp"
android:background="@color/divider" />
<include
layout="@layout/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="@string/shapes"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
android:textColor="@color/charcoal" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginVertical="16dp"
android:background="@color/divider" />
<include
layout="@layout/shapes"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="@string/colors"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
android:textColor="@color/charcoal" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginVertical="16dp"
android:background="@color/divider" />
<include
layout="@layout/colors"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
<View
android:id="@+id/moveHandle"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="#e1bee7" />
<ImageView
android:id="@+id/resizeHandle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="bottom|end"
android:padding="4dp"
android:src="@drawable/ic_resize"
app:tint="@color/divider" />
</FrameLayout>
</com.google.android.material.card.MaterialCardView>
</com.wayfair.panellayout.PanelLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
================================================
FILE: app/src/main/res/layout/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/colorSelector"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:ignore="MissingConstraints">
<androidx.constraintlayout.helper.widget.Flow
android:id="@+id/flow"
android:layout_width="match_parent"
android:layout_height="0dp"
app:constraint_referenced_ids="color1, color2, color3, color4, color5, color6, color7, color8, color9, color10, color11, color12, color13, color14, color15, color16, color17, color18, color19, color20, color21, color22, color23"
app:flow_horizontalGap="16dp"
app:flow_verticalGap="16dp"
app:flow_wrapMode="aligned"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/color1"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color1" />
<View
android:id="@+id/color2"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color2" />
<View
android:id="@+id/color3"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color3" />
<View
android:id="@+id/color4"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color4" />
<View
android:id="@+id/color5"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color5" />
<View
android:id="@+id/color6"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color6" />
<View
android:id="@+id/color7"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color7" />
<View
android:id="@+id/color8"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color8" />
<View
android:id="@+id/color9"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color9" />
<View
android:id="@+id/color10"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color10" />
<View
android:id="@+id/color11"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color11" />
<View
android:id="@+id/color12"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color12" />
<View
android:id="@+id/color13"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color13" />
<View
android:id="@+id/color14"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color14" />
<View
android:id="@+id/color15"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color15" />
<View
android:id="@+id/color16"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color16" />
<View
android:id="@+id/color17"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color17" />
<View
android:id="@+id/color18"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color18" />
<View
android:id="@+id/color19"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color19" />
<View
android:id="@+id/color20"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color20" />
<View
android:id="@+id/color21"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color21" />
<View
android:id="@+id/color22"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color22" />
<View
android:id="@+id/color23"
android:layout_width="@dimen/color_width"
android:layout_height="@dimen/color_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/color23" />
</androidx.constraintlayout.widget.ConstraintLayout>
================================================
FILE: app/src/main/res/layout/shapes.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="MissingConstraints">
<View
android:id="@+id/shape1"
android:layout_width="@dimen/tool_width"
android:layout_height="@dimen/tool_width"
android:background="@drawable/ic_circle"
android:backgroundTint="@color/charcoal" />
<View
android:id="@+id/shape2"
android:layout_width="@dimen/tool_width"
android:layout_height="@dimen/tool_width"
android:background="@drawable/ic_circle_outline"
android:backgroundTint="@color/charcoal" />
<View
android:id="@+id/shape3"
android:layout_width="@dimen/tool_width"
android:layout_height="@dimen/tool_width"
android:background="@drawable/ic_square"
android:backgroundTint="@color/charcoal" />
<View
android:id="@+id/shape4"
android:layout_width="@dimen/tool_width"
android:layout_height="@dimen/tool_width"
android:background="@drawable/ic_square_outline"
android:backgroundTint="@color/charcoal" />
<View
android:id="@+id/shape5"
android:layout_width="@dimen/tool_width"
android:layout_height="@dimen/tool_width"
android:background="@drawable/ic_triangle"
android:backgroundTint="@color/charcoal" />
<View
android:id="@+id/shape6"
android:layout_width="@dimen/tool_width"
android:layout_height="@dimen/tool_width"
android:background="@drawable/ic_triangle_outline"
android:backgroundTint="@color/charcoal" />
<androidx.constraintlayout.helper.widget.Flow
android:layout_width="match_parent"
android:layout_height="0dp"
app:constraint_referenced_ids="shape1, shape2, shape3, shape4, shape5, shape6"
app:flow_horizontalGap="16dp"
app:flow_verticalGap="16dp"
app:flow_wrapMode="aligned"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
================================================
FILE: app/src/main/res/layout/tools.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="MissingConstraints">
<View
android:id="@+id/tool1"
android:layout_width="@dimen/tool_width"
android:layout_height="@dimen/tool_width"
android:background="@drawable/ic_pencil"
android:backgroundTint="@color/charcoal" />
<View
android:id="@+id/tool2"
android:layout_width="@dimen/tool_width"
android:layout_height="@dimen/tool_width"
android:background="@drawable/ic_grease_pencil"
android:backgroundTint="@color/charcoal" />
<View
android:id="@+id/tool3"
android:layout_width="@dimen/tool_width"
android:layout_height="@dimen/tool_width"
android:background="@drawable/ic_fountain_pen"
android:backgroundTint="@color/charcoal" />
<View
android:id="@+id/tool4"
android:layout_width="@dimen/tool_width"
android:layout_height="@dimen/tool_width"
android:background="@drawable/ic_ballpoint_pen"
android:backgroundTint="@color/charcoal" />
<View
android:id="@+id/tool5"
android:layout_width="@dimen/tool_width"
android:layout_height="@dimen/tool_width"
android:background="@drawable/ic_eraser"
android:backgroundTint="@color/charcoal" />
<View
android:id="@+id/tool6"
android:layout_width="@dimen/tool_width"
android:layout_height="@dimen/tool_width"
android:background="@drawable/ic_select"
android:backgroundTint="@color/charcoal" />
<View
android:id="@+id/tool7"
android:layout_width="@dimen/tool_width"
android:layout_height="@dimen/tool_width"
android:background="@drawable/ic_color_fill"
android:backgroundTint="@color/charcoal" />
<View
android:id="@+id/tool8"
android:layout_width="@dimen/tool_width"
android:layout_height="@dimen/tool_width"
android:background="@drawable/ic_text"
android:backgroundTint="@color/charcoal" />
<androidx.constraintlayout.helper.widget.Flow
android:layout_width="match_parent"
android:layout_height="0dp"
app:constraint_referenced_ids="tool1, tool2, tool3, tool4, tool5, tool6, tool7, tool8"
app:flow_horizontalGap="16dp"
app:flow_verticalGap="16dp"
app:flow_wrapMode="aligned"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
================================================
FILE: app/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: app/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: app/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#9C27B0</color>
<color name="colorPrimaryDark">#6a0080</color>
<color name="colorAccent">#9C27B0</color>
<color name="white">#FFFFFF</color>
<color name="divider">#20000000</color>
<color name="charcoal">#424242</color>
<!-- Color picker values-->
<color name="color1">#F44336</color>
<color name="color2">#E91E63</color>
<color name="color3">#9C27B0</color>
<color name="color4">#673AB7</color>
<color name="color5">#3F51B5</color>
<color name="color6">#2196F3</color>
<color name="color7">#03A9F4</color>
<color name="color8">#00BCD4</color>
<color name="color9">#009688</color>
<color name="color10">#4CAF50</color>
<color name="color11">#8BC34A</color>
<color name="color12">#CDDC39</color>
<color name="color13">#FFEB3B</color>
<color name="color14">#FFC107</color>
<color name="color15">#FF9800</color>
<color name="color16">#FF5722</color>
<color name="color17">#795548</color>
<color name="color18">#9E9E9E</color>
<color name="color19">#757575</color>
<color name="color20">#616161</color>
<color name="color21">#424242</color>
<color name="color22">#212121</color>
<color name="color23">#FFFFFF</color>
</resources>
================================================
FILE: app/src/main/res/values/dimens.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="zero">0dp</dimen>
<dimen name="panel_elevation">4dp</dimen>
<dimen name="panel_corner_radius">4dp</dimen>
<dimen name="tool_width">32dp</dimen>
<dimen name="color_width">32dp</dimen>
</resources>
================================================
FILE: app/src/main/res/values/strings.xml
================================================
<resources>
<string name="app_name">Panel Layout</string>
<string name="shapes">Shapes</string>
<string name="tools">Tools</string>
<string name="colors">Colors</string>
</resources>
================================================
FILE: app/src/main/res/values/styles.xml
================================================
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.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>
</resources>
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = "1.3.72"
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.0.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Tue May 05 17:46:57 EET 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
================================================
FILE: gradle.properties
================================================
org.gradle.jvmargs=-Xmx2048m
android.useAndroidX=true
android.enableJetifier=true
kotlin.code.style=official
GROUP=com.wayfair.panellayout
VERSION_NAME=1.0.0-alpha02
POM_URL=https://github.com/wayfair-incubator/panel-layout/
POM_SCM_URL=https://github.com/wayfair-incubator/panel-layout.git
POM_SCM_CONNECTION=scm:git:git://github.com/wayfair-incubator/panel-layout.git
POM_LICENCE_NAME=BSD 2-Clause "Simplified" License
POM_LICENCE_URL=https://raw.githubusercontent.com/wayfair-incubator/panel-layout/master/LICENSE
POM_LICENCE_DIST=repo
# https://github.com/gradle/gradle/issues/11412
systemProp.org.gradle.internal.publish.checksums.insecure=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: panellayout/.gitignore
================================================
/build
================================================
FILE: panellayout/build.gradle
================================================
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'org.gradle.maven-publish'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 21
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
}
buildFeatures {
buildConfig = false
resValues = false
}
}
androidExtensions {
features = ['parcelize']
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'
implementation 'androidx.core:core-ktx:1.2.0'
implementation "androidx.transition:transition:1.3.1"
}
publishing {
publications.create("default", MavenPublication) {
afterEvaluate {
from(components.findByName("release"))
}
def sourcesJarTask = tasks.create("sourcesJar", Jar) {
archiveClassifier.set("sources")
from(android.sourceSets["main"].java.srcDirs)
}
artifact(sourcesJarTask)
groupId = findProperty("GROUP")
version = findProperty("VERSION_NAME")
artifactId = findProperty("POM_ARTIFACT_ID")
pom {
name = findProperty("POM_NAME")
description = findProperty("POM_DESCRIPTION")
packaging = findProperty("POM_PACKAGING")
url = findProperty("POM_URL")
scm {
url = findProperty("POM_SCM_URL")
connection = findProperty("POM_SCM_CONNECTION")
}
licenses {
license {
name = findProperty("POM_LICENCE_NAME")
url = findProperty("POM_LICENCE_URL")
}
}
}
}
repositories {
maven {
name = "bintray"
url = uri("https://api.bintray.com/maven/wayfair/PanelLayout/PanelLayout/;publish=1;override=1")
credentials {
username = System.getenv("BINTRAY_USER")
password = System.getenv("BINTRAY_API_KEY")
}
}
}
}
================================================
FILE: panellayout/gradle.properties
================================================
POM_ARTIFACT_ID=panellayout
POM_NAME=Panel Layout
POM_DESCRIPTION=UI library for Android that allows you to display a floating and resizable panel that can also snap to the edges.
POM_PACKAGING=aar
================================================
FILE: panellayout/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.wayfair.panellayout" />
================================================
FILE: panellayout/src/main/java/com/wayfair/panellayout/PanelLayout.kt
================================================
/* These imports produce a detekt false-positive:
import androidx.core.graphics.component1
import androidx.core.graphics.component2
import androidx.core.graphics.component3
import androidx.core.graphics.component4
So UnusedImports are suppressed. */
@file:Suppress("UnusedImports", "CommentOverPrivateFunction")
package com.wayfair.panellayout
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.TypedArray
import android.graphics.Rect
import android.os.Bundle
import android.os.Parcelable
import android.util.AttributeSet
import android.view.*
import android.view.MotionEvent.*
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.annotation.ColorRes
import androidx.annotation.IdRes
import androidx.annotation.LayoutRes
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.content.ContextCompat
import androidx.core.content.res.getResourceIdOrThrow
import androidx.core.graphics.component1
import androidx.core.graphics.component2
import androidx.core.graphics.component3
import androidx.core.graphics.component4
import androidx.core.graphics.drawable.toDrawable
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import androidx.transition.AutoTransition
import androidx.transition.ChangeBounds
import androidx.transition.Transition
import androidx.transition.TransitionManager
import com.wayfair.panellayout.PanelPosition.*
import com.wayfair.panellayout.PanelPosition.values
import com.wayfair.panellayout.PanelState.HorizontalEdge.LEFT
import com.wayfair.panellayout.PanelState.HorizontalEdge.RIGHT
import com.wayfair.panellayout.PanelState.Snap.ANIMATING
import com.wayfair.panellayout.PanelState.Snap.FLOATING
import com.wayfair.panellayout.PanelState.Snap.SNAPPED
import com.wayfair.panellayout.PanelState.VerticalEdge.BOTTOM
import com.wayfair.panellayout.PanelState.VerticalEdge.TOP
import kotlin.math.roundToInt
import kotlin.math.sqrt
@Suppress("LargeClass", "LongMethod") // It's hard to split view classes
@SuppressLint("ClickableViewAccessibility") // ClickableViewAccessibility: We do not want to handle clicks on move and resize handles
class PanelLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), PanelLayoutCommands {
// Public field
override var panelLayoutCallbacks: Callbacks? = null
override var panelVisible: Boolean
get() = panelState.isVisible
set(value) {
if (value) showPanel() else hidePanel()
}
// Attrs (initial values are not used)
@IdRes
private var panelResId = 0
@IdRes
private var contentResId = 0
@IdRes
private var moveHandleResId = 0
@IdRes
private var resizeHandleResId = 0
private var resizeEnabled = false
private var panelMinWidth = 0
private var panelMaxWidth = 0
private var panelMinHeight = 0
private var panelMaxHeight = 0
private var panelStartWidth = 0
private var panelStartHeight = 0
private var panelSnapWidth = 0f
private var panelSnapHeight = 0f
private var panelSnapWidthPercent = 0f
private var panelSnapHeightPercent = 0f
@LayoutRes
private var snapLeftLayout = 0
@LayoutRes
private var snapTopLayout = 0
@LayoutRes
private var snapRightLayout = 0
@LayoutRes
private var snapBottomLayout = 0
private var snapAnimationDuration = 0L
private var snapOverlayAnimationDuration = 0L
@ColorRes
private var snapOverlayColor = 0
private var snapToEdges = 0
// View references
private lateinit var content: View
private lateinit var panelView: View
private lateinit var moveHandle: View
private var resizeHandle: View? = null
private val leftOverlay = View(context)
private val topOverlay = View(context)
private val rightOverlay = View(context)
private val bottomOverlay = View(context)
private var isPanelStateRestored = false
lateinit var panelState: PanelState
val initialPanelState: PanelState
get() = PanelState(
snap = FLOATING,
position = NO_EDGE,
size = panelStartWidth to panelStartHeight
)
private val preferredSnapWidth: Float
get() = when (panelSnapWidthPercent) {
NOT_SET -> panelSnapWidth
else -> width * panelSnapWidthPercent
}
private val preferredSnapHeight: Float
get() = when (panelSnapHeightPercent) {
NOT_SET -> panelSnapHeight
else -> height * panelSnapHeightPercent
}
// Touch / motion related
private val moveSnapListener = PanelMoveSnapListener()
private val popListener = PanelPopListener()
private val resizeListener = PanelResizeListener()
private var lastDownEvent: MotionEvent? = null
private var relativeTouchPositionX = 0f
private var relativeTouchPositionY = 0f
private var touchSubject: View? = null
init {
isMotionEventSplittingEnabled = false
isSaveEnabled = true
readAttrs(attrs)
setUpOverlays()
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
if (!isPanelStateRestored) {
panelState = initialPanelState
}
ensureChildren()
setTouchListeners()
restorePanelFromState()
}
private fun ensureChildren() {
fun Int.toResourceName() = resources.getResourceName(this)
require(::content.isInitialized) {
"Could not find child (panel_content) with id: ${contentResId.toResourceName()}"
}
require(::panelView.isInitialized) {
"Could not find child (panel_view) with id: ${panelResId.toResourceName()}"
}
require(::moveHandle.isInitialized) {
"Could not find child (panel_move_handle) with id: ${moveHandleResId.toResourceName()}"
}
if (resizeEnabled) {
require(resizeHandle != null) {
"Could not find child (panel_resize_handle) with id: ${resizeHandleResId.toResourceName()}"
}
}
}
override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams?) {
super.addView(child, index, params)
child.findViewById<View>(contentResId)?.let {
content = it
}
child.findViewById<View>(panelResId)?.let {
panelView = it
moveHandle = it.findViewById(moveHandleResId)
}
if (resizeEnabled) {
child.findViewById<View>(resizeHandleResId)?.let {
resizeHandle = it
}
}
}
private fun restorePanelFromState() = post {
if (!panelState.isVisible) {
panelView.isVisible = false
} else if (panelState.snap == FLOATING) {
// Restore size
val (withWidth, withHeight) = panelState.size
// Restore position
val toX = panelState.horizontalNearestEdgeDistance.toX(withWidth)
val toY = panelState.verticalNearestEdgeDistance.toY(withHeight)
applyFloatingPanelConstraints(toX, toY, withWidth, withHeight)
} else {
// Restore snap
snapPanelTo(panelState.position)
}
}
override fun onSaveInstanceState(): Parcelable? {
val superState = super.onSaveInstanceState()
return bundleOf(
PARCELABLE_KEY_SUPER_STATE to superState,
PARCELABLE_KEY_PANEL_STATE to panelState
)
}
override fun onRestoreInstanceState(state: Parcelable?) {
val bundle = state as Bundle
panelState = bundle.getParcelable(PARCELABLE_KEY_PANEL_STATE)!!
val superState: AbsSavedState? = bundle.getParcelable(PARCELABLE_KEY_SUPER_STATE)
isPanelStateRestored = true
super.onRestoreInstanceState(superState)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
if (panelState.snap == FLOATING) {
coercePanelSize()
}
}
private fun readAttrs(attrs: AttributeSet?) = attrs?.let {
val a = context.obtainStyledAttributes(it, R.styleable.PanelLayout, 0, 0)
try {
readResizeEnabledAttr(a)
readViewReferenceAttrs(a)
readMinMaxSizeAttrs(a)
readStartSizeAttrs(a)
readSnapSizeAttrs(a)
readSnapToEdgesAttr(a)
readSnapAttrs(a)
readOverlayAndAnimationAttrs(a)
} finally {
a.recycle()
}
}
private fun readResizeEnabledAttr(a: TypedArray) {
resizeEnabled = a.getBoolean(R.styleable.PanelLayout_panel_resize_enabled, true)
}
private fun readViewReferenceAttrs(a: TypedArray) {
panelResId = a.getResourceIdOrThrow(R.styleable.PanelLayout_panel_view)
contentResId = a.getResourceIdOrThrow(R.styleable.PanelLayout_panel_content)
moveHandleResId = a.getResourceIdOrThrow(R.styleable.PanelLayout_panel_move_handle)
resizeHandleResId = a.getResourceId(R.styleable.PanelLayout_panel_resize_handle, -1)
}
private fun readMinMaxSizeAttrs(a: TypedArray) {
panelMinWidth = a.getDimensionPixelSize(
R.styleable.PanelLayout_panel_min_width,
resources.getDimensionPixelSize(R.dimen.panel_default_min_width)
)
panelMaxWidth = a.getDimensionPixelSize(
R.styleable.PanelLayout_panel_max_width,
resources.getDimensionPixelSize(R.dimen.panel_default_max_width)
)
panelMinHeight = a.getDimensionPixelSize(
R.styleable.PanelLayout_panel_min_height,
resources.getDimensionPixelSize(R.dimen.panel_default_min_height)
)
panelMaxHeight = a.getDimensionPixelSize(
R.styleable.PanelLayout_panel_max_height,
resources.getDimensionPixelSize(R.dimen.panel_default_max_height)
)
}
private fun readStartSizeAttrs(a: TypedArray) {
panelStartWidth = a.getDimensionPixelSize(
R.styleable.PanelLayout_panel_start_width,
resources.getDimensionPixelSize(R.dimen.panel_default_start_width)
)
panelStartHeight = a.getDimensionPixelSize(
R.styleable.PanelLayout_panel_start_height,
resources.getDimensionPixelSize(R.dimen.panel_default_start_height)
)
}
private fun readSnapSizeAttrs(a: TypedArray) {
panelSnapWidthPercent = a.getFloat(R.styleable.PanelLayout_panel_snap_width_percent, NOT_SET)
panelSnapHeightPercent = a.getFloat(R.styleable.PanelLayout_panel_snap_height_percent, NOT_SET)
if (panelSnapWidthPercent == NOT_SET) {
panelSnapWidth = a.getDimension(
R.styleable.PanelLayout_panel_snap_width,
resources.getDimension(R.dimen.panel_default_snap_width)
)
}
if (panelSnapHeightPercent == NOT_SET) {
panelSnapHeight = a.getDimension(
R.styleable.PanelLayout_panel_snap_height,
resources.getDimension(R.dimen.panel_default_snap_height)
)
}
}
private fun readSnapToEdgesAttr(a: TypedArray) {
snapToEdges = a.getInt(
R.styleable.PanelLayout_panel_snap_to_edges,
R.integer.panel_default_snap_edges
)
}
private fun readSnapAttrs(a: TypedArray) {
snapLeftLayout = a.getResourceId(
R.styleable.PanelLayout_panel_snap_left_layout,
R.layout.panel_view_default_snap_left
)
snapTopLayout = a.getResourceId(
R.styleable.PanelLayout_panel_snap_top_layout,
R.layout.panel_view_default_snap_top
)
snapRightLayout = a.getResourceId(
R.styleable.PanelLayout_panel_snap_right_layout,
R.layout.panel_view_default_snap_right
)
snapBottomLayout = a.getResourceId(
R.styleable.PanelLayout_panel_snap_bottom_layout,
R.layout.panel_view_default_snap_bottom
)
}
private fun readOverlayAndAnimationAttrs(a: TypedArray) {
snapAnimationDuration = a.getInt(
R.styleable.PanelLayout_panel_snap_animation_duration,
resources.getInteger(R.integer.panel_default_snap_animation_duration)
).toLong()
snapOverlayAnimationDuration = a.getInt(
R.styleable.PanelLayout_panel_snap_overlay_animation_duration,
resources.getInteger(R.integer.panel_default_snap_overlay_animation_duration)
).toLong()
snapOverlayColor = a.getResourceId(
R.styleable.PanelLayout_panel_snap_overlay_color,
R.color.panel_default_snap_overlay_color
)
}
private fun setUpOverlays() {
for (overlay in arrayOf(leftOverlay, topOverlay, rightOverlay, bottomOverlay)) {
overlay.background = ContextCompat.getColor(context, snapOverlayColor).toDrawable()
overlay.alpha = 0f
overlay.layoutParams = LayoutParams(1, 1) // create square 1px x 1px to scale later.
overlay.elevation = 2f
addView(overlay)
}
}
private fun setTouchListeners() {
setOnTouchListener { v: View, event: MotionEvent ->
when (touchSubject) {
moveHandle -> when (panelState.snap) {
FLOATING -> moveSnapListener.onTouch(v, event)
SNAPPED -> popListener.onTouch(v, event)
else -> false
}
resizeHandle -> when (panelState.snap) {
FLOATING -> resizeListener.onTouch(v, event)
else -> false
}
else -> false
}
}
}
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
when (event.actionMasked) {
ACTION_DOWN -> {
relativeTouchPositionX = event.rawX - panelView.x
relativeTouchPositionY = event.rawY - panelView.y
lastDownEvent = obtain(event)
if (moveHandle.containsInHitRect(event)) {
touchSubject = moveHandle
} else if (resizeEnabled && resizeHandle != null && resizeHandle!!.containsInHitRect(event)) {
touchSubject = resizeHandle!!
} else {
touchSubject = null
}
return false
}
ACTION_MOVE -> {
return touchSubject != null && event.isSignificantlyDistantTo(lastDownEvent!!)
}
ACTION_UP -> {
return touchSubject != null && event.isSignificantlyDistantTo(lastDownEvent!!)
}
ACTION_CANCEL -> {
touchSubject = null
return false
}
else -> return false
}
}
/**
* Imitates a panel resize with zero difference in size.
* <p>
* This will make sure the panel size is recalculated and limited by the new panelLayout size.
* Whenever the keyboard is shown, we resize the panelView to fit into the limited space available.
*/
private fun coercePanelSize() = post {
val (updatedWidth, updatedHeight) = calculateNewSize(diffX = 0f, diffY = 0f)
resizePanel(updatedWidth, updatedHeight)
}
private fun calculateNewSize(diffX: Float, diffY: Float): Pair<Int, Int> {
var updatedWidth =
(panelView.width + diffX).roundToInt().coerceIn(panelMinWidth, panelMaxWidth)
var updatedHeight =
(panelView.height + diffY).roundToInt().coerceIn(panelMinHeight, panelMaxHeight)
// These negative adjustment values are to prevent resizing out of the panel layout
val negativeAdjustmentX = (width - (panelView.x + updatedWidth)).coerceAtMost(0f).roundToInt()
val negativeAdjustmentY = (height - (panelView.y + updatedHeight)).coerceAtMost(0f).roundToInt()
updatedWidth += negativeAdjustmentX
updatedHeight += negativeAdjustmentY
return updatedWidth to updatedHeight
}
private fun resizePanel(updatedWidth: Int, updatedHeight: Int) {
val params = panelView.layoutParams as LayoutParams
params.width = updatedWidth
params.height = updatedHeight
panelView.layoutParams = params
}
private fun calculatePositionFor(x: Int, y: Int): PanelPosition {
val (left, top, right, bottom) = panelView.moveBounds()
if (x == left) return LEFT_EDGE
if (x == right) return RIGHT_EDGE
if (y == top) return TOP_EDGE
if (y == bottom) return BOTTOM_EDGE
return NO_EDGE
}
private fun showSnapOverlay(snapPosition: PanelPosition) {
val overlay = snapPosition.overlay()
val (touchPointX, touchPointY) = snapPosition.touchPoint()
val (scaleX, scaleY) = snapPosition.overlayScale()
overlay.translationX = touchPointX
overlay.translationY = touchPointY
overlay.pivotX = touchPointX / width
overlay.pivotY = touchPointY / height
overlay.clearAnimation()
overlay.animate()
.alpha(1f)
.scaleX(scaleX)
.scaleY(scaleY)
.setDuration(snapOverlayAnimationDuration)
.setInterpolator(AccelerateDecelerateInterpolator())
.start()
}
private fun hideSnapOverlay(snapPosition: PanelPosition) {
val overlay = snapPosition.overlay()
overlay.clearAnimation()
overlay.animate()
.alpha(0f)
.scaleX(0f)
.scaleY(0f)
.setDuration(snapOverlayAnimationDuration)
.setInterpolator(AccelerateDecelerateInterpolator())
.start()
}
private fun hidePanel() {
if (panelState.snap == SNAPPED) {
val transition = AutoTransition().apply {
interpolator = AccelerateDecelerateInterpolator()
duration = snapAnimationDuration
addListener(object : Transition.TransitionListener {
override fun onTransitionEnd(transition: Transition) {
panelLayoutCallbacks?.afterClose()
}
override fun onTransitionStart(transition: Transition) {} // No-op
override fun onTransitionResume(transition: Transition) {} // No-op
override fun onTransitionPause(transition: Transition) {} // No-op
override fun onTransitionCancel(transition: Transition) {} // No-op
})
}
TransitionManager.beginDelayedTransition(this, transition)
}
panelView.isVisible = false
panelState.isVisible = false
}
private fun showPanel() {
panelState.isVisible = true
restorePanelFromState()
}
override fun popPanelTo(x: Int, y: Int) {
panelState.snap = ANIMATING
val (popWidth, popHeight) = panelState.sanitizedSize()
val transition = ChangeBounds().apply {
interpolator = AccelerateDecelerateInterpolator()
duration = snapAnimationDuration
addListener(object : Transition.TransitionListener {
override fun onTransitionStart(transition: Transition) {
panelLayoutCallbacks?.beforePop(x, y)
resizeHandle?.isVisible = true
}
override fun onTransitionEnd(transition: Transition) {
panelLayoutCallbacks?.afterPop(x, y)
}
override fun onTransitionResume(transition: Transition) {} // No-op
override fun onTransitionPause(transition: Transition) {} // No-op
override fun onTransitionCancel(transition: Transition) {} // No-op
})
}
TransitionManager.beginDelayedTransition(this, transition)
applyFloatingPanelConstraints(x, y, popWidth, popHeight)
panelState.position = NO_EDGE
panelState.snap = FLOATING
}
private fun applyFloatingPanelConstraints(x: Int, y: Int, width: Int, height: Int) = ConstraintSet().apply {
clone(this)
clear(panelResId)
clear(contentResId)
connect(contentResId, ConstraintSet.LEFT, ConstraintSet.PARENT_ID, ConstraintSet.LEFT)
connect(contentResId, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP)
connect(contentResId, ConstraintSet.RIGHT, ConstraintSet.PARENT_ID, ConstraintSet.RIGHT)
connect(contentResId, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM)
setTranslationX(panelResId, x.toFloat())
setTranslationY(panelResId, y.toFloat())
constrainWidth(panelResId, width)
constrainHeight(panelResId, height)
applyTo(this@PanelLayout)
}
override fun snapPanelTo(panelPosition: PanelPosition) {
if (panelState.snap == FLOATING) {
switchToMarginPositioning()
}
panelState.snap = ANIMATING
when (panelPosition) {
LEFT_EDGE -> applyConstraintsWithAnimation(snapLeftLayout)
RIGHT_EDGE -> applyConstraintsWithAnimation(snapRightLayout)
TOP_EDGE -> applyConstraintsWithAnimation(snapTopLayout)
BOTTOM_EDGE -> applyConstraintsWithAnimation(snapBottomLayout)
NO_EDGE -> throw IllegalArgumentException("Cannot snap panel with position NO_EDGE")
}
}
private fun switchToMarginPositioning() {
val marginLeft = panelView.translationX.roundToInt()
val marginTop = panelView.translationY.roundToInt()
panelView.translationX = 0f
panelView.translationY = 0f
ConstraintSet().apply {
clone(this)
connect(panelResId, ConstraintSet.LEFT, ConstraintSet.PARENT_ID, ConstraintSet.LEFT, marginLeft)
connect(panelResId, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP, marginTop)
applyTo(this@PanelLayout)
}
}
private fun applyConstraintsWithAnimation(layoutResId: Int) = post {
val constraintSet = ConstraintSet()
constraintSet.load(context, layoutResId)
val transition = AutoTransition().apply {
interpolator = AccelerateDecelerateInterpolator()
duration = snapAnimationDuration
addListener(object : Transition.TransitionListener {
override fun onTransitionStart(transition: Transition) {
panelLayoutCallbacks?.beforeSnap(panelState.position)
resizeHandle?.isVisible = false
}
override fun onTransitionEnd(transition: Transition) {
panelLayoutCallbacks?.afterSnap(panelState.position)
}
override fun onTransitionResume(transition: Transition) {} // No-op
override fun onTransitionPause(transition: Transition) {} // No-op
override fun onTransitionCancel(transition: Transition) {} // No-op
})
}
TransitionManager.beginDelayedTransition(this, transition)
constraintSet.applyTo(this)
panelState.snap = SNAPPED
}
private fun PanelPosition.isSnapEnabled() = snapToEdges.hasFlag(this.snapFlag())
private fun PanelPosition.snapFlag() = when (this) {
LEFT_EDGE -> SNAP_TO_LEFT
RIGHT_EDGE -> SNAP_TO_RIGHT
TOP_EDGE -> SNAP_TO_TOP
BOTTOM_EDGE -> SNAP_TO_BOTTOM
NO_EDGE -> throw IllegalArgumentException("You cannot get a snapFlag for PanelState.Position.NO_EDGE")
}
private fun PanelPosition.overlay() = when (this) {
LEFT_EDGE -> leftOverlay
RIGHT_EDGE -> rightOverlay
TOP_EDGE -> topOverlay
BOTTOM_EDGE -> bottomOverlay
NO_EDGE -> throw IllegalArgumentException("You cannot get an overlay for PanelState.Position.NO_EDGE")
}
private fun PanelPosition.overlayScale() = when (this) {
LEFT_EDGE,
RIGHT_EDGE -> preferredSnapWidth to height.toFloat() + 1
TOP_EDGE,
BOTTOM_EDGE -> width.toFloat() + 1 to preferredSnapHeight
NO_EDGE -> throw IllegalArgumentException("You cannot get an overlayScale for PanelState.Position.NO_EDGE")
}
private fun PanelPosition.touchPoint() = when (this) {
LEFT_EDGE -> 0f to panelView.centerY()
RIGHT_EDGE -> width.toFloat() to panelView.centerY()
TOP_EDGE -> panelView.centerX() to 0f
BOTTOM_EDGE -> panelView.centerX() to height.toFloat()
NO_EDGE -> throw IllegalArgumentException("You cannot get an touchPoint for PanelState.Position.NO_EDGE")
}
private fun View.centerX() = x + width / 2f
private fun View.centerY() = y + height / 2f
private fun PanelState.sanitizedSize(): Pair<Int, Int> {
val (savedWidth, savedHeight) = size
val w = when (savedWidth) {
-1 -> panelStartWidth
else -> savedWidth.coerceAtMost(width)
}
val h = when (savedHeight) {
-1 -> panelStartHeight
else -> savedHeight.coerceAtMost(height)
}
return w to h
}
private fun PanelState.HorizontalEdgeDistance.toX(panelWidth: Int) = when (this.edge) {
LEFT -> distance
RIGHT -> width - panelWidth - distance
}
private fun PanelState.VerticalEdgeDistance.toY(panelHeight: Int) = when (this.edge) {
TOP -> distance
BOTTOM -> height - panelHeight - distance
}
private fun View.calculateHorizontalNearestEdgeDistance(): PanelState.HorizontalEdgeDistance {
val leftDistance = this.x.roundToInt()
val rightDistance = this@PanelLayout.width - this.x.roundToInt() - this.width
return if (leftDistance <= rightDistance) {
PanelState.HorizontalEdgeDistance(LEFT, leftDistance)
} else {
PanelState.HorizontalEdgeDistance(RIGHT, rightDistance)
}
}
private fun View.calculateVerticalNearestEdgeDistance(): PanelState.VerticalEdgeDistance {
val topDistance = this.y.roundToInt()
val bottomDistance = this@PanelLayout.height - this.y.roundToInt() - this.height
return if (topDistance <= bottomDistance) {
PanelState.VerticalEdgeDistance(TOP, topDistance)
} else {
PanelState.VerticalEdgeDistance(BOTTOM, bottomDistance)
}
}
// Calculates move bounds given (width to height) pair in PanelLayout
private fun Pair<Int, Int>.moveBounds(offset: Int = 0): Rect {
val (childWidth, childHeight) = this
val minX = 0 + offset
val minY = 0 + offset
val maxX = (width - childWidth - offset).coerceAtLeast(minX)
val maxY = (height - childHeight - offset).coerceAtLeast(minY)
return Rect(minX, minY, maxX, maxY)
}
private fun View.moveBounds(): Rect = (width to height).moveBounds()
private fun MotionEvent.isSignificantlyDistantTo(other: MotionEvent): Boolean {
val deltaX = other.rawX - this.rawX
val deltaY = other.rawY - this.rawY
return sqrt(deltaX * deltaX + deltaY * deltaY) > ViewConfiguration.get(context).scaledTouchSlop
}
private fun View.containsInHitRect(event: MotionEvent) = offsetHitRectToAscendant(this@PanelLayout)
.contains(event.x.toInt(), event.y.toInt())
private fun View.offsetHitRectToAscendant(ascendant: View): Rect {
val hitRect = Rect()
this.getHitRect(hitRect)
var iterator = parent as View
while (iterator != ascendant) {
hitRect.offset(iterator.x.toInt(), iterator.y.toInt())
iterator = iterator.parent as View
}
return hitRect
}
private inner class PanelMoveSnapListener : OnTouchListener {
override fun onTouch(v: View, event: MotionEvent): Boolean {
return when (event.action) {
ACTION_DOWN -> true
ACTION_MOVE -> handleActionMove(event)
ACTION_UP -> handleActionUp()
else -> false
}
}
private fun handleActionMove(event: MotionEvent): Boolean {
val (left, top, right, bottom) = panelView.moveBounds()
val nextX = (event.rawX - relativeTouchPositionX).roundToInt().coerceIn(left, right)
val nextY = (event.rawY - relativeTouchPositionY).roundToInt().coerceIn(top, bottom)
val currentPosition = panelState.position
val nextPosition = calculatePositionFor(nextX, nextY)
panelState.position = nextPosition
handleSnapOverlayAnimation(currentPosition, nextPosition)
movePanelTo(nextX, nextY)
return true
}
private fun handleActionUp(): Boolean {
if (panelState.position != NO_EDGE && panelState.position.isSnapEnabled()) {
hideSnapOverlay(panelState.position)
snapPanelTo(panelState.position)
}
touchSubject = null
return true
}
// Checking current and next positions shows or hides snap overlays with animation
private fun handleSnapOverlayAnimation(
currentPosition: PanelPosition,
nextPosition: PanelPosition
) {
for (position in values()) {
if (position == NO_EDGE) continue
if (position.isSnapEnabled().not()) continue
if (currentPosition != position && nextPosition == position) {
showSnapOverlay(position)
}
if (currentPosition == position && nextPosition != position) {
hideSnapOverlay(position)
}
}
}
private fun movePanelTo(destinationX: Int, destinationY: Int) {
panelView.animate()
.translationX(destinationX.toFloat())
.translationY(destinationY.toFloat())
.setDuration(0)
.withEndAction {
panelState.horizontalNearestEdgeDistance = panelView.calculateHorizontalNearestEdgeDistance()
panelState.verticalNearestEdgeDistance = panelView.calculateVerticalNearestEdgeDistance()
}
.start()
}
}
private inner class PanelPopListener : OnTouchListener {
override fun onTouch(v: View, event: MotionEvent): Boolean {
return when (event.action) {
ACTION_DOWN -> true
ACTION_MOVE -> handleActionMove(event)
ACTION_UP -> handleActionUp()
else -> false
}
}
private fun handleActionMove(event: MotionEvent): Boolean {
relativeTouchPositionX = event.rawX - panelView.x
relativeTouchPositionY = event.rawY - panelView.y
val (popWidth, popHeight) = panelState.sanitizedSize()
val nextRelativeTouchPositionX = relativeTouchPositionX / panelView.width * popWidth
val nextRelativeTouchPositionY = relativeTouchPositionY / panelView.height * popHeight
val (left, top, right, bottom) = (popWidth to popHeight).moveBounds(offset = PANEL_POP_OFFSET)
val popToX = (event.rawX - nextRelativeTouchPositionX).roundToInt().coerceIn(left, right)
val popToY = (event.rawY - nextRelativeTouchPositionY).roundToInt().coerceIn(top, bottom)
popPanelTo(popToX, popToY)
relativeTouchPositionX = event.rawX - popToX
relativeTouchPositionY = event.rawY - popToY
return true
}
private fun handleActionUp(): Boolean {
touchSubject = null
return true
}
}
private inner class PanelResizeListener : OnTouchListener {
private var previousX = NOT_SET
private var previousY = NOT_SET
override fun onTouch(v: View, event: MotionEvent): Boolean {
return when (event.action) {
ACTION_DOWN -> true
ACTION_MOVE -> handleActionMove(event)
ACTION_UP -> handleActionUp()
else -> false
}
}
private fun handleActionMove(event: MotionEvent): Boolean {
if (previousX == NOT_SET || previousY == NOT_SET) {
previousX = lastDownEvent!!.rawX
previousY = lastDownEvent!!.rawY
}
val diffX = event.rawX - previousX
val diffY = event.rawY - previousY
val (updatedWidth, updatedHeight) = calculateNewSize(diffX, diffY)
resizePanel(updatedWidth, updatedHeight)
panelState.size = updatedWidth to updatedHeight
previousX = event.rawX
previousY = event.rawY
return true
}
private fun handleActionUp(): Boolean {
previousX = NOT_SET
previousY = NOT_SET
touchSubject = null
return true
}
}
interface Callbacks {
fun beforeSnap(position: PanelPosition)
fun afterSnap(position: PanelPosition)
fun beforePop(popToX: Int, popToY: Int)
fun afterPop(popToX: Int, popToY: Int)
fun afterClose()
}
companion object {
private const val NOT_SET = -1f
private const val PANEL_POP_OFFSET = 4 // distance from edges when panelState is popped. in pixels
private const val PARCELABLE_KEY_SUPER_STATE = "superState"
private const val PARCELABLE_KEY_PANEL_STATE = "panelState"
// Possible flags for snapToEdges
private const val SNAP_TO_LEFT = 1
private const val SNAP_TO_TOP = 2
private const val SNAP_TO_RIGHT = 4
private const val SNAP_TO_BOTTOM = 8
private fun Int.hasFlag(flag: Int) = (this and flag) == flag
}
}
================================================
FILE: panellayout/src/main/java/com/wayfair/panellayout/PanelLayoutCommands.kt
================================================
package com.wayfair.panellayout
internal interface PanelLayoutCommands {
var panelLayoutCallbacks: PanelLayout.Callbacks?
var panelVisible: Boolean
fun snapPanelTo(panelPosition: PanelPosition)
fun popPanelTo(x: Int, y: Int)
}
================================================
FILE: panellayout/src/main/java/com/wayfair/panellayout/PanelPosition.kt
================================================
package com.wayfair.panellayout
enum class PanelPosition {
LEFT_EDGE, RIGHT_EDGE, TOP_EDGE, BOTTOM_EDGE, NO_EDGE
}
================================================
FILE: panellayout/src/main/java/com/wayfair/panellayout/PanelState.kt
================================================
package com.wayfair.panellayout
import android.os.Parcelable
import com.wayfair.panellayout.PanelState.HorizontalEdge.LEFT
import com.wayfair.panellayout.PanelState.VerticalEdge.TOP
import kotlinx.android.parcel.Parcelize
@Parcelize
data class PanelState(
var isVisible: Boolean = true,
var snap: Snap = Snap.FLOATING,
var size: Pair<Int, Int> = -1 to -1,
var position: PanelPosition = PanelPosition.NO_EDGE,
var horizontalNearestEdgeDistance: HorizontalEdgeDistance = HorizontalEdgeDistance(edge = LEFT, distance = 0),
var verticalNearestEdgeDistance: VerticalEdgeDistance = VerticalEdgeDistance(edge = TOP, distance = 0)
) : Parcelable {
@Parcelize
data class HorizontalEdgeDistance(val edge: HorizontalEdge, val distance: Int) : Parcelable
@Parcelize
data class VerticalEdgeDistance(val edge: VerticalEdge, val distance: Int) : Parcelable
enum class HorizontalEdge {
LEFT, RIGHT
}
enum class VerticalEdge {
TOP, BOTTOM
}
enum class Snap {
FLOATING, ANIMATING, SNAPPED
}
}
================================================
FILE: panellayout/src/main/res/layout/panel_view_default_snap_bottom.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/panelLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="WrongConstraintLayoutUsage">
<View
android:id="@+id/content"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/panel"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="#ABC" />
<View
android:id="@+id/panel"
android:layout_width="match_parent"
android:layout_height="@dimen/panel_default_snap_height"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
================================================
FILE: panellayout/src/main/res/layout/panel_view_default_snap_left.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/panelLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="WrongConstraintLayoutUsage">
<View
android:id="@+id/content"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/panel"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="#ABC" />
<View
android:id="@+id/panel"
android:layout_width="@dimen/panel_default_snap_width"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
================================================
FILE: panellayout/src/main/res/layout/panel_view_default_snap_right.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/panelLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="WrongConstraintLayoutUsage">
<View
android:id="@+id/content"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/panel"
app:layout_constraintTop_toTopOf="parent"
tools:background="#ABC" />
<View
android:id="@+id/panel"
android:layout_width="@dimen/panel_default_snap_width"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
================================================
FILE: panellayout/src/main/res/layout/panel_view_default_snap_top.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/panelLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="WrongConstraintLayoutUsage">
<View
android:id="@+id/content"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/panel"
tools:background="#ABC" />
<View
android:id="@+id/panel"
android:layout_width="match_parent"
android:layout_height="@dimen/panel_default_snap_height"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
================================================
FILE: panellayout/src/main/res/values/panel_attrs.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="PanelLayout">
<attr name="panel_view" format="reference" />
<attr name="panel_content" format="reference" />
<attr name="panel_move_handle" format="reference" />
<attr name="panel_resize_handle" format="reference" />
<attr name="panel_resize_enabled" format="boolean" />
<attr name="panel_min_width" format="dimension" />
<attr name="panel_max_width" format="dimension" />
<attr name="panel_min_height" format="dimension" />
<attr name="panel_max_height" format="dimension" />
<attr name="panel_start_width" format="dimension" />
<attr name="panel_start_height" format="dimension" />
<attr name="panel_snap_width" format="dimension" />
<attr name="panel_snap_height" format="dimension" />
<attr name="panel_snap_width_percent" format="float" />
<attr name="panel_snap_height_percent" format="float" />
<attr name="panel_snap_animation_duration" format="integer" />
<attr name="panel_snap_overlay_animation_duration" format="integer" />
<attr name="panel_snap_overlay_color" format="color" />
<attr name="panel_snap_to_edges">
<flag name="none" value="0" />
<flag name="left" value="1" />
<flag name="top" value="2" />
<flag name="right" value="4" />
<flag name="bottom" value="8" />
<flag name="all" value="15" />
</attr>
<attr name="panel_snap_top_layout" format="reference" />
<attr name="panel_snap_right_layout" format="reference" />
<attr name="panel_snap_bottom_layout" format="reference" />
<attr name="panel_snap_left_layout" format="reference" />
</declare-styleable>
</resources>
================================================
FILE: panellayout/src/main/res/values/panel_colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="panel_default_snap_overlay_color">#802196F3</color>
</resources>
================================================
FILE: panellayout/src/main/res/values/panel_dimens.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="panel_default_min_width">100dp</dimen>
<dimen name="panel_default_max_width">600dp</dimen>
<dimen name="panel_default_min_height">100dp</dimen>
<dimen name="panel_default_max_height">600dp</dimen>
<dimen name="panel_default_start_width">320dp</dimen>
<dimen name="panel_default_start_height">480dp</dimen>
<dimen name="panel_default_snap_width">300dp</dimen>
<dimen name="panel_default_snap_height">300dp</dimen>
</resources>
================================================
FILE: panellayout/src/main/res/values/panel_integers.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="panel_default_snap_edges">15</integer> <!-- snap to all -->
<integer name="panel_default_snap_animation_duration">300</integer>
<integer name="panel_default_snap_overlay_animation_duration">300</integer>
</resources>
================================================
FILE: settings.gradle
================================================
include ':app', ':panellayout'
rootProject.name = "Panel Layout"
gitextract_i0r_bc1v/ ├── .github/ │ └── workflows/ │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .idea/ │ ├── .name │ ├── codeStyles │ ├── gradle.xml │ ├── jarRepositories.xml │ ├── misc.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── MAINTAINERS.md ├── README.md ├── RELEASING.md ├── SECURITY.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── wayfair/ │ │ └── panellayout/ │ │ └── ExampleInstrumentedTest.kt │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── wayfair/ │ │ └── panellayout/ │ │ ├── MainActivity.kt │ │ └── MaterialCardViewExt.kt │ └── res/ │ ├── drawable/ │ │ ├── ic_ballpoint_pen.xml │ │ ├── ic_baseline_visibility_off.xml │ │ ├── ic_baseline_visibility_on.xml │ │ ├── ic_circle.xml │ │ ├── ic_circle_outline.xml │ │ ├── ic_color_fill.xml │ │ ├── ic_eraser.xml │ │ ├── ic_fountain_pen.xml │ │ ├── ic_grease_pencil.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_pencil.xml │ │ ├── ic_resize.xml │ │ ├── ic_select.xml │ │ ├── ic_show_hide_button.xml │ │ ├── ic_square.xml │ │ ├── ic_square_outline.xml │ │ ├── ic_text.xml │ │ ├── ic_triangle.xml │ │ └── ic_triangle_outline.xml │ ├── drawable-v24/ │ │ └── ic_launcher_foreground.xml │ ├── layout/ │ │ ├── activity_main.xml │ │ ├── colors.xml │ │ ├── shapes.xml │ │ └── tools.xml │ ├── mipmap-anydpi-v26/ │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ └── values/ │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── panellayout/ │ ├── .gitignore │ ├── build.gradle │ ├── gradle.properties │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── wayfair/ │ │ └── panellayout/ │ │ ├── PanelLayout.kt │ │ ├── PanelLayoutCommands.kt │ │ ├── PanelPosition.kt │ │ └── PanelState.kt │ └── res/ │ ├── layout/ │ │ ├── panel_view_default_snap_bottom.xml │ │ ├── panel_view_default_snap_left.xml │ │ ├── panel_view_default_snap_right.xml │ │ └── panel_view_default_snap_top.xml │ └── values/ │ ├── panel_attrs.xml │ ├── panel_colors.xml │ ├── panel_dimens.xml │ └── panel_integers.xml └── settings.gradle
Condensed preview — 76 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (127K chars).
[
{
"path": ".github/workflows/ci.yml",
"chars": 306,
"preview": "name: CI\n\non:\n push:\n branches: [ master ]\n pull_request:\n paths-ignore:\n - 'docs/**'\n - '*.md'\n\njobs:"
},
{
"path": ".github/workflows/release.yml",
"chars": 364,
"preview": "name: Publish a release\n\non:\n push:\n tags:\n - '*'\n\njobs:\n plugin-deploy:\n runs-on: ubuntu-latest\n\n steps"
},
{
"path": ".gitignore",
"chars": 208,
"preview": "*.iml\n.gradle\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n/.idea/navEditor."
},
{
"path": ".idea/.name",
"chars": 12,
"preview": "Panel Layout"
},
{
"path": ".idea/codeStyles",
"chars": 1905,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"ProjectCodeStyleConfiguration\">\n <cod"
},
{
"path": ".idea/gradle.xml",
"chars": 853,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"GradleMigrationSettings\" migrationVersio"
},
{
"path": ".idea/jarRepositories.xml",
"chars": 1052,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"RemoteRepositoriesConfiguration\">\n <r"
},
{
"path": ".idea/misc.xml",
"chars": 5707,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"CMakeSettings\">\n <configurations>\n "
},
{
"path": ".idea/runConfigurations.xml",
"chars": 564,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"RunConfigurationProducerService\">\n <o"
},
{
"path": ".idea/vcs.xml",
"chars": 180,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"VcsDirectoryMappings\">\n <mapping dire"
},
{
"path": "CHANGELOG.md",
"chars": 116,
"preview": "### v1.0.0-alpha02\n- Fixes a bug that prevents resizing of the panel.\n\n### v1.0.0-alpha01\n- The very first release!\n"
},
{
"path": "CONTRIBUTING.md",
"chars": 708,
"preview": "## Contributing to Panel Layout\nFirst off, thank you for your interest in contributing to Panel Layout!\n\n### Issue first"
},
{
"path": "LICENSE.md",
"chars": 1300,
"preview": "BSD 2-Clause License\n\nCopyright (c) 2020 Wayfair GmbH\n\nRedistribution and use in source and binary forms, with or withou"
},
{
"path": "MAINTAINERS.md",
"chars": 30,
"preview": "mkojadinovic [at] wayfair.com\n"
},
{
"path": "README.md",
"chars": 5368,
"preview": "*ARCHIVED* -- Wayfair's technology team is now focused on other endeavors. Please consider this project archived / avail"
},
{
"path": "RELEASING.md",
"chars": 399,
"preview": "Releasing\n========\n\n 1. Change the version in `gradle.properties` to a new version.\n 1. Update the `CHANGELOG.md` for th"
},
{
"path": "SECURITY.md",
"chars": 85,
"preview": "If you have discovered a security vulnerability, please email opensource@wayfair.com\n"
},
{
"path": "app/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "app/build.gradle",
"chars": 1108,
"preview": "apply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-android-extensions'\n\nandroi"
},
{
"path": "app/src/androidTest/java/com/wayfair/panellayout/ExampleInstrumentedTest.kt",
"chars": 674,
"preview": "package com.wayfair.panellayout\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.juni"
},
{
"path": "app/src/main/AndroidManifest.xml",
"chars": 718,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package="
},
{
"path": "app/src/main/java/com/wayfair/panellayout/MainActivity.kt",
"chars": 2433,
"preview": "package com.wayfair.panellayout\n\nimport android.content.res.ColorStateList\nimport android.os.Bundle\nimport androidx.appc"
},
{
"path": "app/src/main/java/com/wayfair/panellayout/MaterialCardViewExt.kt",
"chars": 546,
"preview": "package com.wayfair.panellayout\n\nimport android.animation.ValueAnimator\nimport com.google.android.material.card.Material"
},
{
"path": "app/src/main/res/drawable/ic_ballpoint_pen.xml",
"chars": 599,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:height=\"24dp\"\n android:width=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/ic_baseline_visibility_off.xml",
"chars": 901,
"preview": "<vector android:height=\"24dp\"\n android:tint=\"#FFFFFF\"\n android:viewportHeight=\"24\"\n android:viewportWidth=\"24\"\n"
},
{
"path": "app/src/main/res/drawable/ic_baseline_visibility_on.xml",
"chars": 542,
"preview": "<vector android:height=\"24dp\"\n android:tint=\"#FFFFFF\"\n android:viewportHeight=\"24\"\n android:viewportWidth=\"24\"\n"
},
{
"path": "app/src/main/res/drawable/ic_circle.xml",
"chars": 342,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:height=\"24dp\"\n android:width=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/ic_circle_outline.xml",
"chars": 410,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:height=\"24dp\"\n android:width=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/ic_color_fill.xml",
"chars": 573,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:height=\"24dp\"\n android:width=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/ic_eraser.xml",
"chars": 537,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:height=\"24dp\"\n android:width=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/ic_fountain_pen.xml",
"chars": 543,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:height=\"24dp\"\n android:width=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/ic_grease_pencil.xml",
"chars": 589,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:height=\"24dp\"\n android:width=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/ic_launcher_background.xml",
"chars": 5606,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:wi"
},
{
"path": "app/src/main/res/drawable/ic_pencil.xml",
"chars": 413,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:height=\"24dp\"\n android:width=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/ic_resize.xml",
"chars": 375,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:height=\"24dp\"\n android:width=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/ic_select.xml",
"chars": 573,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:height=\"24dp\"\n android:width=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/ic_show_hide_button.xml",
"chars": 288,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <item a"
},
{
"path": "app/src/main/res/drawable/ic_square.xml",
"chars": 278,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:height=\"24dp\"\n android:width=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/ic_square_outline.xml",
"chars": 295,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:height=\"24dp\"\n android:width=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/ic_text.xml",
"chars": 345,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:height=\"24dp\"\n android:width=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/ic_triangle.xml",
"chars": 279,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:height=\"24dp\"\n android:width=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/ic_triangle_outline.xml",
"chars": 298,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:height=\"24dp\"\n android:width=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
"chars": 1703,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:aapt=\"http://schemas.android.com/aapt\"\n "
},
{
"path": "app/src/main/res/layout/activity_main.xml",
"chars": 6822,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "app/src/main/res/layout/colors.xml",
"chars": 6777,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "app/src/main/res/layout/shapes.xml",
"chars": 2414,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "app/src/main/res/layout/tools.xml",
"chars": 2897,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
"chars": 273,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <b"
},
{
"path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
"chars": 273,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <b"
},
{
"path": "app/src/main/res/values/colors.xml",
"chars": 1325,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"colorPrimary\">#9C27B0</color>\n <color name=\"color"
},
{
"path": "app/src/main/res/values/dimens.xml",
"chars": 280,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <dimen name=\"zero\">0dp</dimen>\n <dimen name=\"panel_elevation\">"
},
{
"path": "app/src/main/res/values/strings.xml",
"chars": 199,
"preview": "<resources>\n <string name=\"app_name\">Panel Layout</string>\n <string name=\"shapes\">Shapes</string>\n <string name"
},
{
"path": "app/src/main/res/values/styles.xml",
"chars": 391,
"preview": "<resources>\n <!-- Base application theme. -->\n <style name=\"AppTheme\" parent=\"Theme.MaterialComponents.Light.DarkA"
},
{
"path": "build.gradle",
"chars": 509,
"preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n ex"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 232,
"preview": "#Tue May 05 17:46:57 EET 2020\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
},
{
"path": "gradle.properties",
"chars": 653,
"preview": "org.gradle.jvmargs=-Xmx2048m\nandroid.useAndroidX=true\nandroid.enableJetifier=true\nkotlin.code.style=official\n\nGROUP=com."
},
{
"path": "gradlew",
"chars": 5296,
"preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n## Gradle start up"
},
{
"path": "gradlew.bat",
"chars": 2176,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
},
{
"path": "panellayout/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "panellayout/build.gradle",
"chars": 2263,
"preview": "apply plugin: 'com.android.library'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-android-extensions'\napply plugi"
},
{
"path": "panellayout/gradle.properties",
"chars": 198,
"preview": "POM_ARTIFACT_ID=panellayout\nPOM_NAME=Panel Layout\nPOM_DESCRIPTION=UI library for Android that allows you to display a fl"
},
{
"path": "panellayout/src/main/AndroidManifest.xml",
"chars": 86,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest package=\"com.wayfair.panellayout\" />\n"
},
{
"path": "panellayout/src/main/java/com/wayfair/panellayout/PanelLayout.kt",
"chars": 34194,
"preview": "/* These imports produce a detekt false-positive:\n import androidx.core.graphics.component1\n import androidx.core"
},
{
"path": "panellayout/src/main/java/com/wayfair/panellayout/PanelLayoutCommands.kt",
"chars": 245,
"preview": "package com.wayfair.panellayout\n\ninternal interface PanelLayoutCommands {\n var panelLayoutCallbacks: PanelLayout.Call"
},
{
"path": "panellayout/src/main/java/com/wayfair/panellayout/PanelPosition.kt",
"chars": 120,
"preview": "package com.wayfair.panellayout\n\nenum class PanelPosition {\n LEFT_EDGE, RIGHT_EDGE, TOP_EDGE, BOTTOM_EDGE, NO_EDGE\n}\n"
},
{
"path": "panellayout/src/main/java/com/wayfair/panellayout/PanelState.kt",
"chars": 1070,
"preview": "package com.wayfair.panellayout\n\nimport android.os.Parcelable\nimport com.wayfair.panellayout.PanelState.HorizontalEdge.L"
},
{
"path": "panellayout/src/main/res/layout/panel_view_default_snap_bottom.xml",
"chars": 1150,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "panellayout/src/main/res/layout/panel_view_default_snap_left.xml",
"chars": 1150,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "panellayout/src/main/res/layout/panel_view_default_snap_right.xml",
"chars": 1149,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "panellayout/src/main/res/layout/panel_view_default_snap_top.xml",
"chars": 1150,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas."
},
{
"path": "panellayout/src/main/res/values/panel_attrs.xml",
"chars": 1847,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n <declare-styleable name=\"PanelLayout\">\n\n <attr name=\"pane"
},
{
"path": "panellayout/src/main/res/values/panel_colors.xml",
"chars": 132,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"panel_default_snap_overlay_color\">#802196F3</color>\n"
},
{
"path": "panellayout/src/main/res/values/panel_dimens.xml",
"chars": 521,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <dimen name=\"panel_default_min_width\">100dp</dimen>\n <dimen na"
},
{
"path": "panellayout/src/main/res/values/panel_integers.xml",
"chars": 294,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <integer name=\"panel_default_snap_edges\">15</integer> <!-- snap t"
},
{
"path": "settings.gradle",
"chars": 65,
"preview": "include ':app', ':panellayout'\nrootProject.name = \"Panel Layout\"\n"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the wayfair-incubator/panel-layout GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 76 files (113.6 KB), approximately 31.8k tokens. 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.