├── .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 | [](https://travis-ci.org/nispok/snackbar) [](https://oss.sonatype.org/content/repositories/releases/com/nispok/snackbar/) [](https://android-arsenal.com/details/1/1160) [](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 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
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 |
5 |
6 |
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 |
17 |
18 |
23 |
24 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_list_sample.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_navigation_bar_translucent_sample.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
17 |
18 |
23 |
24 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_recyclerview_sample.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_sample.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
16 |
17 |
22 |
23 |
28 |
29 |
34 |
35 |
40 |
41 |
46 |
47 |
52 |
53 |
58 |
59 |
64 |
65 |
70 |
71 |
76 |
77 |
82 |
83 |
88 |
89 |
94 |
95 |
100 |
101 |
106 |
107 |
112 |
113 |
118 |
119 |
124 |
125 |
130 |
131 |
136 |
137 |
143 |
144 |
150 |
151 |
152 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_show_in_dialog_sample.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/fragment_dialog_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
18 |
19 |
--------------------------------------------------------------------------------
/sample/src/main/res/menu/recyclerview_sample.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/sample/src/main/res/menu/sample.xml:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Snackbar Demo
5 | Visit repo
6 | Add Snackbar
7 | https://github.com/nispok/snackbar
8 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/sample/src/test/java/com/nispok/snackbar/SnackbarBuilderTest.java:
--------------------------------------------------------------------------------
1 | package com.nispok.snackbar;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 |
6 | import com.nispok.snackbar.enums.SnackbarType;
7 |
8 | import org.junit.Before;
9 | import org.junit.Test;
10 | import org.junit.runner.RunWith;
11 | import org.robolectric.Robolectric;
12 | import org.robolectric.RobolectricTestRunner;
13 |
14 | import static junit.framework.Assert.assertEquals;
15 | import static junit.framework.Assert.assertFalse;
16 | import static junit.framework.Assert.assertTrue;
17 |
18 | @RunWith(RobolectricTestRunner.class)
19 | public class SnackbarBuilderTest {
20 |
21 | private Snackbar mSnackbar;
22 | private Context mContext;
23 |
24 | @Before
25 | public void setUp() throws Exception {
26 | mContext = Robolectric.application;
27 | }
28 |
29 | @Test
30 | public void testSnackbarShouldBeSingleLineByDefault() {
31 | mSnackbar = Snackbar.with(mContext);
32 |
33 | assertEquals(SnackbarType.SINGLE_LINE, mSnackbar.getType());
34 | }
35 |
36 | @Test
37 | public void testSnackbarTypeCanBeChanged() {
38 | mSnackbar = Snackbar.with(mContext).type(SnackbarType.MULTI_LINE);
39 |
40 | assertEquals(SnackbarType.MULTI_LINE, mSnackbar.getType());
41 | }
42 |
43 | @Test
44 | public void testSnackbarShouldHaveLongLengthDurationSetByDefault() {
45 | mSnackbar = Snackbar.with(mContext);
46 |
47 | assertEquals(Snackbar.SnackbarDuration.LENGTH_LONG.getDuration(), mSnackbar.getDuration());
48 | }
49 |
50 | @Test
51 | public void testSnackbarDurationCanBeChanged() {
52 | mSnackbar = Snackbar.with(mContext).duration(Snackbar.SnackbarDuration.LENGTH_SHORT);
53 |
54 | assertEquals(Snackbar.SnackbarDuration.LENGTH_SHORT.getDuration(), mSnackbar.getDuration());
55 | }
56 |
57 | @Test
58 | public void testSnackbarTextColorCanBeChanged() {
59 | mSnackbar = Snackbar.with(mContext).textColor(Color.GREEN);
60 |
61 | assertEquals(Color.GREEN, mSnackbar.getTextColor());
62 | }
63 |
64 | @Test
65 | public void testSnackbarActionColorCanBeChanged() {
66 | mSnackbar = Snackbar.with(mContext).actionColor(Color.BLUE);
67 |
68 | assertEquals(Color.BLUE, mSnackbar.getActionColor());
69 | }
70 |
71 | @Test
72 | public void testSnackbarAnimationShouldBeEnabledByDefault() {
73 | mSnackbar = Snackbar.with(mContext);
74 |
75 | assertTrue(mSnackbar.isAnimated());
76 | }
77 |
78 | @Test
79 | public void testSnackbarAnimationCanBeDisabled() {
80 | mSnackbar = Snackbar.with(mContext).animation(false);
81 |
82 | assertFalse(mSnackbar.isAnimated());
83 | }
84 |
85 | @Test
86 | public void testSnackbarCanBeCreatedWithMultipleCustomConfiguration() {
87 | mSnackbar = Snackbar.with(mContext)
88 | .color(Color.BLUE)
89 | .textColor(Color.GREEN)
90 | .text("Aloha!")
91 | .actionLabel("Action")
92 | .type(SnackbarType.MULTI_LINE)
93 | .duration(Snackbar.SnackbarDuration.LENGTH_SHORT);
94 |
95 | assertEquals(Color.BLUE, mSnackbar.getColor());
96 | assertEquals(Color.GREEN, mSnackbar.getTextColor());
97 | assertEquals("Aloha!", mSnackbar.getText());
98 | assertEquals("Action", mSnackbar.getActionLabel());
99 | assertEquals(SnackbarType.MULTI_LINE, mSnackbar.getType());
100 | assertEquals(Snackbar.SnackbarDuration.LENGTH_SHORT.getDuration(), mSnackbar.getDuration());
101 | }
102 |
103 | /*
104 | @Test
105 | public void testSnackbarCanBeCreatedWithColorResources() {
106 | mSnackbar = Snackbar.with(mContext)
107 | .colorResource(R.color.sb__action_bg_color)
108 | .textColorResource(R.color.sb__text_color)
109 | .actionColorResource(R.color.sb__action_bg_color);
110 |
111 | Resources res = mContext.getResources();
112 | int color = res.getColor(R.color.sb__action_bg_color);
113 | int textColor = res.getColor(R.color.sb__text_color);
114 | int actionColor = res.getColor(R.color.sb__action_bg_color);
115 |
116 | assertEquals(color, mSnackbar.getColor());
117 | assertEquals(textColor, mSnackbar.getTextColor());
118 | assertEquals(actionColor, mSnackbar.getActionColor());
119 | }
120 | */
121 |
122 | @Test
123 | public void testSnackbarCanBeCreatedWithCustomDuration() {
124 | mSnackbar = Snackbar.with(mContext)
125 | .duration(1000l);
126 |
127 | assertEquals(1000l, mSnackbar.getDuration());
128 | }
129 |
130 | @Test
131 | public void testSnackbarWithCustomDurationOverrideSnackbarDuration() {
132 | mSnackbar = Snackbar.with(mContext)
133 | .duration(1000l)
134 | .duration(Snackbar.SnackbarDuration.LENGTH_LONG);
135 |
136 | assertEquals(1000l, mSnackbar.getDuration());
137 | }
138 |
139 | @Test
140 | public void testSnackbarCanBeSetToNotDismissOnActionClicked() {
141 | mSnackbar = Snackbar.with(mContext)
142 | .dismissOnActionClicked(false);
143 |
144 | assertFalse(mSnackbar.shouldDismissOnActionClicked());
145 | }
146 |
147 | @Test
148 | public void testSnackbarShouldBeDismissedOnActionClickedByDefault() {
149 | mSnackbar = Snackbar.with(mContext);
150 |
151 | assertTrue(mSnackbar.shouldDismissOnActionClicked());
152 | }
153 |
154 | @Test
155 | public void testSnackbarCustomDurationMustBeGreaterThanZero() {
156 | mSnackbar = Snackbar.with(mContext).duration(0);
157 |
158 | assertFalse(mSnackbar.getDuration() == 0);
159 | }
160 |
161 | }
162 |
--------------------------------------------------------------------------------
/sample/src/test/res/org.robolectric.Config.properties:
--------------------------------------------------------------------------------
1 | emulateSdk=18
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':lib', 'sample'
2 |
--------------------------------------------------------------------------------