├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── art ├── home-1line.png ├── home-2line.png ├── home-colored.png ├── home.png └── list-1line.png ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lib ├── .gitignore ├── build.gradle ├── gradle.properties ├── maven-push.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── nispok │ │ └── snackbar │ │ ├── DisplayCompat.java │ │ ├── DisplayCompatImplHoneycombMR2.java │ │ ├── DisplayCompatImplJBMR1.java │ │ ├── DisplayCompatImplPreHoneycombMR2.java │ │ ├── RecyclerUtil.java │ │ ├── Snackbar.java │ │ ├── SnackbarHelperChildViewJB.java │ │ ├── SnackbarManager.java │ │ ├── enums │ │ └── SnackbarType.java │ │ ├── layouts │ │ └── SnackbarLayout.java │ │ └── listeners │ │ ├── ActionClickListener.java │ │ ├── ActionSwipeListener.java │ │ ├── EventListener.java │ │ ├── EventListenerAdapter.java │ │ └── SwipeDismissTouchListener.java │ └── res │ ├── anim │ ├── sb__bottom_in.xml │ ├── sb__bottom_out.xml │ ├── sb__top_in.xml │ └── sb__top_out.xml │ ├── drawable-v21 │ └── sb__btn_bg.xml │ ├── drawable │ ├── sb__bg.xml │ ├── sb__btn_bg.xml │ └── sb__divider_bg.xml │ ├── interpolator │ ├── sb__accelerate_cubic.xml │ └── sb__decelerate_cubic.xml │ ├── layout │ └── sb__template.xml │ ├── values-sw600dp │ ├── bools.xml │ └── dimens.xml │ ├── values-v11 │ └── bools.xml │ ├── values-v21 │ └── styles.xml │ ├── values-xlarge │ ├── bools.xml │ └── dimens.xml │ └── values │ ├── bools.xml │ ├── colors.xml │ ├── dimens.xml │ └── styles.xml ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── fonts │ │ │ └── Roboto-LightItalic.ttf │ ├── java │ │ └── com │ │ │ └── nispok │ │ │ └── samples │ │ │ └── snackbar │ │ │ ├── SnackbarImmersiveModeSampleActivity.java │ │ │ ├── SnackbarListViewSampleActivity.java │ │ │ ├── SnackbarNavigationBarTranslucentSampleActivity.java │ │ │ ├── SnackbarRecyclerViewSampleActivity.java │ │ │ ├── SnackbarSampleActivity.java │ │ │ └── SnackbarShowInDialogSampleActivity.java │ └── res │ │ ├── drawable-hdpi │ │ └── ic_launcher.png │ │ ├── drawable-mdpi │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ │ ├── drawable │ │ └── custom_shape.xml │ │ ├── layout │ │ ├── activity_immersive_mode_sample.xml │ │ ├── activity_list_sample.xml │ │ ├── activity_navigation_bar_translucent_sample.xml │ │ ├── activity_recyclerview_sample.xml │ │ ├── activity_sample.xml │ │ ├── activity_show_in_dialog_sample.xml │ │ └── fragment_dialog_list.xml │ │ ├── menu │ │ ├── recyclerview_sample.xml │ │ └── sample.xml │ │ └── values │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ ├── java │ └── com │ │ └── nispok │ │ └── snackbar │ │ └── SnackbarBuilderTest.java │ └── res │ └── org.robolectric.Config.properties └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | /local.properties 4 | .DS_Store 5 | /build 6 | *.iml 7 | .navigation 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: android 3 | 4 | branches: 5 | only: 6 | - master 7 | 8 | android: 9 | components: 10 | # Uncomment the lines below if you want to 11 | # use the latest revision of Android SDK Tools 12 | - platform-tools 13 | - tools 14 | - extra-android-support 15 | - extra-android-m2repository 16 | 17 | # The BuildTools version used by your project 18 | - build-tools-22.0.1 19 | 20 | # The SDK version used to compile your project 21 | - android-22 22 | 23 | script: 24 | - ./gradlew :sample:test :sample:assembleDebug 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 William Mora 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation 7 | the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 8 | and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions 11 | of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 14 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 16 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | This lib is deprecated in favor of Google's [Design Support Library](http://developer.android.com/tools/support-library/features.html#design) which includes a [Snackbar](http://developer.android.com/reference/android/support/design/widget/Snackbar.html) and is no longer being developed. 3 | 4 | Thanks for all your support! 5 | 6 | # Snackbar 7 | [![Build Status](https://travis-ci.org/nispok/snackbar.svg?branch=master)](https://travis-ci.org/nispok/snackbar) [![Maven Central](https://img.shields.io/maven-central/v/com.nispok/snackbar.svg)](https://oss.sonatype.org/content/repositories/releases/com/nispok/snackbar/) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Snackbar-brightgreen.svg?style=flat)](https://android-arsenal.com/details/1/1160) [![Android Weekly](https://img.shields.io/badge/Android%20Weekly-129-blue.svg?style=flat)](http://androidweekly.net/issues/issue-129) 8 | 9 | Library that implements Snackbars from Google's Material Design documentation. 10 | Works on API levels >= 8 11 | 12 | 13 | Get it on Google Play 15 | 16 | 17 | Example App 18 | Example App 19 | Example App 20 | Example App 21 | Example App 22 | 23 | ## Installation 24 | You can import the library from source as a module or grab via Gradle: 25 | 26 | ```groovy 27 | compile 'com.nispok:snackbar:2.11.+' 28 | ``` 29 | ## Usage 30 | 31 | Using the `Snackbar` class is easy, this is how you would display it on an `Activity`: 32 | 33 | ```java 34 | Snackbar.with(getApplicationContext()) // context 35 | .text("Single-line snackbar") // text to display 36 | .show(this); // activity where it is displayed 37 | ``` 38 | However, I recommend you use the `SnackbarManager` to handle the Snackbars queue: 39 | 40 | ```java 41 | // Dismisses the Snackbar being shown, if any, and displays the new one 42 | SnackbarManager.show( 43 | Snackbar.with(myActivity) 44 | .text("Single-line snackbar")); 45 | ``` 46 | If you are using `getApplicationContext()` as the `Context` to create the `Snackbar` then you must 47 | specify the target `Activity` when calling the `SnackbarManager`: 48 | 49 | ```java 50 | // Dismisses the Snackbar being shown, if any, and displays the new one 51 | SnackbarManager.show( 52 | Snackbar.with(getApplicationContext()) 53 | .text("Single-line snackbar"), myActivity); 54 | ``` 55 | You can place the `Snackbar` at the bottom of a particular hierarchy of views. The sample app makes 56 | use of this; check out [SnackbarImmersiveModeSampleActivity](./sample/src/main/java/com/nispok/samples/snackbar/SnackbarImmersiveModeSampleActivity.java): 57 | ```java 58 | SnackbarManager.show(Snackbar snackbar, ViewGroup parent) { } 59 | SnackbarManager.show(Snackbar snackbar, ViewGroup parent, boolean usePhoneLayout) { } 60 | ``` 61 | If you want an action button to be displayed, just assign a label and an `ActionClickListener`: 62 | 63 | ```java 64 | SnackbarManager.show( 65 | Snackbar.with(getApplicationContext()) // context 66 | .text("Item deleted") // text to display 67 | .actionLabel("Undo") // action button label 68 | .actionListener(new ActionClickListener() { 69 | @Override 70 | public void onActionClicked(Snackbar snackbar) { 71 | Log.d(TAG, "Undoing something"); 72 | } 73 | }) // action button's ActionClickListener 74 | , this); // activity where it is displayed 75 | ``` 76 | If you need to know when the `Snackbar` is shown or dismissed, assign a `EventListener` to it. 77 | This is useful if you need to move other objects while the `Snackbar` is displayed. For instance, 78 | you can move a Floating Action Button up while the `Snackbar` is on screen. Note that if you only 79 | need to override a subset of the interface methods you can extend from `EventListenerAdapter`: 80 | 81 | ```java 82 | SnackbarManager.show( 83 | Snackbar.with(getApplicationContext()) // context 84 | .text("This will do something when dismissed") // text to display 85 | .eventListener(new EventListener() { 86 | @Override 87 | public void onShow(Snackbar snackbar) { 88 | myFloatingActionButton.moveUp(snackbar.getHeight()); 89 | } 90 | @Override 91 | public void onShowByReplace(Snackbar snackbar) { 92 | Log.i(TAG, String.format("Snackbar will show by replace. Width: %d Height: %d Offset: %d", 93 | snackbar.getWidth(), snackbar.getHeight(), 94 | snackbar.getOffset())); 95 | } 96 | @Override 97 | public void onShown(Snackbar snackbar) { 98 | Log.i(TAG, String.format("Snackbar shown. Width: %d Height: %d Offset: %d", 99 | snackbar.getWidth(), snackbar.getHeight(), 100 | snackbar.getOffset())); 101 | } 102 | @Override 103 | public void onDismiss(Snackbar snackbar) { 104 | myFloatingActionButton.moveDown(snackbar.getHeight()); 105 | } 106 | @Override 107 | public void onDismissByReplace(Snackbar snackbar) { 108 | Log.i(TAG, String.format( 109 | "Snackbar will dismiss by replace. Width: %d Height: %d Offset: %d", 110 | snackbar.getWidth(), snackbar.getHeight(), 111 | snackbar.getOffset())); 112 | } 113 | @Override 114 | public void onDismissed(Snackbar snackbar) { 115 | Log.i(TAG, String.format("Snackbar dismissed. Width: %d Height: %d Offset: %d", 116 | snackbar.getWidth(), snackbar.getHeight(), 117 | snackbar.getOffset())); 118 | } 119 | }) // Snackbar's EventListener 120 | , this); // activity where it is displayed 121 | ``` 122 | There are two `Snackbar` types: single-line (default) and multi-line (2 lines max. Note this only applies for phones; tablets are always single-line). You can also set 123 | the duration of the `Snackbar` similar to a 124 | `Toast`. 125 | 126 | The lengths of a Snackbar duration are: 127 | * `LENGTH_SHORT`: 2s 128 | * `LENGTH_LONG`: 3.5s (default) 129 | * `LENGTH_INDEFINTE`: Indefinite; ideal for persistent errors 130 | 131 | You could also set a custom duration. 132 | 133 | Animation disabling is also possible. 134 | 135 | ```java 136 | SnackbarManager.show( 137 | Snackbar.with(getApplicationContext()) // context 138 | .type(Snackbar.SnackbarType.MULTI_LINE) // Set is as a multi-line snackbar 139 | .text("This is a multi-line snackbar. Keep in mind that snackbars are " + 140 | "meant for VERY short messages") // text to be displayed 141 | .duration(Snackbar.SnackbarDuration.LENGTH_SHORT) // make it shorter 142 | .animation(false) // don't animate it 143 | , this); // where it is displayed 144 | ``` 145 | You can also change the `Snackbar`'s colors and fonts. 146 | 147 | ```java 148 | SnackbarManager.show( 149 | Snackbar.with(getApplicationContext()) // context 150 | .text("Different colors this time") // text to be displayed 151 | .textColor(Color.GREEN) // change the text color 152 | .textTypeface(myTypeface) // change the text font 153 | .color(Color.BLUE) // change the background color 154 | .actionLabel("Action") // action button label 155 | .actionColor(Color.RED) // action button label color 156 | .actionLabelTypeface(myTypeface) // change the action button font 157 | .actionListener(new ActionClickListener() { 158 | @Override 159 | public void onActionClicked(Snackbar snackbar) { 160 | Log.d(TAG, "Doing something"); 161 | } 162 | }) // action button's ActionClickListener 163 | , this); // activity where it is displayed 164 | ``` 165 | Finally, you can attach the `Snackbar` to a AbsListView (ListView, GridView) or a RecyclerView. 166 | 167 | ```java 168 | SnackbarManager.show( 169 | Snackbar.with(getApplicationContext()) // context 170 | .type(Snackbar.SnackbarType.MULTI_LINE) // Set is as a multi-line snackbar 171 | .text(R.string.message) // text to be displayed 172 | .duration(Snackbar.SnackbarDuration.LENGTH_LONG) 173 | .animation(false) // don't animate it 174 | .attachToAbsListView(listView) // Attach to ListView - attachToRecyclerView() is for RecyclerViews 175 | , this); // where it is displayed 176 | ``` 177 | It uses [Roman Nurik's SwipeToDismiss sample code](https://github.com/romannurik/android-swipetodismiss) 178 | to implement the swipe-to-dismiss functionality. This is enabled by default. You can disable this if 179 | you don't want this functionality: 180 | 181 | **NOTE:** This has no effect on apps running on APIs < 11; swiping will always be disabled in those cases 182 | 183 | ```java 184 | SnackbarManager.show( 185 | Snackbar.with(SnackbarSampleActivity.this) // context 186 | .text("Can't swipe this") // text to be displayed 187 | .swipeToDismiss(false) // disable swipe-to-dismiss functionality 188 | , this); // activity where it is displayed 189 | ``` 190 | 191 | # Examples 192 | There's a sample app included in the project. [SnackbarSampleActivity](./sample/src/main/java/com/nispok/samples/snackbar/SnackbarSampleActivity.java) is where you want to start. 193 | 194 | # Apps Using Snackbar 195 | * [Imagine for Instagram](https://play.google.com/store/apps/details?id=com.imagine.free) 196 | * [Kuantize](https://play.google.com/store/apps/details?id=eu.pedrofonseca.compras) 197 | 198 | # Contributing 199 | If you would like to add features or report any bugs, open a [PR](https://github.com/nispok/snackbar/pulls) or refer to the [issues](https://github.com/nispok/snackbar/issues) section. 200 | 201 | # Contributors 202 | Thanks to all [contributors](https://github.com/nispok/snackbar/graphs/contributors)! 203 | 204 | # License 205 | [MIT](./LICENSE) 206 | 207 | # ChangeLog 208 | Go to the [releases](https://github.com/nispok/snackbar/releases) section for a brief description of each release. 209 | -------------------------------------------------------------------------------- /art/home-1line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nispok/snackbar/a4e0449874423031107f6aaa7e97d0f1714a1d3b/art/home-1line.png -------------------------------------------------------------------------------- /art/home-2line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nispok/snackbar/a4e0449874423031107f6aaa7e97d0f1714a1d3b/art/home-2line.png -------------------------------------------------------------------------------- /art/home-colored.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nispok/snackbar/a4e0449874423031107f6aaa7e97d0f1714a1d3b/art/home-colored.png -------------------------------------------------------------------------------- /art/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nispok/snackbar/a4e0449874423031107f6aaa7e97d0f1714a1d3b/art/home.png -------------------------------------------------------------------------------- /art/list-1line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nispok/snackbar/a4e0449874423031107f6aaa7e97d0f1714a1d3b/art/list-1line.png -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.1.3' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Settings specified in this file will override any Gradle settings 5 | # configured through the IDE. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nispok/snackbar/a4e0449874423031107f6aaa7e97d0f1714a1d3b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Nov 28 20:15:55 GMT-03:00 2014 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /lib/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 22 5 | buildToolsVersion "22.0.1" 6 | 7 | defaultConfig { 8 | minSdkVersion 8 9 | targetSdkVersion 22 10 | } 11 | buildTypes { 12 | release { 13 | minifyEnabled false 14 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 15 | } 16 | } 17 | } 18 | 19 | dependencies { 20 | compile 'com.android.support:recyclerview-v7:22.0.0' 21 | compile 'com.android.support:support-annotations:22.0.0' 22 | } 23 | 24 | apply from: 'maven-push.gradle' -------------------------------------------------------------------------------- /lib/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Snackbar 2 | POM_ARTIFACT_ID=snackbar 3 | POM_PACKAGING=aar 4 | GROUP=com.nispok 5 | VERSION_NAME=2.11.0 6 | 7 | POM_DESCRIPTION=Snackbar Android library 8 | POM_URL=https://github.com/nispok/snackbar 9 | POM_SCM_URL=https://github.com/nispok/snackbar 10 | POM_SCM_CONNECTION=scm:hg:https://github.com/nispok/snackbar 11 | POM_SCM_DEV_CONNECTION=scm:hg:https://github.com/nispok/snackbar 12 | POM_LICENCE_NAME=MIT License 13 | POM_LICENCE_URL=http://www.opensource.org/licenses/mit-license.php 14 | POM_LICENCE_DIST=repo 15 | POM_DEVELOPER_ID=nispok 16 | POM_DEVELOPER_NAME=Nispok 17 | 18 | SNAPSHOT_REPOSITORY_URL=https://oss.sonatype.org/content/repositories/snapshots 19 | RELEASE_REPOSITORY_URL=https://oss.sonatype.org/service/local/staging/deploy/maven2 20 | -------------------------------------------------------------------------------- /lib/maven-push.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'maven' 2 | apply plugin: 'signing' 3 | 4 | def isReleaseBuild() { 5 | return VERSION_NAME.contains("-SNAPSHOT") == false 6 | } 7 | 8 | def getReleaseRepositoryUrl() { 9 | return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL 10 | : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 11 | } 12 | 13 | def getSnapshotRepositoryUrl() { 14 | return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL 15 | : "https://oss.sonatype.org/content/repositories/snapshots/" 16 | } 17 | 18 | def getRepositoryUsername() { 19 | return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" 20 | } 21 | 22 | def getRepositoryPassword() { 23 | return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "" 24 | } 25 | 26 | afterEvaluate { project -> 27 | uploadArchives { 28 | repositories { 29 | mavenDeployer { 30 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 31 | 32 | pom.groupId = GROUP 33 | pom.artifactId = POM_ARTIFACT_ID 34 | pom.version = VERSION_NAME 35 | 36 | repository(url: getReleaseRepositoryUrl()) { 37 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 38 | } 39 | snapshotRepository(url: getSnapshotRepositoryUrl()) { 40 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 41 | } 42 | 43 | pom.project { 44 | name POM_NAME 45 | packaging POM_PACKAGING 46 | description POM_DESCRIPTION 47 | url POM_URL 48 | 49 | scm { 50 | url POM_SCM_URL 51 | connection POM_SCM_CONNECTION 52 | developerConnection POM_SCM_DEV_CONNECTION 53 | } 54 | 55 | licenses { 56 | license { 57 | name POM_LICENCE_NAME 58 | url POM_LICENCE_URL 59 | distribution POM_LICENCE_DIST 60 | } 61 | } 62 | 63 | developers { 64 | developer { 65 | id POM_DEVELOPER_ID 66 | name POM_DEVELOPER_NAME 67 | } 68 | } 69 | } 70 | } 71 | } 72 | } 73 | 74 | signing { 75 | required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } 76 | sign configurations.archives 77 | } 78 | 79 | task androidSourcesJar(type: Jar) { 80 | classifier = 'sources' 81 | from android.sourceSets.main.java.sourceFiles 82 | } 83 | 84 | artifacts { 85 | archives androidSourcesJar 86 | } 87 | } -------------------------------------------------------------------------------- /lib/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Applications/Android Studio.app/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /lib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /lib/src/main/java/com/nispok/snackbar/DisplayCompat.java: -------------------------------------------------------------------------------- 1 | package com.nispok.snackbar; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Point; 5 | import android.os.Build; 6 | import android.view.Display; 7 | 8 | class DisplayCompat { 9 | static abstract class Impl { 10 | abstract void getSize(Display display, Point outSize); 11 | 12 | abstract void getRealSize(Display display, Point outSize); 13 | } 14 | 15 | private static final Impl IMPL; 16 | 17 | static { 18 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 19 | IMPL = new DisplayCompatImplJBMR1(); 20 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { 21 | IMPL = new DisplayCompatImplHoneycombMR2(); 22 | } else { 23 | IMPL = new DisplayCompatImplPreHoneycombMR2(); 24 | } 25 | } 26 | 27 | public static void getSize(Display display, Point outSize) { 28 | IMPL.getSize(display, outSize); 29 | } 30 | 31 | public static void getRealSize(Display display, Point outSize) { 32 | IMPL.getRealSize(display, outSize); 33 | } 34 | 35 | public static int getWidthFromPercentage(Activity targetActivity, Float mMaxWidthPercentage) { 36 | Display display = targetActivity.getWindowManager().getDefaultDisplay(); 37 | Point dispSize = new Point(); 38 | getRealSize(display, dispSize); 39 | 40 | return (int) (dispSize.x * mMaxWidthPercentage); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/src/main/java/com/nispok/snackbar/DisplayCompatImplHoneycombMR2.java: -------------------------------------------------------------------------------- 1 | package com.nispok.snackbar; 2 | 3 | import android.annotation.TargetApi; 4 | import android.graphics.Point; 5 | import android.os.Build; 6 | import android.view.Display; 7 | 8 | @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) 9 | class DisplayCompatImplHoneycombMR2 extends DisplayCompat.Impl { 10 | @Override 11 | void getSize(Display display, Point outSize) { 12 | display.getSize(outSize); 13 | } 14 | 15 | @Override 16 | void getRealSize(Display display, Point outSize) { 17 | display.getSize(outSize); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/src/main/java/com/nispok/snackbar/DisplayCompatImplJBMR1.java: -------------------------------------------------------------------------------- 1 | package com.nispok.snackbar; 2 | 3 | import android.annotation.TargetApi; 4 | import android.graphics.Point; 5 | import android.os.Build; 6 | import android.view.Display; 7 | 8 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) 9 | class DisplayCompatImplJBMR1 extends DisplayCompat.Impl { 10 | @Override 11 | void getSize(Display display, Point outSize) { 12 | display.getSize(outSize); 13 | } 14 | 15 | @Override 16 | void getRealSize(Display display, Point outSize) { 17 | display.getRealSize(outSize); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/src/main/java/com/nispok/snackbar/DisplayCompatImplPreHoneycombMR2.java: -------------------------------------------------------------------------------- 1 | package com.nispok.snackbar; 2 | 3 | import android.graphics.Point; 4 | import android.view.Display; 5 | 6 | @SuppressWarnings("deprecation") 7 | class DisplayCompatImplPreHoneycombMR2 extends DisplayCompat.Impl { 8 | @Override 9 | void getSize(Display display, Point outSize) { 10 | outSize.x = display.getWidth(); 11 | outSize.y = display.getHeight(); 12 | } 13 | 14 | @Override 15 | void getRealSize(Display display, Point outSize) { 16 | outSize.x = display.getWidth(); 17 | outSize.y = display.getHeight(); 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /lib/src/main/java/com/nispok/snackbar/RecyclerUtil.java: -------------------------------------------------------------------------------- 1 | package com.nispok.snackbar; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.View; 5 | 6 | /** 7 | * RecyclerView is a provided dependency, so in order to avoid burdening developers with a 8 | * potentially unnecessary dependency, we move the RecyclerView-related code here and only call it 9 | * if we confirm that they've provided it themselves. 10 | */ 11 | class RecyclerUtil { 12 | static void setScrollListener(final Snackbar snackbar, View view) { 13 | RecyclerView recyclerView = (RecyclerView) view; 14 | recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() { 15 | @Override 16 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 17 | super.onScrollStateChanged(recyclerView, newState); 18 | snackbar.dismiss(); 19 | } 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/main/java/com/nispok/snackbar/Snackbar.java: -------------------------------------------------------------------------------- 1 | package com.nispok.snackbar; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.content.res.Resources; 7 | import android.graphics.Point; 8 | import android.graphics.Rect; 9 | import android.graphics.Typeface; 10 | import android.graphics.drawable.Drawable; 11 | import android.graphics.drawable.GradientDrawable; 12 | import android.os.Build; 13 | import android.support.annotation.AnimRes; 14 | import android.support.annotation.ColorRes; 15 | import android.support.annotation.DrawableRes; 16 | import android.support.annotation.StringRes; 17 | import android.support.v4.view.accessibility.AccessibilityEventCompat; 18 | import android.text.TextUtils; 19 | import android.view.Display; 20 | import android.view.Gravity; 21 | import android.view.LayoutInflater; 22 | import android.view.View; 23 | import android.view.ViewGroup; 24 | import android.view.ViewTreeObserver; 25 | import android.view.WindowManager; 26 | import android.view.accessibility.AccessibilityEvent; 27 | import android.view.animation.Animation; 28 | import android.view.animation.AnimationUtils; 29 | import android.widget.AbsListView; 30 | import android.widget.FrameLayout; 31 | import android.widget.LinearLayout; 32 | import android.widget.RelativeLayout; 33 | import android.widget.TextView; 34 | 35 | import com.nispok.snackbar.enums.SnackbarType; 36 | import com.nispok.snackbar.layouts.SnackbarLayout; 37 | import com.nispok.snackbar.listeners.ActionClickListener; 38 | import com.nispok.snackbar.listeners.ActionSwipeListener; 39 | import com.nispok.snackbar.listeners.EventListener; 40 | import com.nispok.snackbar.listeners.SwipeDismissTouchListener; 41 | 42 | /** 43 | * View that provides quick feedback about an operation in a small popup at the base of the screen 44 | */ 45 | public class Snackbar extends SnackbarLayout { 46 | 47 | public enum SnackbarDuration { 48 | LENGTH_SHORT(2000), LENGTH_LONG(3500), LENGTH_INDEFINITE(-1); 49 | 50 | private long duration; 51 | 52 | SnackbarDuration(long duration) { 53 | this.duration = duration; 54 | } 55 | 56 | public long getDuration() { 57 | return duration; 58 | } 59 | } 60 | 61 | public static enum SnackbarPosition { 62 | TOP(Gravity.TOP), BOTTOM(Gravity.BOTTOM), BOTTOM_CENTER(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL); 63 | 64 | private int layoutGravity; 65 | 66 | SnackbarPosition(int layoutGravity) { 67 | this.layoutGravity = layoutGravity; 68 | } 69 | 70 | public int getLayoutGravity() { 71 | return layoutGravity; 72 | } 73 | } 74 | 75 | private int mUndefinedColor = -10000; 76 | private int mUndefinedDrawable = -10000; 77 | 78 | private SnackbarType mType = SnackbarType.SINGLE_LINE; 79 | private SnackbarDuration mDuration = SnackbarDuration.LENGTH_LONG; 80 | private CharSequence mText; 81 | private TextView snackbarText; 82 | private TextView snackbarAction; 83 | private int mColor = mUndefinedColor; 84 | private int mTextColor = mUndefinedColor; 85 | private int mOffset; 86 | private Integer mLineColor; 87 | private SnackbarPosition mPhonePosition = SnackbarPosition.BOTTOM; 88 | private SnackbarPosition mWidePosition = SnackbarPosition.BOTTOM_CENTER; 89 | private int mDrawable = mUndefinedDrawable; 90 | private int mMarginTop = 0; 91 | private int mMarginBottom = 0; 92 | private int mMarginLeft = 0; 93 | private int mMarginRight = 0; 94 | private long mSnackbarStart; 95 | private long mSnackbarFinish; 96 | private long mTimeRemaining = -1; 97 | private CharSequence mActionLabel; 98 | private int mActionColor = mUndefinedColor; 99 | private boolean mShowAnimated = true; 100 | private boolean mDismissAnimated = true; 101 | private boolean mIsReplacePending = false; 102 | private boolean mIsShowingByReplace = false; 103 | private long mCustomDuration = -1; 104 | private ActionClickListener mActionClickListener; 105 | private ActionSwipeListener mActionSwipeListener; 106 | private boolean mShouldAllowMultipleActionClicks; 107 | private boolean mActionClicked; 108 | private boolean mShouldDismissOnActionClicked = true; 109 | private EventListener mEventListener; 110 | private Typeface mTextTypeface; 111 | private Typeface mActionTypeface; 112 | private boolean mIsShowing = false; 113 | private boolean mCanSwipeToDismiss = true; 114 | private boolean mIsDismissing = false; 115 | private Rect mWindowInsets = new Rect(); 116 | private Rect mDisplayFrame = new Rect(); 117 | private Point mDisplaySize = new Point(); 118 | private Point mRealDisplaySize = new Point(); 119 | private Activity mTargetActivity; 120 | private Float mMaxWidthPercentage = null; 121 | private boolean mUsePhoneLayout; 122 | private Runnable mDismissRunnable = new Runnable() { 123 | @Override 124 | public void run() { 125 | dismiss(); 126 | } 127 | }; 128 | private Runnable mRefreshLayoutParamsMarginsRunnable = new Runnable() { 129 | @Override 130 | public void run() { 131 | refreshLayoutParamsMargins(); 132 | } 133 | }; 134 | 135 | private Snackbar(Context context) { 136 | super(context); 137 | 138 | // inject helper view to use onWindowSystemUiVisibilityChangedCompat() event 139 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 140 | addView(new SnackbarHelperChildViewJB(getContext())); 141 | } 142 | } 143 | 144 | public static Snackbar with(Context context) { 145 | return new Snackbar(context); 146 | } 147 | 148 | /** 149 | * Sets the type of {@link Snackbar} to be displayed. 150 | * 151 | * @param type the {@link SnackbarType} of this instance 152 | * @return 153 | */ 154 | public Snackbar type(SnackbarType type) { 155 | mType = type; 156 | return this; 157 | } 158 | 159 | /** 160 | * Sets the text to be displayed in this {@link Snackbar} 161 | * 162 | * @param text 163 | * @return 164 | */ 165 | public Snackbar text(CharSequence text) { 166 | mText = text; 167 | if (snackbarText != null) { 168 | snackbarText.setText(mText); 169 | } 170 | return this; 171 | } 172 | 173 | /** 174 | * Sets the text to be displayed in this {@link Snackbar} 175 | * 176 | * @param resId 177 | * @return 178 | */ 179 | public Snackbar text(@StringRes int resId) { 180 | return text(getContext().getText(resId)); 181 | } 182 | 183 | /** 184 | * Sets the background color of this {@link Snackbar} 185 | * 186 | * @param color 187 | * @return 188 | */ 189 | public Snackbar color(int color) { 190 | mColor = color; 191 | return this; 192 | } 193 | 194 | /** 195 | * Sets the background color of this {@link Snackbar} 196 | * 197 | * @param resId 198 | * @return 199 | */ 200 | public Snackbar colorResource(@ColorRes int resId) { 201 | return color(getResources().getColor(resId)); 202 | } 203 | 204 | /** 205 | * Sets the background drawable of this {@link Snackbar} 206 | * 207 | * @param resId 208 | * @return 209 | */ 210 | public Snackbar backgroundDrawable(@DrawableRes int resId) { 211 | mDrawable = resId; 212 | return this; 213 | } 214 | 215 | /** 216 | * Sets the text color of this {@link Snackbar} 217 | * 218 | * @param textColor 219 | * @return 220 | */ 221 | public Snackbar textColor(int textColor) { 222 | mTextColor = textColor; 223 | return this; 224 | } 225 | 226 | /** 227 | * Sets the text color of this {@link Snackbar} 228 | * 229 | * @param resId 230 | * @return 231 | */ 232 | public Snackbar textColorResource(@ColorRes int resId) { 233 | return textColor(getResources().getColor(resId)); 234 | } 235 | 236 | /** 237 | * Sets the text color of this {@link Snackbar}'s top line, or null for none 238 | * 239 | * @param lineColor 240 | * @return 241 | */ 242 | public Snackbar lineColor(Integer lineColor) { 243 | mLineColor = lineColor; 244 | return this; 245 | } 246 | 247 | /** 248 | * Sets the text color of this {@link Snackbar}'s top line 249 | * 250 | * @param resId 251 | * @return 252 | */ 253 | public Snackbar lineColorResource(@ColorRes int resId) { 254 | return lineColor(getResources().getColor(resId)); 255 | } 256 | 257 | /** 258 | * Sets the action label to be displayed, if any. Note that if this is not set, the action 259 | * button will not be displayed 260 | * 261 | * @param actionButtonLabel 262 | * @return 263 | */ 264 | public Snackbar actionLabel(CharSequence actionButtonLabel) { 265 | mActionLabel = actionButtonLabel; 266 | if (snackbarAction != null) { 267 | snackbarAction.setText(mActionLabel); 268 | } 269 | return this; 270 | } 271 | 272 | /** 273 | * Sets the action label to be displayed, if any. Note that if this is not set, the action 274 | * button will not be displayed 275 | * 276 | * @param resId 277 | * @return 278 | */ 279 | public Snackbar actionLabel(@StringRes int resId) { 280 | return actionLabel(getContext().getString(resId)); 281 | } 282 | 283 | /** 284 | * Set the position of the {@link Snackbar}. Note that if this is not set, the default is to 285 | * show the snackbar to the bottom of the screen. 286 | * 287 | * @param position 288 | * @return 289 | */ 290 | public Snackbar position(SnackbarPosition position) { 291 | mPhonePosition = position; 292 | return this; 293 | } 294 | 295 | /** 296 | * Set the position for wide screen (tablets | desktop) of the {@link Snackbar}. Note that if this is not set, the default is to 297 | * show the snackbar to the bottom | center of the screen. 298 | * 299 | * @param position A {@link com.nispok.snackbar.Snackbar.SnackbarPosition} 300 | * @return A {@link Snackbar} instance to make changing 301 | */ 302 | public Snackbar widePosition(SnackbarPosition position){ 303 | mWidePosition = position; 304 | return this; 305 | } 306 | 307 | /** 308 | * Sets all the margins of the {@link Snackbar} to the same value, in pixels 309 | * 310 | * @param margin 311 | * @return 312 | */ 313 | public Snackbar margin(int margin) { 314 | return margin(margin, margin, margin, margin); 315 | } 316 | 317 | /** 318 | * Sets the margins of the {@link Snackbar} in pixels such that the left and right are equal, and the top and bottom are equal 319 | * 320 | * @param marginLR 321 | * @param marginTB 322 | * @return 323 | */ 324 | public Snackbar margin(int marginLR, int marginTB) { 325 | return margin(marginLR, marginTB, marginLR, marginTB); 326 | } 327 | 328 | /** 329 | * Sets all the margin of the {@link Snackbar} individually, in pixels 330 | * 331 | * @param marginLeft 332 | * @param marginTop 333 | * @param marginRight 334 | * @param marginBottom 335 | * @return 336 | */ 337 | public Snackbar margin(int marginLeft, int marginTop, int marginRight, int marginBottom) { 338 | mMarginLeft = marginLeft; 339 | mMarginTop = marginTop; 340 | mMarginBottom = marginBottom; 341 | mMarginRight = marginRight; 342 | 343 | return this; 344 | } 345 | 346 | /** 347 | * Sets the color of the action button label. Note that you must set a button label with 348 | * {@link Snackbar#actionLabel(CharSequence)} for this button to be displayed 349 | * 350 | * @param actionColor 351 | * @return 352 | */ 353 | public Snackbar actionColor(int actionColor) { 354 | mActionColor = actionColor; 355 | return this; 356 | } 357 | 358 | /** 359 | * Sets the color of the action button label. Note that you must set a button label with 360 | * {@link Snackbar#actionLabel(CharSequence)} for this button to be displayed 361 | * 362 | * @param resId 363 | * @return 364 | */ 365 | public Snackbar actionColorResource(@ColorRes int resId) { 366 | return actionColor(getResources().getColor(resId)); 367 | } 368 | 369 | /** 370 | * Determines whether this {@link Snackbar} should dismiss when the action button is touched 371 | * 372 | * @param shouldDismiss 373 | * @return 374 | */ 375 | public Snackbar dismissOnActionClicked(boolean shouldDismiss) { 376 | mShouldDismissOnActionClicked = shouldDismiss; 377 | return this; 378 | } 379 | 380 | /** 381 | * Sets the listener to be called when the {@link Snackbar} action is 382 | * selected. Note that you must set a button label with 383 | * {@link Snackbar#actionLabel(CharSequence)} for this button to be displayed 384 | * 385 | * @param listener 386 | * @return 387 | */ 388 | public Snackbar actionListener(ActionClickListener listener) { 389 | mActionClickListener = listener; 390 | return this; 391 | } 392 | 393 | 394 | /** 395 | * Sets the listener to be called when the {@link Snackbar} is dismissed by swipe. 396 | * 397 | * @param listener 398 | * @return 399 | */ 400 | public Snackbar swipeListener(ActionSwipeListener listener) { 401 | mActionSwipeListener = listener; 402 | return this; 403 | } 404 | 405 | /** 406 | * Determines whether this {@link Snackbar} should allow the action button to be 407 | * clicked multiple times 408 | * 409 | * @param shouldAllow 410 | * @return 411 | */ 412 | public Snackbar allowMultipleActionClicks(boolean shouldAllow) { 413 | 414 | mShouldAllowMultipleActionClicks = shouldAllow; 415 | return this; 416 | } 417 | 418 | /** 419 | * Sets the listener to be called when the {@link Snackbar} is dismissed. 420 | * 421 | * @param listener 422 | * @return 423 | */ 424 | public Snackbar eventListener(EventListener listener) { 425 | mEventListener = listener; 426 | return this; 427 | } 428 | 429 | /** 430 | * Sets on/off both show and dismiss animations for this {@link Snackbar} 431 | * 432 | * @param withAnimation 433 | * @return 434 | */ 435 | public Snackbar animation(boolean withAnimation) { 436 | mShowAnimated = withAnimation; 437 | mDismissAnimated = withAnimation; 438 | return this; 439 | } 440 | 441 | /** 442 | * Sets on/off show animation for this {@link Snackbar} 443 | * 444 | * @param withAnimation 445 | * @return 446 | */ 447 | public Snackbar showAnimation(boolean withAnimation) { 448 | mShowAnimated = withAnimation; 449 | return this; 450 | } 451 | 452 | /** 453 | * Sets on/off dismiss animation for this {@link Snackbar} 454 | * 455 | * @param withAnimation 456 | * @return 457 | */ 458 | public Snackbar dismissAnimation(boolean withAnimation) { 459 | mDismissAnimated = withAnimation; 460 | return this; 461 | } 462 | 463 | /** 464 | * Determines whether this {@link com.nispok.snackbar.Snackbar} can be swiped off from the screen 465 | * 466 | * @param canSwipeToDismiss 467 | * @return 468 | */ 469 | public Snackbar swipeToDismiss(boolean canSwipeToDismiss) { 470 | mCanSwipeToDismiss = canSwipeToDismiss; 471 | return this; 472 | } 473 | 474 | /** 475 | * Sets the duration of this {@link Snackbar}. See 476 | * {@link Snackbar.SnackbarDuration} for available options 477 | * 478 | * @param duration 479 | * @return 480 | */ 481 | public Snackbar duration(SnackbarDuration duration) { 482 | mDuration = duration; 483 | return this; 484 | } 485 | 486 | /** 487 | * Sets a custom duration of this {@link Snackbar} 488 | * 489 | * @param duration custom duration. Value must be greater than 0 or it will be ignored 490 | * @return 491 | */ 492 | public Snackbar duration(long duration) { 493 | mCustomDuration = duration > 0 ? duration : mCustomDuration; 494 | return this; 495 | } 496 | 497 | /** 498 | * Attaches this {@link Snackbar} to an AbsListView (ListView, GridView, ExpandableListView) so 499 | * it dismisses when the list is scrolled 500 | * 501 | * @param absListView 502 | * @return 503 | */ 504 | public Snackbar attachToAbsListView(AbsListView absListView) { 505 | absListView.setOnScrollListener(new AbsListView.OnScrollListener() { 506 | @Override 507 | public void onScrollStateChanged(AbsListView view, int scrollState) { 508 | dismiss(); 509 | } 510 | 511 | @Override 512 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, 513 | int totalItemCount) { 514 | } 515 | }); 516 | 517 | return this; 518 | } 519 | 520 | /** 521 | * Attaches this {@link Snackbar} to a RecyclerView so it dismisses when the list is scrolled 522 | * 523 | * @param recyclerView The RecyclerView instance to attach to. 524 | * @return 525 | */ 526 | public Snackbar attachToRecyclerView(View recyclerView) { 527 | 528 | try { 529 | Class.forName("android.support.v7.widget.RecyclerView"); 530 | 531 | // We got here, so now we can safely check 532 | RecyclerUtil.setScrollListener(this, recyclerView); 533 | } catch (ClassNotFoundException ignored) { 534 | throw new IllegalArgumentException("RecyclerView not found. Did you add it to your dependencies?"); 535 | } 536 | 537 | return this; 538 | } 539 | 540 | /** 541 | * Use a custom typeface for this Snackbar's text 542 | * 543 | * @param typeface 544 | * @return 545 | */ 546 | public Snackbar textTypeface(Typeface typeface) { 547 | mTextTypeface = typeface; 548 | return this; 549 | } 550 | 551 | /** 552 | * Use a custom typeface for this Snackbar's action label 553 | * 554 | * @param typeface 555 | * @return 556 | */ 557 | public Snackbar actionLabelTypeface(Typeface typeface) { 558 | mActionTypeface = typeface; 559 | return this; 560 | } 561 | 562 | private static MarginLayoutParams createMarginLayoutParams(ViewGroup viewGroup, int width, int height, SnackbarPosition position) { 563 | if (viewGroup instanceof FrameLayout) { 564 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width, height); 565 | params.gravity = position.getLayoutGravity(); 566 | return params; 567 | } else if (viewGroup instanceof RelativeLayout) { 568 | RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height); 569 | 570 | if (position == SnackbarPosition.TOP) 571 | params.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE); 572 | else 573 | params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE); 574 | 575 | return params; 576 | } else if (viewGroup instanceof LinearLayout) { 577 | LinearLayout.LayoutParams params = new LayoutParams(width, height); 578 | params.gravity = position.getLayoutGravity(); 579 | return params; 580 | } else { 581 | throw new IllegalStateException("Requires FrameLayout or RelativeLayout for the parent of Snackbar"); 582 | } 583 | } 584 | 585 | static boolean shouldUsePhoneLayout(Context context) { 586 | if (context == null) { 587 | return true; 588 | } else { 589 | return context.getResources().getBoolean(R.bool.sb__is_phone); 590 | } 591 | } 592 | 593 | private MarginLayoutParams init(Context context, Activity targetActivity, ViewGroup parent, boolean usePhoneLayout) { 594 | SnackbarLayout layout = (SnackbarLayout) LayoutInflater.from(context) 595 | .inflate(R.layout.sb__template, this, true); 596 | layout.setOrientation(LinearLayout.VERTICAL); 597 | 598 | Resources res = getResources(); 599 | mColor = mColor != mUndefinedColor ? mColor : res.getColor(R.color.sb__background); 600 | mOffset = res.getDimensionPixelOffset(R.dimen.sb__offset); 601 | mUsePhoneLayout = usePhoneLayout; 602 | float scale = res.getDisplayMetrics().density; 603 | 604 | View divider = layout.findViewById(R.id.sb__divider); 605 | 606 | MarginLayoutParams params; 607 | if (mUsePhoneLayout) { 608 | // Phone 609 | layout.setMinimumHeight(dpToPx(mType.getMinHeight(), scale)); 610 | layout.setMaxHeight(dpToPx(mType.getMaxHeight(), scale)); 611 | layout.setBackgroundColor(mColor); 612 | params = createMarginLayoutParams( 613 | parent, FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT, mPhonePosition); 614 | 615 | if (mLineColor != null) { 616 | divider.setBackgroundColor(mLineColor); 617 | } else { 618 | divider.setVisibility(View.GONE); 619 | } 620 | } else { 621 | // Tablet/desktop 622 | mType = SnackbarType.SINGLE_LINE; // Force single-line 623 | layout.setMinimumWidth(res.getDimensionPixelSize(R.dimen.sb__min_width)); 624 | layout.setMaxWidth( 625 | mMaxWidthPercentage == null 626 | ? res.getDimensionPixelSize(R.dimen.sb__max_width) 627 | : DisplayCompat.getWidthFromPercentage(targetActivity, mMaxWidthPercentage)); 628 | layout.setBackgroundResource(R.drawable.sb__bg); 629 | GradientDrawable bg = (GradientDrawable) layout.getBackground(); 630 | bg.setColor(mColor); 631 | 632 | params = createMarginLayoutParams( 633 | parent, FrameLayout.LayoutParams.WRAP_CONTENT, dpToPx(mType.getMaxHeight(), scale), mWidePosition); 634 | 635 | if (mLineColor != null) { 636 | divider.setBackgroundResource(R.drawable.sb__divider_bg); 637 | GradientDrawable dbg = (GradientDrawable) divider.getBackground(); 638 | dbg.setColor(mLineColor); 639 | } else { 640 | divider.setVisibility(View.GONE); 641 | } 642 | } 643 | 644 | if (mDrawable != mUndefinedDrawable) 645 | setBackgroundDrawable(layout, res.getDrawable(mDrawable)); 646 | 647 | snackbarText = (TextView) layout.findViewById(R.id.sb__text); 648 | snackbarText.setText(mText); 649 | snackbarText.setTypeface(mTextTypeface); 650 | 651 | if (mTextColor != mUndefinedColor) { 652 | snackbarText.setTextColor(mTextColor); 653 | } 654 | 655 | snackbarText.setMaxLines(mType.getMaxLines()); 656 | 657 | snackbarAction = (TextView) layout.findViewById(R.id.sb__action); 658 | if (!TextUtils.isEmpty(mActionLabel)) { 659 | requestLayout(); 660 | snackbarAction.setText(mActionLabel); 661 | snackbarAction.setTypeface(mActionTypeface); 662 | 663 | if (mActionColor != mUndefinedColor) { 664 | snackbarAction.setTextColor(mActionColor); 665 | } 666 | 667 | snackbarAction.setOnClickListener(new OnClickListener() { 668 | @Override 669 | public void onClick(View view) { 670 | if (mActionClickListener != null) { 671 | 672 | // Before calling the onActionClicked() callback, make sure: 673 | // 1) The snackbar is not dismissing 674 | // 2) If we aren't allowing multiple clicks, that this is the first click 675 | if (!mIsDismissing && (!mActionClicked || mShouldAllowMultipleActionClicks)) { 676 | 677 | mActionClickListener.onActionClicked(Snackbar.this); 678 | mActionClicked = true; 679 | } 680 | } 681 | if (mShouldDismissOnActionClicked) { 682 | dismiss(); 683 | } 684 | } 685 | }); 686 | snackbarAction.setMaxLines(mType.getMaxLines()); 687 | } else { 688 | snackbarAction.setVisibility(GONE); 689 | } 690 | 691 | View inner = layout.findViewById(R.id.sb__inner); 692 | inner.setClickable(true); 693 | 694 | if (mCanSwipeToDismiss && res.getBoolean(R.bool.sb__is_swipeable)) { 695 | inner.setOnTouchListener(new SwipeDismissTouchListener(this, null, 696 | new SwipeDismissTouchListener.DismissCallbacks() { 697 | @Override 698 | public boolean canDismiss(Object token) { 699 | return true; 700 | } 701 | 702 | @Override 703 | public void onDismiss(View view, Object token) { 704 | if (view != null) { 705 | if (mActionSwipeListener != null) { 706 | mActionSwipeListener.onSwipeToDismiss(); 707 | } 708 | dismiss(false); 709 | } 710 | } 711 | 712 | @Override 713 | public void pauseTimer(boolean shouldPause) { 714 | if (isIndefiniteDuration()) { 715 | return; 716 | } 717 | if (shouldPause) { 718 | removeCallbacks(mDismissRunnable); 719 | 720 | mSnackbarFinish = System.currentTimeMillis(); 721 | } else { 722 | mTimeRemaining -= (mSnackbarFinish - mSnackbarStart); 723 | 724 | startTimer(mTimeRemaining); 725 | } 726 | } 727 | })); 728 | } 729 | 730 | return params; 731 | } 732 | 733 | 734 | private void updateWindowInsets(Activity targetActivity, Rect outInsets) { 735 | outInsets.left = outInsets.top = outInsets.right = outInsets.bottom = 0; 736 | 737 | if (targetActivity == null) { 738 | return; 739 | } 740 | 741 | ViewGroup decorView = (ViewGroup) targetActivity.getWindow().getDecorView(); 742 | Display display = targetActivity.getWindowManager().getDefaultDisplay(); 743 | 744 | boolean isTranslucent = isNavigationBarTranslucent(targetActivity); 745 | boolean isHidden = isNavigationBarHidden(decorView); 746 | 747 | Rect dispFrame = mDisplayFrame; 748 | Point realDispSize = mRealDisplaySize; 749 | Point dispSize = mDisplaySize; 750 | 751 | decorView.getWindowVisibleDisplayFrame(dispFrame); 752 | 753 | DisplayCompat.getRealSize(display, realDispSize); 754 | DisplayCompat.getSize(display, dispSize); 755 | 756 | if (dispSize.x < realDispSize.x) { 757 | // navigation bar is placed on right side of the screen 758 | if (isTranslucent || isHidden) { 759 | int navBarWidth = realDispSize.x - dispSize.x; 760 | int overlapWidth = realDispSize.x - dispFrame.right; 761 | outInsets.right = Math.max(Math.min(navBarWidth, overlapWidth), 0); 762 | } 763 | } else if (dispSize.y < realDispSize.y) { 764 | // navigation bar is placed on bottom side of the screen 765 | 766 | if (isTranslucent || isHidden) { 767 | int navBarHeight = realDispSize.y - dispSize.y; 768 | int overlapHeight = realDispSize.y - dispFrame.bottom; 769 | outInsets.bottom = Math.max(Math.min(navBarHeight, overlapHeight), 0); 770 | } 771 | } 772 | } 773 | 774 | private static int dpToPx(int dp, float scale) { 775 | return (int) (dp * scale + 0.5f); 776 | } 777 | 778 | public void showByReplace(Activity targetActivity) { 779 | mIsShowingByReplace = true; 780 | show(targetActivity); 781 | } 782 | 783 | public void showByReplace(ViewGroup parent) { 784 | mIsShowingByReplace = true; 785 | show(parent, shouldUsePhoneLayout(parent.getContext())); 786 | } 787 | 788 | public void showByReplace(ViewGroup parent, boolean usePhoneLayout) { 789 | mIsShowingByReplace = true; 790 | show(parent, usePhoneLayout); 791 | } 792 | 793 | /** 794 | * Displays the {@link Snackbar} at the bottom of the 795 | * {@link android.app.Activity} provided. 796 | * 797 | * @param targetActivity 798 | */ 799 | public void show(Activity targetActivity) { 800 | ViewGroup root = (ViewGroup) targetActivity.findViewById(android.R.id.content); 801 | boolean usePhoneLayout = shouldUsePhoneLayout(targetActivity); 802 | MarginLayoutParams params = init(targetActivity, targetActivity, root, usePhoneLayout); 803 | updateLayoutParamsMargins(targetActivity, params); 804 | showInternal(targetActivity, params, root); 805 | } 806 | 807 | /** 808 | * Displays the {@link Snackbar} at the bottom of the 809 | * {@link android.view.ViewGroup} provided. 810 | * 811 | * @param parent 812 | */ 813 | public void show(ViewGroup parent) { 814 | show(parent, shouldUsePhoneLayout(parent.getContext())); 815 | } 816 | 817 | /** 818 | * Displays the {@link Snackbar} at the bottom of the 819 | * {@link android.view.ViewGroup} provided. 820 | * 821 | * @param parent 822 | * @param usePhoneLayout 823 | */ 824 | public void show(ViewGroup parent, boolean usePhoneLayout) { 825 | MarginLayoutParams params = init(parent.getContext(), null, parent, usePhoneLayout); 826 | updateLayoutParamsMargins(null, params); 827 | showInternal(null, params, parent); 828 | } 829 | 830 | public Snackbar maxWidthPercentage(float maxWidthPercentage) { 831 | mMaxWidthPercentage = maxWidthPercentage; 832 | return this; 833 | } 834 | 835 | private void showInternal(Activity targetActivity, MarginLayoutParams params, ViewGroup parent) { 836 | parent.removeView(this); 837 | 838 | // We need to make sure the Snackbar elevation is at least as high as 839 | // any other child views, or it will be displayed underneath them 840 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 841 | for (int i = 0; i < parent.getChildCount(); i++) { 842 | View otherChild = parent.getChildAt(i); 843 | float elvation = otherChild.getElevation(); 844 | if (elvation > getElevation()) { 845 | setElevation(elvation); 846 | } 847 | } 848 | } 849 | parent.addView(this, params); 850 | 851 | bringToFront(); 852 | 853 | // As requested in the documentation for bringToFront() 854 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 855 | parent.requestLayout(); 856 | parent.invalidate(); 857 | } 858 | 859 | mIsShowing = true; 860 | mTargetActivity = targetActivity; 861 | 862 | getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { 863 | @Override 864 | public boolean onPreDraw() { 865 | getViewTreeObserver().removeOnPreDrawListener(this); 866 | if (mEventListener != null) { 867 | if (mIsShowingByReplace) { 868 | mEventListener.onShowByReplace(Snackbar.this); 869 | } else { 870 | mEventListener.onShow(Snackbar.this); 871 | } 872 | if (!mShowAnimated) { 873 | mEventListener.onShown(Snackbar.this); 874 | mIsShowingByReplace = false; // reset flag 875 | } 876 | } 877 | return true; 878 | } 879 | }); 880 | 881 | if (!mShowAnimated) { 882 | if (shouldStartTimer()) { 883 | startTimer(); 884 | } 885 | return; 886 | } 887 | 888 | Animation slideIn = AnimationUtils.loadAnimation(getContext(), getInAnimationResource(mPhonePosition)); 889 | slideIn.setAnimationListener(new Animation.AnimationListener() { 890 | @Override 891 | public void onAnimationStart(Animation animation) { 892 | } 893 | 894 | @Override 895 | public void onAnimationEnd(Animation animation) { 896 | if (mEventListener != null) { 897 | mEventListener.onShown(Snackbar.this); 898 | mIsShowingByReplace = false; // reset flag 899 | } 900 | 901 | focusForAccessibility(snackbarText); 902 | 903 | post(new Runnable() { 904 | @Override 905 | public void run() { 906 | mSnackbarStart = System.currentTimeMillis(); 907 | 908 | if (mTimeRemaining == -1) { 909 | mTimeRemaining = getDuration(); 910 | } 911 | if (shouldStartTimer()) { 912 | startTimer(); 913 | } 914 | } 915 | }); 916 | } 917 | 918 | @Override 919 | public void onAnimationRepeat(Animation animation) { 920 | } 921 | }); 922 | startAnimation(slideIn); 923 | } 924 | 925 | private void focusForAccessibility(View view) { 926 | final AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED); 927 | 928 | AccessibilityEventCompat.asRecord(event).setSource(view); 929 | try { 930 | view.sendAccessibilityEventUnchecked(event); 931 | } catch (IllegalStateException e) { 932 | // accessibility is off. 933 | } 934 | } 935 | 936 | private boolean shouldStartTimer() { 937 | return !isIndefiniteDuration(); 938 | } 939 | 940 | private boolean isIndefiniteDuration() { 941 | return getDuration() == SnackbarDuration.LENGTH_INDEFINITE.getDuration(); 942 | } 943 | 944 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 945 | private boolean isNavigationBarHidden(ViewGroup root) { 946 | 947 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { 948 | return false; 949 | } 950 | 951 | int viewFlags = root.getWindowSystemUiVisibility(); 952 | return (viewFlags & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 953 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; 954 | } 955 | 956 | private boolean isNavigationBarTranslucent(Activity targetActivity) { 957 | 958 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 959 | return false; 960 | } 961 | 962 | int flags = targetActivity.getWindow().getAttributes().flags; 963 | return (flags & WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) != 0; 964 | } 965 | 966 | private void startTimer() { 967 | postDelayed(mDismissRunnable, getDuration()); 968 | } 969 | 970 | private void startTimer(long duration) { 971 | postDelayed(mDismissRunnable, duration); 972 | } 973 | 974 | public void dismissByReplace() { 975 | mIsReplacePending = true; 976 | dismiss(); 977 | } 978 | 979 | public void dismiss() { 980 | dismiss(mDismissAnimated); 981 | } 982 | 983 | private void dismiss(boolean animate) { 984 | if (mIsDismissing) { 985 | return; 986 | } 987 | 988 | mIsDismissing = true; 989 | 990 | if (mEventListener != null && mIsShowing) { 991 | if (mIsReplacePending) { 992 | mEventListener.onDismissByReplace(Snackbar.this); 993 | } else { 994 | mEventListener.onDismiss(Snackbar.this); 995 | } 996 | } 997 | 998 | if (!animate) { 999 | finish(); 1000 | return; 1001 | } 1002 | 1003 | final Animation slideOut = AnimationUtils.loadAnimation(getContext(), getOutAnimationResource(mPhonePosition)); 1004 | slideOut.setAnimationListener(new Animation.AnimationListener() { 1005 | @Override 1006 | public void onAnimationStart(Animation animation) { 1007 | } 1008 | 1009 | @Override 1010 | public void onAnimationEnd(Animation animation) { 1011 | post(new Runnable() { 1012 | @Override 1013 | public void run() { 1014 | finish(); 1015 | } 1016 | }); 1017 | } 1018 | 1019 | @Override 1020 | public void onAnimationRepeat(Animation animation) { 1021 | } 1022 | }); 1023 | startAnimation(slideOut); 1024 | } 1025 | 1026 | private void finish() { 1027 | clearAnimation(); 1028 | ViewGroup parent = (ViewGroup) getParent(); 1029 | if (parent != null) { 1030 | parent.removeView(this); 1031 | } 1032 | if (mEventListener != null && mIsShowing) { 1033 | mEventListener.onDismissed(this); 1034 | } 1035 | mIsShowing = false; 1036 | mIsDismissing = false; 1037 | mIsReplacePending = false; 1038 | mTargetActivity = null; 1039 | } 1040 | 1041 | @Override 1042 | protected void onDetachedFromWindow() { 1043 | super.onDetachedFromWindow(); 1044 | 1045 | mIsShowing = false; 1046 | 1047 | if (mDismissRunnable != null) { 1048 | removeCallbacks(mDismissRunnable); 1049 | } 1050 | if (mRefreshLayoutParamsMarginsRunnable != null) { 1051 | removeCallbacks(mRefreshLayoutParamsMarginsRunnable); 1052 | } 1053 | } 1054 | 1055 | void dispatchOnWindowSystemUiVisibilityChangedCompat(int visible) { 1056 | onWindowSystemUiVisibilityChangedCompat(visible); 1057 | } 1058 | 1059 | protected void onWindowSystemUiVisibilityChangedCompat(int visible) { 1060 | if (mRefreshLayoutParamsMarginsRunnable != null) { 1061 | post(mRefreshLayoutParamsMarginsRunnable); 1062 | } 1063 | } 1064 | 1065 | protected void refreshLayoutParamsMargins() { 1066 | if (mIsDismissing) { 1067 | return; 1068 | } 1069 | 1070 | ViewGroup parent = (ViewGroup) getParent(); 1071 | if (parent == null) { 1072 | return; 1073 | } 1074 | 1075 | MarginLayoutParams params = (MarginLayoutParams) getLayoutParams(); 1076 | 1077 | updateLayoutParamsMargins(mTargetActivity, params); 1078 | 1079 | setLayoutParams(params); 1080 | } 1081 | 1082 | protected void updateLayoutParamsMargins(Activity targetActivity, MarginLayoutParams params) { 1083 | if (mUsePhoneLayout) { 1084 | // Phone 1085 | params.topMargin = mMarginTop; 1086 | params.rightMargin = mMarginRight; 1087 | params.leftMargin = mMarginLeft; 1088 | params.bottomMargin = mMarginBottom; 1089 | } else { 1090 | // Tablet/desktop 1091 | params.topMargin = mMarginTop; 1092 | params.rightMargin = mMarginRight; 1093 | params.leftMargin = mMarginLeft + mOffset; 1094 | params.bottomMargin = mMarginBottom + mOffset; 1095 | } 1096 | 1097 | // Add bottom/right margin when navigation bar is hidden or translucent 1098 | updateWindowInsets(targetActivity, mWindowInsets); 1099 | 1100 | params.rightMargin += mWindowInsets.right; 1101 | params.bottomMargin += mWindowInsets.bottom; 1102 | } 1103 | 1104 | 1105 | public int getActionColor() { 1106 | return mActionColor; 1107 | } 1108 | 1109 | public CharSequence getActionLabel() { 1110 | return mActionLabel; 1111 | } 1112 | 1113 | public int getTextColor() { 1114 | return mTextColor; 1115 | } 1116 | 1117 | public int getColor() { 1118 | return mColor; 1119 | } 1120 | 1121 | public int getLineColor() { 1122 | return mLineColor; 1123 | } 1124 | 1125 | public CharSequence getText() { 1126 | return mText; 1127 | } 1128 | 1129 | public long getDuration() { 1130 | return mCustomDuration == -1 ? mDuration.getDuration() : mCustomDuration; 1131 | } 1132 | 1133 | public SnackbarType getType() { 1134 | return mType; 1135 | } 1136 | 1137 | /** 1138 | * @return whether the action button has been clicked. In other words, this method will let 1139 | * you know if {@link com.nispok.snackbar.listeners.ActionClickListener#onActionClicked(Snackbar)} 1140 | * was called. This is useful, for instance, if you want to know during 1141 | * {@link com.nispok.snackbar.listeners.EventListener#onDismiss(Snackbar)} if the 1142 | * {@link com.nispok.snackbar.Snackbar} is being dismissed because of its action click 1143 | */ 1144 | public boolean isActionClicked() { 1145 | return mActionClicked; 1146 | } 1147 | 1148 | /** 1149 | * @return the pixel offset of this {@link com.nispok.snackbar.Snackbar} from the left and 1150 | * bottom of the {@link android.app.Activity}. 1151 | */ 1152 | public int getOffset() { 1153 | return mOffset; 1154 | } 1155 | 1156 | /** 1157 | * @return true only if both dismiss and show animations are enabled 1158 | */ 1159 | public boolean isAnimated() { 1160 | return mShowAnimated && mDismissAnimated; 1161 | } 1162 | 1163 | public boolean isDismissAnimated() { 1164 | return mDismissAnimated; 1165 | } 1166 | 1167 | public boolean isShowAnimated() { 1168 | return mShowAnimated; 1169 | } 1170 | 1171 | public boolean shouldDismissOnActionClicked() { 1172 | return mShouldDismissOnActionClicked; 1173 | } 1174 | 1175 | /** 1176 | * @return true if this {@link com.nispok.snackbar.Snackbar} is currently showing 1177 | */ 1178 | public boolean isShowing() { 1179 | return mIsShowing; 1180 | } 1181 | 1182 | /** 1183 | * @return true if this {@link com.nispok.snackbar.Snackbar} is dismissing. 1184 | */ 1185 | public boolean isDimissing() { 1186 | return mIsDismissing; 1187 | } 1188 | 1189 | /** 1190 | * @return false if this {@link com.nispok.snackbar.Snackbar} has been dismissed 1191 | */ 1192 | public boolean isDismissed() { 1193 | return !mIsShowing; 1194 | } 1195 | 1196 | /** 1197 | * @param snackbarPosition 1198 | * @return the animation resource used by this {@link com.nispok.snackbar.Snackbar} instance 1199 | * to enter the view 1200 | */ 1201 | @AnimRes 1202 | public static int getInAnimationResource(SnackbarPosition snackbarPosition) { 1203 | return snackbarPosition == SnackbarPosition.TOP ? R.anim.sb__top_in : R.anim.sb__bottom_in; 1204 | } 1205 | 1206 | /** 1207 | * @param snackbarPosition 1208 | * @return the animation resource used by this {@link com.nispok.snackbar.Snackbar} instance 1209 | * to exit the view 1210 | */ 1211 | @AnimRes 1212 | public static int getOutAnimationResource(SnackbarPosition snackbarPosition) { 1213 | return snackbarPosition == SnackbarPosition.TOP ? R.anim.sb__top_out : R.anim.sb__bottom_out; 1214 | } 1215 | 1216 | /** 1217 | * Set a Background Drawable using the appropriate Android version api call 1218 | * 1219 | * @param view 1220 | * @param drawable 1221 | */ 1222 | public static void setBackgroundDrawable(View view, Drawable drawable) { 1223 | if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) { 1224 | view.setBackgroundDrawable(drawable); 1225 | } else { 1226 | view.setBackground(drawable); 1227 | } 1228 | } 1229 | } 1230 | -------------------------------------------------------------------------------- /lib/src/main/java/com/nispok/snackbar/SnackbarHelperChildViewJB.java: -------------------------------------------------------------------------------- 1 | package com.nispok.snackbar; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.os.Build; 6 | import android.view.View; 7 | import android.view.ViewParent; 8 | 9 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 10 | class SnackbarHelperChildViewJB extends View { 11 | public SnackbarHelperChildViewJB(Context context) { 12 | super(context); 13 | setSaveEnabled(false); 14 | setWillNotDraw(true); 15 | setVisibility(GONE); 16 | } 17 | 18 | @Override 19 | public void onWindowSystemUiVisibilityChanged(int visible) { 20 | super.onWindowSystemUiVisibilityChanged(visible); 21 | 22 | final ViewParent parent = getParent(); 23 | if (parent instanceof Snackbar) { 24 | ((Snackbar) parent).dispatchOnWindowSystemUiVisibilityChangedCompat(visible); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/src/main/java/com/nispok/snackbar/SnackbarManager.java: -------------------------------------------------------------------------------- 1 | package com.nispok.snackbar; 2 | 3 | import java.lang.ref.WeakReference; 4 | 5 | import android.app.Activity; 6 | import android.os.Handler; 7 | import android.os.Looper; 8 | import android.support.annotation.NonNull; 9 | import android.util.Log; 10 | import android.view.ViewGroup; 11 | 12 | /** 13 | * A handler for multiple {@link Snackbar}s 14 | */ 15 | public class SnackbarManager { 16 | 17 | private static final String TAG = SnackbarManager.class.getSimpleName(); 18 | private static final Handler MAIN_THREAD = new Handler(Looper.getMainLooper()); 19 | 20 | private static WeakReference snackbarReference; 21 | 22 | private SnackbarManager() { 23 | } 24 | 25 | /** 26 | * Displays a {@link com.nispok.snackbar.Snackbar} in the current {@link Activity}, dismissing 27 | * the current Snackbar being displayed, if any. Note that the Activity will be obtained from 28 | * the Snackbar's {@link android.content.Context}. If the Snackbar was created with 29 | * {@link android.app.Activity#getApplicationContext()} then you must explicitly pass the target 30 | * Activity using {@link #show(Snackbar, android.app.Activity)} 31 | * 32 | * @param snackbar instance of {@link com.nispok.snackbar.Snackbar} to display 33 | */ 34 | public static void show(@NonNull Snackbar snackbar) { 35 | try { 36 | show(snackbar, (Activity) snackbar.getContext()); 37 | } catch (ClassCastException e) { 38 | Log.e(TAG, "Couldn't get Activity from the Snackbar's Context. Try calling " + 39 | "#show(Snackbar, Activity) instead", e); 40 | } 41 | } 42 | 43 | /** 44 | * Displays a {@link com.nispok.snackbar.Snackbar} in the current {@link Activity}, dismissing 45 | * the current Snackbar being displayed, if any 46 | * 47 | * @param snackbar instance of {@link com.nispok.snackbar.Snackbar} to display 48 | * @param activity target {@link Activity} to display the Snackbar 49 | */ 50 | public static void show(@NonNull final Snackbar snackbar, @NonNull final Activity activity) { 51 | MAIN_THREAD.post(new Runnable() { 52 | @Override 53 | public void run() { 54 | Snackbar currentSnackbar = getCurrentSnackbar(); 55 | if (currentSnackbar != null) { 56 | if (currentSnackbar.isShowing() && !currentSnackbar.isDimissing()) { 57 | currentSnackbar.dismissAnimation(false); 58 | currentSnackbar.dismissByReplace(); 59 | snackbarReference = new WeakReference<>(snackbar); 60 | snackbar.showAnimation(false); 61 | snackbar.showByReplace(activity); 62 | return; 63 | } 64 | currentSnackbar.dismiss(); 65 | } 66 | snackbarReference = new WeakReference<>(snackbar); 67 | snackbar.show(activity); 68 | } 69 | }); 70 | } 71 | 72 | /** 73 | * Displays a {@link com.nispok.snackbar.Snackbar} in the specified {@link ViewGroup}, dismissing 74 | * the current Snackbar being displayed, if any 75 | * 76 | * @param snackbar instance of {@link com.nispok.snackbar.Snackbar} to display 77 | * @param parent parent {@link ViewGroup} to display the Snackbar 78 | */ 79 | public static void show(@NonNull Snackbar snackbar, @NonNull ViewGroup parent) { 80 | show(snackbar, parent, Snackbar.shouldUsePhoneLayout(snackbar.getContext())); 81 | } 82 | 83 | /** 84 | * Displays a {@link com.nispok.snackbar.Snackbar} in the specified {@link ViewGroup}, dismissing 85 | * the current Snackbar being displayed, if any 86 | * 87 | * @param snackbar instance of {@link com.nispok.snackbar.Snackbar} to display 88 | * @param parent parent {@link ViewGroup} to display the Snackbar 89 | * @param usePhoneLayout true: use phone layout, false: use tablet layout 90 | */ 91 | public static void show(@NonNull final Snackbar snackbar, @NonNull final ViewGroup parent, 92 | final boolean usePhoneLayout) { 93 | MAIN_THREAD.post(new Runnable() { 94 | @Override 95 | public void run() { 96 | Snackbar currentSnackbar = getCurrentSnackbar(); 97 | if (currentSnackbar != null) { 98 | if (currentSnackbar.isShowing() && !currentSnackbar.isDimissing()) { 99 | currentSnackbar.dismissAnimation(false); 100 | currentSnackbar.dismissByReplace(); 101 | snackbarReference = new WeakReference<>(snackbar); 102 | snackbar.showAnimation(false); 103 | snackbar.showByReplace(parent, usePhoneLayout); 104 | return; 105 | } 106 | currentSnackbar.dismiss(); 107 | } 108 | snackbarReference = new WeakReference<>(snackbar); 109 | snackbar.show(parent, usePhoneLayout); 110 | } 111 | }); 112 | } 113 | 114 | /** 115 | * Dismisses the {@link com.nispok.snackbar.Snackbar} shown by this manager. 116 | */ 117 | public static void dismiss() { 118 | final Snackbar currentSnackbar = getCurrentSnackbar(); 119 | if (currentSnackbar != null) { 120 | MAIN_THREAD.post(new Runnable() { 121 | @Override 122 | public void run() { 123 | currentSnackbar.dismiss(); 124 | } 125 | }); 126 | } 127 | } 128 | 129 | /** 130 | * Return the current Snackbar 131 | */ 132 | public static Snackbar getCurrentSnackbar() { 133 | if (snackbarReference != null) { 134 | return snackbarReference.get(); 135 | } 136 | return null; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /lib/src/main/java/com/nispok/snackbar/enums/SnackbarType.java: -------------------------------------------------------------------------------- 1 | package com.nispok.snackbar.enums; 2 | 3 | public enum SnackbarType { 4 | 5 | /** 6 | * Snackbar with a single line 7 | */ 8 | SINGLE_LINE(48, 48, 1), 9 | /** 10 | * Snackbar with two lines 11 | */ 12 | MULTI_LINE(48, 80, 2); 13 | 14 | private int minHeight; 15 | private int maxHeight; 16 | private int maxLines; 17 | 18 | SnackbarType(int minHeight, int maxHeight, int maxLines) { 19 | this.minHeight = minHeight; 20 | this.maxHeight = maxHeight; 21 | this.maxLines = maxLines; 22 | } 23 | 24 | public int getMinHeight() { 25 | return minHeight; 26 | } 27 | 28 | public int getMaxHeight() { 29 | return maxHeight; 30 | } 31 | 32 | public int getMaxLines() { 33 | return maxLines; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/src/main/java/com/nispok/snackbar/layouts/SnackbarLayout.java: -------------------------------------------------------------------------------- 1 | package com.nispok.snackbar.layouts; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.LinearLayout; 6 | 7 | public class SnackbarLayout extends LinearLayout { 8 | private int mMaxWidth = Integer.MAX_VALUE; 9 | private int mMaxHeight = Integer.MAX_VALUE; 10 | 11 | public SnackbarLayout(Context context) { 12 | super(context); 13 | } 14 | 15 | public SnackbarLayout(Context context, AttributeSet attrs) { 16 | this(context, attrs, 0); 17 | } 18 | 19 | public SnackbarLayout(Context context, AttributeSet attrs, int defStyle) { 20 | super(context, attrs, defStyle); 21 | } 22 | 23 | @Override 24 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 25 | // Adjust width as necessary 26 | int width = MeasureSpec.getSize(widthMeasureSpec); 27 | if (mMaxWidth < width) { 28 | int mode = MeasureSpec.getMode(widthMeasureSpec); 29 | widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxWidth, mode); 30 | } 31 | // Adjust height as necessary 32 | int height = MeasureSpec.getSize(heightMeasureSpec); 33 | if (mMaxHeight < height) { 34 | int mode = MeasureSpec.getMode(heightMeasureSpec); 35 | heightMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxHeight, mode); 36 | } 37 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 38 | } 39 | 40 | public void setMaxWidth(int maxWidth) { 41 | mMaxWidth = maxWidth; 42 | requestLayout(); 43 | } 44 | 45 | public void setMaxHeight(int maxHeight) { 46 | mMaxHeight = maxHeight; 47 | requestLayout(); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /lib/src/main/java/com/nispok/snackbar/listeners/ActionClickListener.java: -------------------------------------------------------------------------------- 1 | package com.nispok.snackbar.listeners; 2 | 3 | import com.nispok.snackbar.Snackbar; 4 | 5 | public interface ActionClickListener { 6 | void onActionClicked(Snackbar snackbar); 7 | } 8 | -------------------------------------------------------------------------------- /lib/src/main/java/com/nispok/snackbar/listeners/ActionSwipeListener.java: -------------------------------------------------------------------------------- 1 | package com.nispok.snackbar.listeners; 2 | 3 | public interface ActionSwipeListener { 4 | 5 | void onSwipeToDismiss(); 6 | } 7 | -------------------------------------------------------------------------------- /lib/src/main/java/com/nispok/snackbar/listeners/EventListener.java: -------------------------------------------------------------------------------- 1 | package com.nispok.snackbar.listeners; 2 | 3 | import com.nispok.snackbar.Snackbar; 4 | 5 | /** 6 | * Interface used to notify of all {@link com.nispok.snackbar.Snackbar} display events. Useful if you want 7 | * to move other views while the Snackbar is on screen. 8 | */ 9 | public interface EventListener { 10 | /** 11 | * Called when a {@link com.nispok.snackbar.Snackbar} is about to enter the screen 12 | * 13 | * @param snackbar the {@link com.nispok.snackbar.Snackbar} that's being shown 14 | */ 15 | public void onShow(Snackbar snackbar); 16 | 17 | /** 18 | * Called when a {@link com.nispok.snackbar.Snackbar} is about to enter the screen while 19 | * a {@link com.nispok.snackbar.Snackbar} is about to exit the screen by replacement. 20 | * 21 | * @param snackbar the {@link com.nispok.snackbar.Snackbar} that's being shown 22 | */ 23 | public void onShowByReplace(Snackbar snackbar); 24 | 25 | /** 26 | * Called when a {@link com.nispok.snackbar.Snackbar} is fully shown 27 | * 28 | * @param snackbar the {@link com.nispok.snackbar.Snackbar} that's being shown 29 | */ 30 | public void onShown(Snackbar snackbar); 31 | 32 | /** 33 | * Called when a {@link com.nispok.snackbar.Snackbar} is about to exit the screen 34 | * 35 | * @param snackbar the {@link com.nispok.snackbar.Snackbar} that's being dismissed 36 | */ 37 | public void onDismiss(Snackbar snackbar); 38 | 39 | /** 40 | * Called when a {@link com.nispok.snackbar.Snackbar} is about to exit the screen 41 | * when a new {@link com.nispok.snackbar.Snackbar} is about to enter the screen. 42 | * 43 | * @param snackbar the {@link com.nispok.snackbar.Snackbar} that's being dismissed 44 | */ 45 | public void onDismissByReplace(Snackbar snackbar); 46 | 47 | /** 48 | * Called when a {@link com.nispok.snackbar.Snackbar} had just been dismissed 49 | * 50 | * @param snackbar the {@link com.nispok.snackbar.Snackbar} that's being dismissed 51 | */ 52 | public void onDismissed(Snackbar snackbar); 53 | } 54 | -------------------------------------------------------------------------------- /lib/src/main/java/com/nispok/snackbar/listeners/EventListenerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.nispok.snackbar.listeners; 2 | 3 | import com.nispok.snackbar.Snackbar; 4 | 5 | /** 6 | * This adapter class provides empty implementations of the methods from {@link com.nispok.snackbar.listeners.EventListener}. 7 | * If you are only interested in a subset of the interface methods you can extend this class an override only the methods you need. 8 | */ 9 | public abstract class EventListenerAdapter implements EventListener { 10 | 11 | /** 12 | * {@inheritDoc} 13 | */ 14 | @Override 15 | public void onShow(Snackbar snackbar) { 16 | 17 | } 18 | 19 | /** 20 | * {@inheritDoc} 21 | */ 22 | @Override 23 | public void onShowByReplace(Snackbar snackbar) { 24 | 25 | } 26 | 27 | /** 28 | * {@inheritDoc} 29 | */ 30 | @Override 31 | public void onShown(Snackbar snackbar) { 32 | 33 | } 34 | 35 | /** 36 | * {@inheritDoc} 37 | */ 38 | @Override 39 | public void onDismiss(Snackbar snackbar) { 40 | 41 | } 42 | 43 | /** 44 | * {@inheritDoc} 45 | */ 46 | @Override 47 | public void onDismissByReplace(Snackbar snackbar) { 48 | 49 | } 50 | 51 | /** 52 | * {@inheritDoc} 53 | */ 54 | @Override 55 | public void onDismissed(Snackbar snackbar) { 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/src/main/java/com/nispok/snackbar/listeners/SwipeDismissTouchListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.nispok.snackbar.listeners; 18 | 19 | import android.animation.Animator; 20 | import android.animation.AnimatorListenerAdapter; 21 | import android.view.MotionEvent; 22 | import android.view.VelocityTracker; 23 | import android.view.View; 24 | import android.view.ViewConfiguration; 25 | 26 | /** 27 | * A {@link android.view.View.OnTouchListener} that makes any {@link android.view.View} dismissible 28 | * when the user swipes (drags her finger) horizontally across the view. 29 | * 30 | * 31 | * @author Roman Nurik 32 | * 33 | */ 34 | public class SwipeDismissTouchListener implements View.OnTouchListener { 35 | // Cached ViewConfiguration and system-wide constant values 36 | private int mSlop; 37 | private int mMinFlingVelocity; 38 | private int mMaxFlingVelocity; 39 | private long mAnimationTime; 40 | 41 | // Fixed properties 42 | private View mContainerView; 43 | private DismissCallbacks mCallbacks; 44 | private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero 45 | 46 | // Transient properties 47 | private float mDownX; 48 | private float mDownY; 49 | private boolean mSwiping; 50 | private int mSwipingSlop; 51 | private Object mToken; 52 | private VelocityTracker mVelocityTracker; 53 | private float mTranslationX; 54 | 55 | /** 56 | * The callback interface used by {@link SwipeDismissTouchListener} to inform its client 57 | * about a successful dismissal of the view for which it was created. 58 | */ 59 | public interface DismissCallbacks { 60 | /** 61 | * Called to determine whether the view can be dismissed. 62 | */ 63 | boolean canDismiss(Object token); 64 | 65 | /** 66 | * Called when the user has indicated they she would like to dismiss the view. 67 | * 68 | * @param view The originating {@link android.view.View} to be dismissed. 69 | * @param token The optional token passed to this object's constructor. 70 | */ 71 | void onDismiss(View view, Object token); 72 | 73 | /** 74 | * Called on touch down/up to indicate the {@link com.nispok.snackbar.Snackbar} dismissal 75 | * timer should or should not pause when the user is swiping the snack bar away. 76 | * 77 | * @param shouldPause Whether or not the dismissal timer should pause or not 78 | */ 79 | void pauseTimer(boolean shouldPause); 80 | } 81 | 82 | /** 83 | * Constructs a new swipe-to-dismiss touch listener for the given view. 84 | * 85 | * @param view The view to make dismissable. 86 | * @param token An optional token/cookie object to be passed through to the callback. 87 | * @param callbacks The callback to trigger when the user has indicated that she would like to 88 | * dismiss this view. 89 | */ 90 | public SwipeDismissTouchListener(View view, Object token, DismissCallbacks callbacks) { 91 | ViewConfiguration vc = ViewConfiguration.get(view.getContext()); 92 | mSlop = vc.getScaledTouchSlop(); 93 | mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16; 94 | mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); 95 | mAnimationTime = view.getContext().getResources().getInteger( 96 | android.R.integer.config_shortAnimTime); 97 | mContainerView = view; 98 | mToken = token; 99 | mCallbacks = callbacks; 100 | } 101 | 102 | @Override 103 | public boolean onTouch(final View view, MotionEvent motionEvent) { 104 | // offset because the view is translated during swipe 105 | motionEvent.offsetLocation(mTranslationX, 0); 106 | 107 | if (mViewWidth < 2) { 108 | mViewWidth = view.getWidth(); 109 | } 110 | 111 | switch (motionEvent.getActionMasked()) { 112 | case MotionEvent.ACTION_DOWN: { 113 | // TODO: ensure this is a finger, and set a flag 114 | mDownX = motionEvent.getRawX(); 115 | mDownY = motionEvent.getRawY(); 116 | if (mCallbacks.canDismiss(mToken)) { 117 | mCallbacks.pauseTimer(true); 118 | mVelocityTracker = VelocityTracker.obtain(); 119 | mVelocityTracker.addMovement(motionEvent); 120 | } 121 | return false; 122 | } 123 | 124 | case MotionEvent.ACTION_UP: { 125 | if (mVelocityTracker == null) { 126 | break; 127 | } 128 | 129 | mCallbacks.pauseTimer(false); 130 | float deltaX = motionEvent.getRawX() - mDownX; 131 | mVelocityTracker.addMovement(motionEvent); 132 | mVelocityTracker.computeCurrentVelocity(1000); 133 | float velocityX = mVelocityTracker.getXVelocity(); 134 | float absVelocityX = Math.abs(velocityX); 135 | float absVelocityY = Math.abs(mVelocityTracker.getYVelocity()); 136 | boolean dismiss = false; 137 | boolean dismissRight = false; 138 | if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) { 139 | dismiss = true; 140 | dismissRight = deltaX > 0; 141 | } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity 142 | && absVelocityY < absVelocityX 143 | && absVelocityY < absVelocityX && mSwiping) { 144 | // dismiss only if flinging in the same direction as dragging 145 | dismiss = (velocityX < 0) == (deltaX < 0); 146 | dismissRight = mVelocityTracker.getXVelocity() > 0; 147 | } 148 | if (dismiss) { 149 | // dismiss 150 | view.animate() 151 | .translationX(dismissRight ? mViewWidth : -mViewWidth) 152 | .alpha(0) 153 | .setDuration(mAnimationTime) 154 | .setListener(null); 155 | mContainerView.animate() 156 | .alpha(0) 157 | .setDuration(mAnimationTime) 158 | .setListener(new AnimatorListenerAdapter() { 159 | @Override 160 | public void onAnimationEnd(Animator animation) { 161 | performDismiss(view); 162 | } 163 | }); 164 | } else if (mSwiping) { 165 | // cancel 166 | view.animate() 167 | .translationX(0) 168 | .alpha(1) 169 | .setDuration(mAnimationTime) 170 | .setListener(null); 171 | mContainerView.animate() 172 | .alpha(1) 173 | .setDuration(mAnimationTime); 174 | } 175 | if (mVelocityTracker != null) { 176 | mVelocityTracker.recycle(); 177 | mVelocityTracker = null; 178 | } 179 | mTranslationX = 0; 180 | mDownX = 0; 181 | mDownY = 0; 182 | mSwiping = false; 183 | break; 184 | } 185 | 186 | case MotionEvent.ACTION_CANCEL: { 187 | if (mVelocityTracker == null) { 188 | break; 189 | } 190 | 191 | view.animate() 192 | .translationX(0) 193 | .alpha(1) 194 | .setDuration(mAnimationTime) 195 | .setListener(null); 196 | mContainerView.animate() 197 | .alpha(1) 198 | .setDuration(mAnimationTime); 199 | mVelocityTracker.recycle(); 200 | mVelocityTracker = null; 201 | mTranslationX = 0; 202 | mDownX = 0; 203 | mDownY = 0; 204 | mSwiping = false; 205 | break; 206 | } 207 | 208 | case MotionEvent.ACTION_MOVE: { 209 | if (mVelocityTracker == null) { 210 | break; 211 | } 212 | 213 | mVelocityTracker.addMovement(motionEvent); 214 | float deltaX = motionEvent.getRawX() - mDownX; 215 | float deltaY = motionEvent.getRawY() - mDownY; 216 | if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) { 217 | mSwiping = true; 218 | mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop); 219 | 220 | if(view.getParent() != null) { 221 | view.getParent().requestDisallowInterceptTouchEvent(true); 222 | } 223 | 224 | // Cancel listview's touch 225 | MotionEvent cancelEvent = MotionEvent.obtain(motionEvent); 226 | cancelEvent.setAction(MotionEvent.ACTION_CANCEL | 227 | (motionEvent.getActionIndex() << 228 | MotionEvent.ACTION_POINTER_INDEX_SHIFT)); 229 | view.onTouchEvent(cancelEvent); 230 | cancelEvent.recycle(); 231 | } 232 | 233 | if (mSwiping) { 234 | mTranslationX = deltaX; 235 | view.setTranslationX(deltaX - mSwipingSlop); 236 | // TODO: use an ease-out interpolator or such 237 | view.setAlpha(Math.max(0f, Math.min(1f, 238 | 1f - 2f * Math.abs(deltaX) / mViewWidth))); 239 | mContainerView.setAlpha(Math.max(0.2f, Math.min(1f, 240 | 1f - Math.abs(deltaX) / mViewWidth))); 241 | return true; 242 | } 243 | break; 244 | } 245 | } 246 | return false; 247 | } 248 | 249 | private void performDismiss(View view) { 250 | mCallbacks.onDismiss(view, mToken); 251 | } 252 | } -------------------------------------------------------------------------------- /lib/src/main/res/anim/sb__bottom_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /lib/src/main/res/anim/sb__bottom_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /lib/src/main/res/anim/sb__top_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /lib/src/main/res/anim/sb__top_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /lib/src/main/res/drawable-v21/sb__btn_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/src/main/res/drawable/sb__bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /lib/src/main/res/drawable/sb__btn_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/src/main/res/drawable/sb__divider_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /lib/src/main/res/interpolator/sb__accelerate_cubic.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 22 | -------------------------------------------------------------------------------- /lib/src/main/res/interpolator/sb__decelerate_cubic.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 22 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/sb__template.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 23 | 24 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /lib/src/main/res/values-sw600dp/bools.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | 5 | -------------------------------------------------------------------------------- /lib/src/main/res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2dp 4 | 288dp 5 | 568dp 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/src/main/res/values-v11/bools.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | -------------------------------------------------------------------------------- /lib/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /lib/src/main/res/values-xlarge/bools.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | 5 | -------------------------------------------------------------------------------- /lib/src/main/res/values-xlarge/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2dp 4 | 288dp 5 | 568dp 6 | 24dp 7 | 8 | -------------------------------------------------------------------------------- /lib/src/main/res/values/bools.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | false 5 | 6 | -------------------------------------------------------------------------------- /lib/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #ff323232 5 | #ffffffff 6 | #ffffffff 7 | #1affffff 8 | 9 | -------------------------------------------------------------------------------- /lib/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 0dp 4 | 0dp 5 | 14dp 6 | 24dp 7 | 14dp 8 | 24dp 9 | 14sp 10 | 288dp 11 | 568dp 12 | 13 | -------------------------------------------------------------------------------- /lib/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 20 | -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | def loadProperty(name) { 4 | hasProperty(name) ? "${getProperty(name)}" : "" 5 | } 6 | 7 | android { 8 | compileSdkVersion 22 9 | buildToolsVersion "22.0.1" 10 | 11 | defaultConfig { 12 | applicationId "com.nispok.samples.snackbar" 13 | minSdkVersion 8 14 | targetSdkVersion 22 15 | versionCode 18 16 | versionName "1.3.9" 17 | } 18 | 19 | signingConfigs { 20 | release { 21 | storeFile file(loadProperty("NISPOK_RELEASE_KEYSTORE_PATH") ?: signingConfigs.debug.storeFile) 22 | storePassword loadProperty("NISPOK_RELEASE_PASSWORD") ?: signingConfigs.debug.storePassword 23 | keyAlias loadProperty("NISPOK_RELEASE_KEY_ALIAS") ?: signingConfigs.debug.keyAlias 24 | keyPassword loadProperty("NISPOK_RELEASE_KEY_PASSWORD") ?: signingConfigs.debug.keyPassword 25 | } 26 | } 27 | 28 | buildTypes { 29 | release { 30 | signingConfig signingConfigs.release 31 | debuggable false 32 | } 33 | } 34 | 35 | } 36 | 37 | dependencies { 38 | compile project(':lib') 39 | compile 'com.android.support:appcompat-v7:22.0.0' 40 | compile 'com.android.support:recyclerview-v7:22.0.0' 41 | 42 | testCompile 'junit:junit:4.12' 43 | testCompile 'org.apache.maven:maven-ant-tasks:2.1.3' 44 | testCompile 'org.robolectric:robolectric:2.4' 45 | } 46 | -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Applications/Android Studio.app/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /sample/src/main/assets/fonts/Roboto-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nispok/snackbar/a4e0449874423031107f6aaa7e97d0f1714a1d3b/sample/src/main/assets/fonts/Roboto-LightItalic.ttf -------------------------------------------------------------------------------- /sample/src/main/java/com/nispok/samples/snackbar/SnackbarImmersiveModeSampleActivity.java: -------------------------------------------------------------------------------- 1 | package com.nispok.samples.snackbar; 2 | 3 | import android.annotation.TargetApi; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import android.support.v7.app.ActionBarActivity; 7 | import android.view.MenuItem; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.view.Window; 11 | import android.widget.Button; 12 | 13 | import com.nispok.snackbar.Snackbar; 14 | import com.nispok.snackbar.SnackbarManager; 15 | import com.nispok.snackbar.enums.SnackbarType; 16 | 17 | public class SnackbarImmersiveModeSampleActivity extends ActionBarActivity { 18 | 19 | private static final String TAG = SnackbarImmersiveModeSampleActivity.class.getSimpleName(); 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_immersive_mode_sample); 25 | 26 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 27 | hideSystemUI(); 28 | 29 | Button singleLineButton = (Button) findViewById(R.id.single_line); 30 | singleLineButton.setOnClickListener(new View.OnClickListener() { 31 | @Override 32 | public void onClick(View v) { 33 | SnackbarManager.show( 34 | Snackbar.with(SnackbarImmersiveModeSampleActivity.this) 35 | .text("Single-line snackbar")); 36 | } 37 | }); 38 | 39 | Button multiLineButton = (Button) findViewById(R.id.multi_line); 40 | multiLineButton.setOnClickListener(new View.OnClickListener() { 41 | @Override 42 | public void onClick(View v) { 43 | SnackbarManager.show( 44 | Snackbar.with(SnackbarImmersiveModeSampleActivity.this) 45 | .type(SnackbarType.MULTI_LINE) 46 | .text("This is a multi-line snackbar. Keep in mind that snackbars" + 47 | " are meant for VERY short messages")); 48 | } 49 | }); 50 | 51 | ViewGroup container = (ViewGroup) findViewById(android.R.id.content); 52 | container.setClickable(true); 53 | container.setOnClickListener(new View.OnClickListener() { 54 | @Override 55 | public void onClick(View v) { 56 | onClickContainerView(); 57 | } 58 | }); 59 | } 60 | 61 | @Override 62 | protected void onDestroy() { 63 | super.onDestroy(); 64 | } 65 | 66 | @Override 67 | public boolean onOptionsItemSelected(MenuItem item) { 68 | switch (item.getItemId()) { 69 | case android.R.id.home: 70 | finish(); 71 | return true; 72 | default: 73 | return super.onOptionsItemSelected(item); 74 | } 75 | } 76 | 77 | public static boolean isImmersiveModeCapable() { 78 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; 79 | } 80 | 81 | @TargetApi(Build.VERSION_CODES.KITKAT) 82 | private void hideSystemUI() { 83 | getWindow().getDecorView().setSystemUiVisibility( 84 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE 85 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 86 | | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 87 | | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 88 | | View.SYSTEM_UI_FLAG_FULLSCREEN 89 | | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); 90 | } 91 | 92 | @TargetApi(Build.VERSION_CODES.KITKAT) 93 | private void showSystemUI() { 94 | getWindow().getDecorView().setSystemUiVisibility( 95 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE 96 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 97 | | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); 98 | } 99 | 100 | @TargetApi(Build.VERSION_CODES.KITKAT) 101 | private boolean isSystemUiHidden() { 102 | return ((getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0); 103 | } 104 | 105 | @Override 106 | public void onWindowFocusChanged(boolean hasFocus) { 107 | super.onWindowFocusChanged(hasFocus); 108 | 109 | if (hasFocus) { 110 | hideSystemUI(); 111 | } 112 | } 113 | 114 | private void onClickContainerView() { 115 | if (isSystemUiHidden()) { 116 | showSystemUI(); 117 | } else { 118 | hideSystemUI(); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /sample/src/main/java/com/nispok/samples/snackbar/SnackbarListViewSampleActivity.java: -------------------------------------------------------------------------------- 1 | package com.nispok.samples.snackbar; 2 | 3 | import com.nispok.snackbar.Snackbar; 4 | import com.nispok.snackbar.SnackbarManager; 5 | 6 | import android.graphics.Color; 7 | import android.os.Bundle; 8 | import android.support.v7.app.ActionBarActivity; 9 | import android.view.MenuItem; 10 | import android.view.View; 11 | import android.widget.AdapterView; 12 | import android.widget.ArrayAdapter; 13 | import android.widget.ListView; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | public class SnackbarListViewSampleActivity extends ActionBarActivity { 19 | 20 | private static final String TAG = SnackbarListViewSampleActivity.class.getSimpleName(); 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_list_sample); 26 | 27 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 28 | 29 | final ListView listView = (ListView) findViewById(android.R.id.list); 30 | 31 | List data = new ArrayList(); 32 | 33 | for(int i = 0; i < 25; i++) { 34 | data.add(String.format("Item %d", (i + 1))); 35 | } 36 | 37 | ArrayAdapter adapter = new ArrayAdapter(this, 38 | android.R.layout.simple_list_item_1, data); 39 | 40 | listView.setAdapter(adapter); 41 | listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 42 | @Override 43 | public void onItemClick(AdapterView parent, View view, int position, long id) { 44 | SnackbarManager.show( 45 | Snackbar.with(SnackbarListViewSampleActivity.this) 46 | .text(String.format("Item %d pressed", (position + 1))) 47 | .actionLabel("Close") 48 | .actionColor(Color.parseColor("#FF8A80")) 49 | .duration(Snackbar.SnackbarDuration.LENGTH_LONG) 50 | .attachToAbsListView(listView)); 51 | } 52 | }); 53 | } 54 | 55 | @Override 56 | public boolean onOptionsItemSelected(MenuItem item) { 57 | switch(item.getItemId()) { 58 | case android.R.id.home: 59 | finish(); 60 | return true; 61 | default: 62 | return super.onOptionsItemSelected(item); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /sample/src/main/java/com/nispok/samples/snackbar/SnackbarNavigationBarTranslucentSampleActivity.java: -------------------------------------------------------------------------------- 1 | package com.nispok.samples.snackbar; 2 | 3 | import android.annotation.TargetApi; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import android.support.v7.app.ActionBarActivity; 7 | import android.view.MenuItem; 8 | import android.view.View; 9 | import android.view.Window; 10 | import android.view.WindowManager; 11 | import android.widget.Button; 12 | 13 | import com.nispok.snackbar.Snackbar; 14 | import com.nispok.snackbar.SnackbarManager; 15 | import com.nispok.snackbar.enums.SnackbarType; 16 | 17 | public class SnackbarNavigationBarTranslucentSampleActivity extends ActionBarActivity { 18 | 19 | private static final String TAG = SnackbarNavigationBarTranslucentSampleActivity.class.getSimpleName(); 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_navigation_bar_translucent_sample); 25 | 26 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 27 | enableTransparentSystemBars(getWindow()); 28 | 29 | Button singleLineButton = (Button) findViewById(R.id.single_line); 30 | singleLineButton.setOnClickListener(new View.OnClickListener() { 31 | @Override 32 | public void onClick(View v) { 33 | SnackbarManager.show( 34 | Snackbar.with(SnackbarNavigationBarTranslucentSampleActivity.this) 35 | .text("Single-line snackbar")); 36 | } 37 | }); 38 | 39 | Button multiLineButton = (Button) findViewById(R.id.multi_line); 40 | multiLineButton.setOnClickListener(new View.OnClickListener() { 41 | @Override 42 | public void onClick(View v) { 43 | SnackbarManager.show( 44 | Snackbar.with(SnackbarNavigationBarTranslucentSampleActivity.this) 45 | .type(SnackbarType.MULTI_LINE) 46 | .text("This is a multi-line snackbar. Keep in mind that snackbars" + 47 | " are meant for VERY short messages")); 48 | } 49 | }); 50 | } 51 | 52 | @Override 53 | protected void onDestroy() { 54 | super.onDestroy(); 55 | disableTransparentSystemBars(getWindow()); 56 | } 57 | 58 | @Override 59 | public boolean onOptionsItemSelected(MenuItem item) { 60 | switch(item.getItemId()) { 61 | case android.R.id.home: 62 | finish(); 63 | return true; 64 | default: 65 | return super.onOptionsItemSelected(item); 66 | } 67 | } 68 | 69 | public static boolean isTranslucentSystemBarsCapable() { 70 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; 71 | } 72 | 73 | @TargetApi(Build.VERSION_CODES.KITKAT) 74 | private void enableTransparentSystemBars(Window window) { 75 | window.setFlags( 76 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, 77 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); 78 | } 79 | 80 | @TargetApi(Build.VERSION_CODES.KITKAT) 81 | private void disableTransparentSystemBars(Window window) { 82 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /sample/src/main/java/com/nispok/samples/snackbar/SnackbarRecyclerViewSampleActivity.java: -------------------------------------------------------------------------------- 1 | package com.nispok.samples.snackbar; 2 | 3 | import com.nispok.snackbar.Snackbar; 4 | import com.nispok.snackbar.SnackbarManager; 5 | 6 | import android.content.Context; 7 | import android.graphics.Color; 8 | import android.os.Bundle; 9 | import android.support.v7.app.ActionBarActivity; 10 | import android.support.v7.widget.LinearLayoutManager; 11 | import android.support.v7.widget.RecyclerView; 12 | import android.view.LayoutInflater; 13 | import android.view.Menu; 14 | import android.view.MenuInflater; 15 | import android.view.MenuItem; 16 | import android.view.View; 17 | import android.view.ViewGroup; 18 | import android.widget.TextView; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | public class SnackbarRecyclerViewSampleActivity extends ActionBarActivity { 24 | 25 | private static final String TAG = SnackbarRecyclerViewSampleActivity.class.getSimpleName(); 26 | 27 | private RecyclerView mRecyclerView; 28 | 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | setContentView(R.layout.activity_recyclerview_sample); 33 | 34 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 35 | 36 | mRecyclerView = (RecyclerView) findViewById(android.R.id.list); 37 | mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 38 | 39 | List data = new ArrayList(); 40 | 41 | for(int i = 0; i < 25; i++) { 42 | data.add(String.format("Item %d", (i + 1))); 43 | } 44 | 45 | SimpleDataAdapter adapter = new SimpleDataAdapter(android.R.layout.simple_list_item_1, data, 46 | this); 47 | mRecyclerView.setAdapter(adapter); 48 | } 49 | 50 | @Override 51 | public boolean onCreateOptionsMenu(Menu menu) { 52 | MenuInflater inflater = getMenuInflater(); 53 | inflater.inflate(R.menu.recyclerview_sample, menu); 54 | return true; 55 | } 56 | 57 | @Override 58 | public boolean onOptionsItemSelected(MenuItem item) { 59 | switch(item.getItemId()) { 60 | case android.R.id.home: 61 | finish(); 62 | return true; 63 | case R.id.action_add_snackbar: 64 | SnackbarManager.show( 65 | Snackbar.with(SnackbarRecyclerViewSampleActivity.this) 66 | .text("Woo, snackbar!") 67 | .actionLabel("Close") 68 | .actionColor(Color.parseColor("#FF8A80")) 69 | .duration(Snackbar.SnackbarDuration.LENGTH_LONG) 70 | .attachToRecyclerView(mRecyclerView)); 71 | return true; 72 | default: 73 | return super.onOptionsItemSelected(item); 74 | } 75 | } 76 | 77 | private static class SimpleDataAdapter extends RecyclerView.Adapter { 78 | 79 | private final int mLayoutId; 80 | 81 | private List mData; 82 | 83 | private Context mContext; 84 | 85 | private SimpleDataAdapter(int layoutId, List data, Context context) { 86 | mLayoutId = layoutId; 87 | mContext = context; 88 | mData = data; 89 | } 90 | 91 | @Override 92 | public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int itemType) { 93 | final View rowView = LayoutInflater.from(mContext).inflate(mLayoutId, parent, false); 94 | return new SimpleViewHolder(rowView); 95 | } 96 | 97 | @Override 98 | public void onBindViewHolder(SimpleViewHolder viewHolder, int position) { 99 | String data = mData.get(position); 100 | 101 | viewHolder.getText1().setText(data); 102 | } 103 | 104 | @Override 105 | public int getItemCount() { 106 | return mData.size(); 107 | } 108 | } 109 | 110 | private static class SimpleViewHolder extends RecyclerView.ViewHolder { 111 | 112 | private final TextView text1; 113 | 114 | public SimpleViewHolder(View itemView) { 115 | super(itemView); 116 | 117 | this.text1 = (TextView) itemView.findViewById(android.R.id.text1); 118 | } 119 | 120 | public TextView getText1() { 121 | return text1; 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /sample/src/main/java/com/nispok/samples/snackbar/SnackbarSampleActivity.java: -------------------------------------------------------------------------------- 1 | package com.nispok.samples.snackbar; 2 | 3 | import android.content.Intent; 4 | import android.graphics.Color; 5 | import android.graphics.Typeface; 6 | import android.net.Uri; 7 | import android.os.Bundle; 8 | import android.support.v7.app.ActionBarActivity; 9 | import android.util.Log; 10 | import android.view.Menu; 11 | import android.view.MenuInflater; 12 | import android.view.MenuItem; 13 | import android.view.View; 14 | import android.widget.Button; 15 | import android.widget.Toast; 16 | import com.nispok.snackbar.Snackbar; 17 | import com.nispok.snackbar.SnackbarManager; 18 | import com.nispok.snackbar.enums.SnackbarType; 19 | import com.nispok.snackbar.listeners.ActionClickListener; 20 | import com.nispok.snackbar.listeners.ActionSwipeListener; 21 | import com.nispok.snackbar.listeners.EventListener; 22 | 23 | public class SnackbarSampleActivity extends ActionBarActivity { 24 | 25 | private static final String TAG = SnackbarSampleActivity.class.getSimpleName(); 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_sample); 31 | 32 | Button singleLineButton = (Button) findViewById(R.id.single_line); 33 | singleLineButton.setOnClickListener(new View.OnClickListener() { 34 | @Override 35 | public void onClick(View v) { 36 | SnackbarManager.show( 37 | Snackbar.with(SnackbarSampleActivity.this) 38 | .text("Single-line snackbar")); 39 | } 40 | }); 41 | 42 | Button singleLineWithActionButton = (Button) findViewById(R.id.single_line_with_action); 43 | singleLineWithActionButton.setOnClickListener(new View.OnClickListener() { 44 | @Override 45 | public void onClick(View v) { 46 | SnackbarManager.show( 47 | Snackbar.with(SnackbarSampleActivity.this) 48 | .text("Something has been done") 49 | .actionLabel("Undo") 50 | .swipeListener(new ActionSwipeListener() { 51 | @Override 52 | public void onSwipeToDismiss() { 53 | Toast.makeText(SnackbarSampleActivity.this, 54 | "swipe to dismiss", 55 | Toast.LENGTH_SHORT).show(); 56 | } 57 | }) 58 | .actionListener(new ActionClickListener() { 59 | @Override 60 | public void onActionClicked(Snackbar snackbar) { 61 | Toast.makeText(SnackbarSampleActivity.this, 62 | "Action undone", 63 | Toast.LENGTH_SHORT).show(); 64 | } 65 | })); 66 | } 67 | }); 68 | 69 | Button multiLineButton = (Button) findViewById(R.id.multi_line); 70 | multiLineButton.setOnClickListener(new View.OnClickListener() { 71 | @Override 72 | public void onClick(View v) { 73 | SnackbarManager.show( 74 | Snackbar.with(SnackbarSampleActivity.this) 75 | .type(SnackbarType.MULTI_LINE) 76 | .text("This is a multi-line snackbar. Keep in mind that snackbars" + 77 | " are meant for VERY short messages")); 78 | } 79 | }); 80 | 81 | Button multiLineWithActionButton = (Button) findViewById(R.id.multi_line_with_action); 82 | multiLineWithActionButton.setOnClickListener(new View.OnClickListener() { 83 | @Override 84 | public void onClick(View v) { 85 | SnackbarManager.show( 86 | Snackbar.with(SnackbarSampleActivity.this) 87 | .type(SnackbarType.MULTI_LINE) 88 | .text("This is a multi-line snackbar with an action button. Note " + 89 | "that multi-line snackbars are 2 lines max") 90 | .actionLabel("Action") 91 | .actionListener(new ActionClickListener() { 92 | @Override 93 | public void onActionClicked(Snackbar snackbar) { 94 | Toast.makeText(SnackbarSampleActivity.this, 95 | "Action clicked", 96 | Toast.LENGTH_SHORT).show(); 97 | } 98 | })); 99 | } 100 | }); 101 | 102 | Button noAnimationButton = (Button) findViewById(R.id.no_animation); 103 | noAnimationButton.setOnClickListener(new View.OnClickListener() { 104 | @Override 105 | public void onClick(View v) { 106 | SnackbarManager.show( 107 | Snackbar.with(SnackbarSampleActivity.this) 108 | .text("No animation :(") 109 | .animation(false) 110 | .duration(2500l)); 111 | } 112 | }); 113 | 114 | Button eventListenerButton = (Button) findViewById(R.id.event_listener); 115 | eventListenerButton.setOnClickListener(new View.OnClickListener() { 116 | @Override 117 | public void onClick(View v) { 118 | SnackbarManager.show( 119 | Snackbar.with(SnackbarSampleActivity.this) 120 | .text("I'm showing a toast on exit") 121 | .eventListener(new EventListener() { 122 | @Override 123 | public void onShow(Snackbar snackbar) { 124 | Log.i(TAG, String.format( 125 | "Snackbar will show. Width: %d Height: %d Offset: %d", 126 | snackbar.getWidth(), snackbar.getHeight(), 127 | snackbar.getOffset())); 128 | } 129 | 130 | @Override 131 | public void onShowByReplace(Snackbar snackbar) { 132 | Log.i(TAG, String.format( 133 | "Snackbar will show by replace. Width: %d Height: %d Offset: %d", 134 | snackbar.getWidth(), snackbar.getHeight(), 135 | snackbar.getOffset())); 136 | } 137 | 138 | @Override 139 | public void onShown(Snackbar snackbar) { 140 | Log.i(TAG, String.format( 141 | "Snackbar shown. Width: %d Height: %d Offset: %d", 142 | snackbar.getWidth(), snackbar.getHeight(), 143 | snackbar.getOffset())); 144 | } 145 | 146 | @Override 147 | public void onDismiss(Snackbar snackbar) { 148 | Log.i(TAG, String.format( 149 | "Snackbar will dismiss. Width: %d Height: %d Offset: %d", 150 | snackbar.getWidth(), snackbar.getHeight(), 151 | snackbar.getOffset())); 152 | } 153 | 154 | @Override 155 | public void onDismissByReplace(Snackbar snackbar) { 156 | Log.i(TAG, String.format( 157 | "Snackbar will dismiss by replace. Width: %d Height: %d Offset: %d", 158 | snackbar.getWidth(), snackbar.getHeight(), 159 | snackbar.getOffset())); 160 | } 161 | 162 | @Override 163 | public void onDismissed(Snackbar snackbar) { 164 | Toast.makeText(SnackbarSampleActivity.this, String.format( 165 | "Snackbar dismissed. Width: %d Height: %d Offset: %d", 166 | snackbar.getWidth(), snackbar.getHeight(), 167 | snackbar.getOffset()), 168 | Toast.LENGTH_SHORT).show(); 169 | } 170 | })); 171 | } 172 | }); 173 | 174 | Button customColorsButton = (Button) findViewById(R.id.custom_colors); 175 | customColorsButton.setOnClickListener(new View.OnClickListener() { 176 | @Override 177 | public void onClick(View v) { 178 | SnackbarManager.show( 179 | Snackbar.with(SnackbarSampleActivity.this) 180 | .text("Different colors!!!") 181 | .textColor(Color.parseColor("#ff9d9d9c")) 182 | .color(Color.parseColor("#ff914300")) 183 | .actionLabel("Action") 184 | .actionColor(Color.parseColor("#ff5a2900")) 185 | .actionListener(new ActionClickListener() { 186 | @Override 187 | public void onActionClicked(Snackbar snackbar) { 188 | Log.i(TAG, "Action touched"); 189 | } 190 | }) 191 | .duration(Snackbar.SnackbarDuration.LENGTH_SHORT)); 192 | } 193 | }); 194 | 195 | Button unswipeableButton = (Button) findViewById(R.id.unswipeable); 196 | unswipeableButton.setOnClickListener(new View.OnClickListener() { 197 | @Override 198 | public void onClick(View v) { 199 | SnackbarManager.show( 200 | Snackbar.with(SnackbarSampleActivity.this) 201 | .text("Try to swipe me off from the screen") 202 | .swipeToDismiss(false)); 203 | } 204 | }); 205 | 206 | Button indefiniteButton = (Button) findViewById(R.id.indefinite); 207 | indefiniteButton.setOnClickListener(new View.OnClickListener() { 208 | @Override 209 | public void onClick(View v) { 210 | SnackbarManager.show( 211 | Snackbar.with(SnackbarSampleActivity.this) 212 | .type(SnackbarType.MULTI_LINE) 213 | .duration(Snackbar.SnackbarDuration.LENGTH_INDEFINITE) 214 | .text("Indefinite duration, ideal for communicating errors")); 215 | } 216 | }); 217 | 218 | Button listSampleButton = (Button) findViewById(R.id.list_example); 219 | listSampleButton.setOnClickListener(new View.OnClickListener() { 220 | @Override 221 | public void onClick(View v) { 222 | Intent sampleIntent = new Intent(SnackbarSampleActivity.this, 223 | SnackbarListViewSampleActivity.class); 224 | startActivity(sampleIntent); 225 | } 226 | }); 227 | 228 | Button recyclerSampleButton = (Button) findViewById(R.id.recycler_example); 229 | recyclerSampleButton.setOnClickListener(new View.OnClickListener() { 230 | @Override 231 | public void onClick(View v) { 232 | Intent sampleIntent = new Intent(SnackbarSampleActivity.this, 233 | SnackbarRecyclerViewSampleActivity.class); 234 | startActivity(sampleIntent); 235 | } 236 | }); 237 | 238 | Button customTypefaceButton = (Button) findViewById(R.id.typeface_example); 239 | customTypefaceButton.setOnClickListener(new View.OnClickListener() { 240 | @Override 241 | public void onClick(View v) { 242 | Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/Roboto-LightItalic.ttf"); 243 | SnackbarManager.show( 244 | Snackbar.with(SnackbarSampleActivity.this) 245 | .text("Custom font!") 246 | .textTypeface(tf) 247 | .actionLabel("Cool") 248 | .actionLabelTypeface(tf)); 249 | } 250 | }); 251 | 252 | Button navigationBarTranslucentButton = (Button) findViewById(R.id.navigation_bar_translucent); 253 | navigationBarTranslucentButton.setOnClickListener(new View.OnClickListener() { 254 | @Override 255 | public void onClick(View v) { 256 | 257 | if(SnackbarNavigationBarTranslucentSampleActivity.isTranslucentSystemBarsCapable()) { 258 | Intent sampleIntent = new Intent(SnackbarSampleActivity.this, 259 | SnackbarNavigationBarTranslucentSampleActivity.class); 260 | startActivity(sampleIntent); 261 | } else { 262 | Toast.makeText(SnackbarSampleActivity.this, 263 | "Translucent System bars only available for KITKAT or newer", 264 | Toast.LENGTH_SHORT).show(); 265 | } 266 | } 267 | }); 268 | 269 | 270 | Button immersiveModeButton = (Button) findViewById(R.id.immersive_mode_example); 271 | immersiveModeButton.setOnClickListener(new View.OnClickListener() { 272 | @Override 273 | public void onClick(View v) { 274 | 275 | if(SnackbarImmersiveModeSampleActivity.isImmersiveModeCapable()) { 276 | Intent sampleIntent = new Intent(SnackbarSampleActivity.this, 277 | SnackbarImmersiveModeSampleActivity.class); 278 | startActivity(sampleIntent); 279 | } else { 280 | Toast.makeText(SnackbarSampleActivity.this, 281 | "Immersive mode only available for KITKAT or newer", 282 | Toast.LENGTH_SHORT).show(); 283 | } 284 | } 285 | }); 286 | 287 | Button showInDialogButton = (Button) findViewById(R.id.show_in_dialog_example); 288 | showInDialogButton.setOnClickListener(new View.OnClickListener() { 289 | @Override 290 | public void onClick(View v) { 291 | Intent sampleIntent = new Intent(SnackbarSampleActivity.this, 292 | SnackbarShowInDialogSampleActivity.class); 293 | startActivity(sampleIntent); 294 | } 295 | }); 296 | 297 | Button singleLineMarginsButton = (Button) findViewById(R.id.single_line_margins); 298 | singleLineMarginsButton.setOnClickListener(new View.OnClickListener() { 299 | @Override 300 | public void onClick(View v) { 301 | SnackbarManager.show( 302 | Snackbar.with(SnackbarSampleActivity.this) 303 | .margin(25) 304 | .text("Single-line Margins")); 305 | } 306 | }); 307 | 308 | Button singleLineTopButton = (Button) findViewById(R.id.single_line_top); 309 | singleLineTopButton.setOnClickListener(new View.OnClickListener() { 310 | @Override 311 | public void onClick(View v) { 312 | SnackbarManager.show( 313 | Snackbar.with(SnackbarSampleActivity.this).position(Snackbar.SnackbarPosition.TOP) 314 | .text("Single-line Top")); 315 | } 316 | }); 317 | 318 | 319 | Button singleLineButtonInside = (Button) findViewById(R.id.single_line_inside); 320 | singleLineButtonInside.setOnClickListener(new View.OnClickListener() { 321 | @Override 322 | public void onClick(View v) { 323 | SnackbarManager.show( 324 | Snackbar.with(SnackbarSampleActivity.this) 325 | .text("Single-line Inside RelativeLayout") 326 | , (android.view.ViewGroup) findViewById(R.id.view_relative_layout)); 327 | 328 | } 329 | }); 330 | 331 | Button singleLineTopButtonInside = (Button) findViewById(R.id.single_line_top_inside); 332 | singleLineTopButtonInside.setOnClickListener(new View.OnClickListener() { 333 | @Override 334 | public void onClick(View v) { 335 | SnackbarManager.show( 336 | Snackbar.with(SnackbarSampleActivity.this) 337 | .position(Snackbar.SnackbarPosition.TOP) 338 | .margin(25, 15) 339 | .text("Single-line TOP Inside LinearLayout") 340 | , (android.view.ViewGroup) findViewById(R.id.view_linear_layout)); 341 | } 342 | }); 343 | 344 | Button singleLineButtonCustomShape = (Button) findViewById(R.id.single_line_shape); 345 | singleLineButtonCustomShape.setOnClickListener(new View.OnClickListener() { 346 | @Override 347 | public void onClick(View v) { 348 | SnackbarManager.show( 349 | Snackbar.with(SnackbarSampleActivity.this) 350 | .position(Snackbar.SnackbarPosition.TOP) 351 | .margin(15, 15) 352 | .backgroundDrawable(R.drawable.custom_shape) 353 | .text("Single-line Custom Shape")); 354 | } 355 | }); 356 | } 357 | 358 | @Override 359 | public boolean onCreateOptionsMenu(Menu menu) { 360 | MenuInflater inflater = getMenuInflater(); 361 | inflater.inflate(R.menu.sample, menu); 362 | return true; 363 | } 364 | 365 | @Override 366 | public boolean onOptionsItemSelected(MenuItem item) { 367 | switch (item.getItemId()) { 368 | case R.id.visit_repo: 369 | goToRepo(); 370 | return true; 371 | default: 372 | return super.onOptionsItemSelected(item); 373 | } 374 | } 375 | 376 | private void goToRepo() { 377 | Uri uri = Uri.parse(getString(R.string.repo_url)); 378 | Intent intent = new Intent(Intent.ACTION_VIEW, uri); 379 | startActivity(intent); 380 | } 381 | } 382 | -------------------------------------------------------------------------------- /sample/src/main/java/com/nispok/samples/snackbar/SnackbarShowInDialogSampleActivity.java: -------------------------------------------------------------------------------- 1 | package com.nispok.samples.snackbar; 2 | 3 | import android.app.AlertDialog; 4 | import android.app.Dialog; 5 | import android.content.Context; 6 | import android.content.DialogInterface; 7 | import android.graphics.Color; 8 | import android.os.Bundle; 9 | import android.support.annotation.NonNull; 10 | import android.support.v4.app.DialogFragment; 11 | import android.support.v7.app.ActionBarActivity; 12 | import android.view.LayoutInflater; 13 | import android.view.MenuItem; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.widget.AdapterView; 17 | import android.widget.ArrayAdapter; 18 | import android.widget.Button; 19 | import android.widget.ListView; 20 | 21 | import com.nispok.snackbar.Snackbar; 22 | import com.nispok.snackbar.SnackbarManager; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | import static android.widget.AdapterView.OnItemClickListener; 28 | 29 | public class SnackbarShowInDialogSampleActivity extends ActionBarActivity { 30 | 31 | private static final String TAG = SnackbarShowInDialogSampleActivity.class.getSimpleName(); 32 | private static final String FRAGMENT_TAG_MY_DIALOG = "MyDialog"; 33 | 34 | @Override 35 | protected void onCreate(Bundle savedInstanceState) { 36 | super.onCreate(savedInstanceState); 37 | setContentView(R.layout.activity_show_in_dialog_sample); 38 | 39 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 40 | 41 | final Button button = (Button) findViewById(android.R.id.button1); 42 | 43 | button.setOnClickListener(new View.OnClickListener() { 44 | @Override 45 | public void onClick(View v) { 46 | openDialog(); 47 | } 48 | }); 49 | } 50 | 51 | private void openDialog() { 52 | getSupportFragmentManager() 53 | .beginTransaction() 54 | .add(new MyDialogFragment(), FRAGMENT_TAG_MY_DIALOG) 55 | .commit(); 56 | } 57 | 58 | @Override 59 | public boolean onOptionsItemSelected(MenuItem item) { 60 | switch (item.getItemId()) { 61 | case android.R.id.home: 62 | finish(); 63 | return true; 64 | default: 65 | return super.onOptionsItemSelected(item); 66 | } 67 | } 68 | 69 | public static class MyDialogFragment extends DialogFragment implements OnItemClickListener { 70 | private ViewGroup mSnackbarContainer; 71 | private ListView mListView; 72 | 73 | @NonNull 74 | @Override 75 | public Dialog onCreateDialog(Bundle savedInstanceState) { 76 | Context context = getActivity(); 77 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 78 | 79 | View view = LayoutInflater.from(context).inflate(R.layout.fragment_dialog_list, null, false); 80 | 81 | mListView = (ListView) view.findViewById(android.R.id.list); 82 | mSnackbarContainer = (ViewGroup) view.findViewById(R.id.snackbar_container); 83 | 84 | List data = new ArrayList(); 85 | 86 | for (int i = 0; i < 25; i++) { 87 | data.add(String.format("Item %d", (i + 1))); 88 | } 89 | 90 | ArrayAdapter adapter = new ArrayAdapter( 91 | context, android.R.layout.simple_list_item_1, data); 92 | 93 | mListView.setAdapter(adapter); 94 | mListView.setOnItemClickListener(this); 95 | 96 | builder.setView(view); 97 | builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 98 | @Override 99 | public void onClick(DialogInterface dialog, int which) { 100 | } 101 | }); 102 | 103 | return builder.create(); 104 | } 105 | 106 | @Override 107 | public void onDismiss(DialogInterface dialog) { 108 | super.onDismiss(dialog); 109 | 110 | mListView.setOnItemClickListener(null); 111 | mListView = null; 112 | mSnackbarContainer = null; 113 | } 114 | 115 | @Override 116 | public void onItemClick(AdapterView parent, View view, int position, long id) { 117 | SnackbarManager.show( 118 | Snackbar.with(getActivity()) 119 | .text(String.format("Item %d pressed", (position + 1))) 120 | .actionLabel("Close") 121 | .actionColor(Color.parseColor("#FF8A80")) 122 | .duration(Snackbar.SnackbarDuration.LENGTH_LONG) 123 | .attachToAbsListView(mListView), 124 | mSnackbarContainer, true); 125 | 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /sample/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nispok/snackbar/a4e0449874423031107f6aaa7e97d0f1714a1d3b/sample/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nispok/snackbar/a4e0449874423031107f6aaa7e97d0f1714a1d3b/sample/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nispok/snackbar/a4e0449874423031107f6aaa7e97d0f1714a1d3b/sample/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nispok/snackbar/a4e0449874423031107f6aaa7e97d0f1714a1d3b/sample/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable/custom_shape.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_immersive_mode_sample.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 |