Browse Source

Rebased repo.

Aidan Follestad 10 years ago
commit
b27b2168af
70 changed files with 2842 additions and 0 deletions
  1. 86 0
      .gitignore
  2. 21 0
      LICENSE.txt
  3. 303 0
      README.md
  4. 19 0
      build.gradle
  5. BIN
      gradle/wrapper/gradle-wrapper.jar
  6. 6 0
      gradle/wrapper/gradle-wrapper.properties
  7. 164 0
      gradlew
  8. 90 0
      gradlew.bat
  9. 1 0
      library/.gitignore
  10. 25 0
      library/build.gradle
  11. 90 0
      library/library.iml
  12. 17 0
      library/proguard-rules.pro
  13. 13 0
      library/src/androidTest/java/com/afollestad/materialdialogs/ApplicationTest.java
  14. 6 0
      library/src/main/AndroidManifest.xml
  15. BIN
      library/src/main/assets/Roboto-Medium.ttf
  16. BIN
      library/src/main/assets/Roboto-Regular.ttf
  17. 8 0
      library/src/main/java/com/afollestad/materialdialogs/Alignment.java
  18. 10 0
      library/src/main/java/com/afollestad/materialdialogs/DialogAction.java
  19. 47 0
      library/src/main/java/com/afollestad/materialdialogs/MaterialButton.java
  20. 665 0
      library/src/main/java/com/afollestad/materialdialogs/MaterialDialog.java
  21. 8 0
      library/src/main/java/com/afollestad/materialdialogs/Theme.java
  22. 28 0
      library/src/main/java/com/afollestad/materialdialogs/Utils.java
  23. 142 0
      library/src/main/java/com/afollestad/materialdialogs/base/DialogBase.java
  24. BIN
      library/src/main/res/drawable-hdpi/ic_action_close.png
  25. BIN
      library/src/main/res/drawable-mdpi/ic_action_close.png
  26. BIN
      library/src/main/res/drawable-xhdpi/ic_action_close.png
  27. BIN
      library/src/main/res/drawable-xxhdpi/ic_action_close.png
  28. 22 0
      library/src/main/res/layout/dialog_listitem.xml
  29. 31 0
      library/src/main/res/layout/dialog_listitem_multichoice.xml
  30. 31 0
      library/src/main/res/layout/dialog_listitem_singlechoice.xml
  31. 143 0
      library/src/main/res/layout/material_dialog.xml
  32. 7 0
      library/src/main/res/values-v14/dimens.xml
  33. 27 0
      library/src/main/res/values-v14/styles.xml
  34. 6 0
      library/src/main/res/values-v19/styles.xml
  35. 22 0
      library/src/main/res/values-v21/styles.xml
  36. 10 0
      library/src/main/res/values/attrs.xml
  37. 21 0
      library/src/main/res/values/colors.xml
  38. 20 0
      library/src/main/res/values/dimens.xml
  39. 8 0
      library/src/main/res/values/strings.xml
  40. 41 0
      library/src/main/res/values/styles.xml
  41. 1 0
      sample/.gitignore
  42. BIN
      sample/art/example1.png
  43. BIN
      sample/art/example2.png
  44. BIN
      sample/art/example3.png
  45. BIN
      sample/art/example4.png
  46. BIN
      sample/art/example5.png
  47. BIN
      sample/art/example6.png
  48. BIN
      sample/art/example7.png
  49. BIN
      sample/art/example8.png
  50. BIN
      sample/art/example_0.png
  51. 25 0
      sample/build.gradle
  52. BIN
      sample/keystore.jks
  53. 17 0
      sample/proguard-rules.pro
  54. 88 0
      sample/sample.iml
  55. 13 0
      sample/src/androidTest/java/com/afollestad/materialdialogssample/ApplicationTest.java
  56. 18 0
      sample/src/main/AndroidManifest.xml
  57. 310 0
      sample/src/main/java/com/afollestad/materialdialogssample/MainActivity.java
  58. BIN
      sample/src/main/res/drawable-hdpi/ic_launcher.png
  59. BIN
      sample/src/main/res/drawable-mdpi/ic_launcher.png
  60. BIN
      sample/src/main/res/drawable-xhdpi/ic_launcher.png
  61. BIN
      sample/src/main/res/drawable-xxhdpi/ic_launcher.png
  62. BIN
      sample/src/main/res/drawable-xxxhdpi/ic_launcher.png
  63. 83 0
      sample/src/main/res/layout/activity_main.xml
  64. 64 0
      sample/src/main/res/layout/dialog_customview.xml
  65. 8 0
      sample/src/main/res/menu/main.xml
  66. 10 0
      sample/src/main/res/values/colors.xml
  67. 7 0
      sample/src/main/res/values/dimens.xml
  68. 49 0
      sample/src/main/res/values/strings.xml
  69. 10 0
      sample/src/main/res/values/styles.xml
  70. 1 0
      settings.gradle

+ 86 - 0
.gitignore

@@ -0,0 +1,86 @@
+# Created by https://www.gitignore.io
+
+### Android ###
+
+# Files for the Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+*.log
+
+
+### Intellij ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
+
+/*.iml
+
+## Directory-based project format:
+.idea/
+# if you remove the above rule, at least ignore the following:
+
+# User-specific stuff:
+# .idea/workspace.xml
+# .idea/tasks.xml
+# .idea/dictionaries
+
+# Sensitive or high-churn files:
+# .idea/dataSources.ids
+# .idea/dataSources.xml
+# .idea/sqlDataSources.xml
+# .idea/dynamic.xml
+# .idea/uiDesigner.xml
+
+# Gradle:
+# .idea/gradle.xml
+# .idea/libraries
+
+# Mongo Explorer plugin:
+# .idea/mongoSettings.xml
+
+## File-based project format:
+*.ipr
+*.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+
+# Ignore Gradle GUI config
+gradle-app.setting
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*

+ 21 - 0
LICENSE.txt

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) [year] [fullname]
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 303 - 0
README.md

@@ -0,0 +1,303 @@
+# Material Dialogs
+
+Welcome. This library was designed to solve a personal problem with my apps, I use AppCompat to use
+Material theming on versions of Android below Lollipop. However, AppCompat doesn't theme AlertDialogs
+to use Material on pre-Lollipop. This library allows you to use a consistently Material themed dialog on
+all versions of Android, along with specific customizations that make it easier to brand the dialog.
+
+The code you see below is also found in the sample project. You can download a APK of the sample here: https://github.com/afollestad/material-dialogs/blob/master/sample/sample.apk
+
+It's also available on Google Play: https://play.google.com/store/apps/details?id=com.afollestad.materialdialogssample
+
+---
+
+### Basic Dialog
+
+Here's a basic example that mimics the dialog you see on Google's Material design guidelines
+(here: http://www.google.com/design/spec/components/dialogs.html#dialogs-usage). Note that you can
+always substitute literal strings and string resources for methods that take strings, the same goes
+for color resources (e.g. `titleColor` and `titleColorRes`).
+
+```java
+new MaterialDialog.Builder(this)
+        .title("Permissions")
+        .content("This app determines your phone's location and shares it with Google in order to serve personalized alerts to you. This allows for a better overall app experience.")
+        .theme(Theme.LIGHT)  // the default is light, so you don't need this line
+        .positiveText("Accept)  // the default is 'OK'
+        .negativeText("Decline")  // leaving this line out will remove the negative button
+        .build()
+        .show();
+```
+
+![Example 1](/sample/art/example1.png)
+
+On Lollipop (API 21), the Material dialog will automatically match the `positiveColor` (which is used on the
+positive action button) to the `colorAccent` attribute of your styles.xml theme.
+
+Dialogs don't even need a title:
+
+![Example 0](/sample/art/example_0.png)
+
+---
+
+### Stacked Buttons
+
+If the action text is too long, it will stack the buttons as also seen on Google's Material design guidelines.
+
+```java
+new MaterialDialog.Builder(this)
+        .title("Permissions")
+        .content("This app determines your phone's location and shares it with Google in order to serve personalized alerts to you. This allows for a better overall app experience.")
+        .positiveText("Turn on speed boost")
+        .negativeText("No thanks")
+        .build()
+        .show();
+```
+
+![Example 2](/sample/art/example2.png)
+
+---
+
+### Neutral Button
+
+You can specify neutral text in addition to the positive and negative text. On smaller screens, it will
+cause the buttons to be stacked due to limited space. On a larger screen, however, it will show the neutral
+action on the far left as seen on the Design Guidelines (here: http://www.google.com/design/spec/components/dialogs.html#dialogs-actions).
+
+```java
+new MaterialDialog.Builder(this)
+        .title("Permissions")
+        .content("This app determines your phone's location and shares it with Google in order to serve personalized alerts to you. This allows for a better overall app experience.")
+        .positiveText("Accept")
+        .negativeText("Decline")
+        .neutralText("More info")
+        .build()
+        .show();
+```
+
+The result on a tablet:
+
+![Example 3](/sample/art/example3.png)
+
+---
+
+### Callbacks
+
+To know when the user selects a button, you set a callback. There's three variations of the callback for the action buttons:
+
+```java
+new MaterialDialog.Builder(this)
+                .callback(new MaterialDialog.SimpleCallback() {
+                    @Override
+                    public void onPositive(MaterialDialog dialog) {
+                    }
+                });
+
+new MaterialDialog.Builder(this)
+                .callback(new MaterialDialog.Callback() {
+                    @Override
+                    public void onPositive(MaterialDialog dialog) {
+                    }
+
+                    @Override
+                    public void onNegative(MaterialDialog dialog) {
+                    }
+                });
+
+new MaterialDialog.Builder(this)
+                .callback(new MaterialDialog.FullCallback() {
+                    @Override
+                    public void onPositive(MaterialDialog dialog) {
+                    }
+
+                    @Override
+                    public void onNegative(MaterialDialog dialog) {
+                    }
+
+                    @Override
+                    public void onNeutral(MaterialDialog dialog) {
+                    }
+                });
+```
+
+You can choose which one to use based on which actions you make visible, and which actions need to trigger an event.
+If you pass text to an action, it will become visible (not including the positive action which is always visible
+and will default to 'Accept' unless you make the dialog a list dialog).
+You don't need a callback to make actions visible. But the dialog will not dismiss when an action is pressed if no callback is set for it.
+
+---
+
+### List Dialogs
+
+Creating a list dialog only requires passing in an array of strings. The callback (`itemsCallback`) is
+also very simple.
+
+```java
+new MaterialDialog.Builder(this)
+        .title("Social Networks")
+        .items(new String[]{"Twitter", "Google+", "Instagram", "Facebook"})
+        .itemsCallback(new MaterialDialog.ListCallback() {
+            @Override
+            public void onSelection(MaterialDialog dialog, int which, String text) {
+            }
+        })
+        .build()
+        .show();
+```
+
+![Example 4](/sample/art/example4.png)
+
+---
+
+### Single Choice List Dialogs
+
+Single choice list dialogs are almost identical to regular list dialogs. The only difference is that
+you use `itemsCallbackSingleChoice` to set a callback rather than `itemsCallback`. That signals the dialog to
+display radio buttons next to list items.
+
+This also makes it so that an action button has to be pressed, tapping a list item won't dismiss the dialog.
+Note that this means the positive action button callback will be overridden if you specify one.
+
+```java
+new MaterialDialog.Builder(this)
+        .title("Social Networks")
+        .items(new String[]{"Twitter", "Google+", "Instagram", "Facebook"})
+        .itemsCallbackSingleChoice(-1, new MaterialDialog.ListCallback() {
+            @Override
+            public void onSelection(MaterialDialog dialog, int which, String text) {
+            }
+        })
+        .positiveText("Choose")
+        .build()
+        .show();
+```
+
+The result:
+
+![Example 5](/sample/art/example5.png)
+
+If you want to preselected an item, pass an index 0 or greater in place of -1 in `itemsCallbackSingleChoice()`.
+
+---
+
+### Multi Choice List Dialogs
+
+Multiple choice list dialogs are almost identical to regular list dialogs. The only difference is that
+you use `itemsCallbackMultiChoice` to set a callback rather than `itemsCallback`. That signals the dialog to
+display check boxes next to list items, and the callback can return multiple selections.
+
+This also makes it so that an action button has to be pressed, tapping a list item won't dismiss the dialog.
+Note that this means the positive action button callback will be overridden if you specify one.
+
+```java
+new MaterialDialog.Builder(this)
+        .title("Social Networks")
+        .items(new String[]{"Twitter", "Google+", "Instagram", "Facebook"})
+        .itemsCallbackMultiChoice(null, new MaterialDialog.ListCallbackMulti() {
+            @Override
+            public void onSelection(MaterialDialog dialog, Integer[] which, String[] text) {
+            }
+        })
+        .positiveText("Choose")
+        .build()
+        .show();
+```
+
+The result:
+
+![Example 6](/sample/art/example6.png)
+
+If you want to preselected item(s), pass an array of indices in place of null in `itemsCallbackSingleChoice()`.
+For an example, `new Integer[] { 2, 5 }`.
+
+---
+
+### Custom Views
+
+Custom views are very easy to implement. To match the dialog show here: http://www.google.com/design/spec/components/dialogs.html#dialogs-behavior
+
+```java
+new MaterialDialog.Builder(this)
+        .title("Google Wifi")
+        .positiveText("Accept")
+        .customView(R.layout.custom_view)
+        .positiveText("Connect")
+        .positiveColor(Color.parseColor("#03a9f4"))
+        .build()
+        .show();
+```
+
+Where `custom_view.xml` contains a LinearLayout of TextViews, an EditText, and a CheckBox. No padding
+is used on the top, bottom, left, or right of the root view, that's all stock to the dialog. Note that
+your custom view's top and bottom margins will be overrided; if your custom view is a ViewGroup (e.g.
+a LinearLayout or RelativeLayout), then the first and last child's top and bottom will be overided.
+
+`MaterialDialog` inserts your view into a `ScrollView` and displays a divider above the action buttons,
+so don't wrap your custom view in a scroll view and don't worry about it being too long or needing a divider.
+However, you should avoid making any content that wouldn't belong in a dialog because of its size.
+
+![Example 7](/sample/art/example7.png)
+
+---
+
+### Theming
+
+Before Lollipop, theming AlertDialogs was basically impossible without using reflection and custom drawables.
+Since KitKat, Android became more color neutral but AlertDialogs continued to use Holo Blue for the title and
+title divider. Lollipop has improved even more, with no colors in the dialog by default other than the action
+buttons. This library makes theming even easier. Here's a basic example:
+
+```java
+final int materialRed500 = Color.parseColor("#D50000");
+new MaterialDialog.Builder(this)
+        .title("Permissions")
+        .content("This app determines your phone's location and shares it with Google in order to serve personalized alerts to you. This allows for a better overall app experience.")
+        .positiveText("Accept")
+        .negativeText("Decline")
+        .positiveColor(materialRed500)
+        .titleAlignment(Alignment.CENTER)
+        .titleColor(materialRed500)
+        .theme(Theme.DARK)
+        .build()
+        .show();
+```
+
+The result:
+
+![Example 8](/sample/art/example8.png)
+
+To see more colors that fit the Material design palette, see this page: http://www.google.com/design/spec/style/color.html#color-color-palette
+
+---
+
+### Misc
+
+If you need to access a View in the custom view set to a MaterialDialog, you can use `getCustomView()` of
+MaterialDialog. This is especially useful if you pass a layout resource to the Builder.
+
+```java
+MaterialDialog dialog = //... initialization via the builder ...
+View view = dialog.getCustomView();
+```
+
+If you want to get a reference to one of the dialog action buttons (e.g. to enable or disable buttons):
+
+```java
+MaterialDialog dialog = //... initialization via the builder ...
+View negative = dialog.getActionButton(DialogAction.NEGATIVE);
+View neutral = dialog.getActionButton(DialogAction.NEUTRAL);
+View positive = dialog.getActionButton(DialogAction.POSITIVE);
+```
+
+If you want to update the title of a dialog action button (you can pass a string resource ID in place of the literal string, too):
+
+```java
+MaterialDialog dialog = //... initialization via the builder ...
+dialog.setActionButton(DialogAction.NEGATIVE, "New Title");
+```
+
+--
+
+### Maven/Gradle Dependency
+
+Coming soon

+ 19 - 0
build.gradle

@@ -0,0 +1,19 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+    repositories {
+        jcenter()
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:0.13.2'
+
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        jcenter()
+    }
+}

BIN
gradle/wrapper/gradle-wrapper.jar


+ 6 - 0
gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-all.zip

+ 164 - 0
gradlew

@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

+ 90 - 0
gradlew.bat

@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 1 - 0
library/.gitignore

@@ -0,0 +1 @@
+/build

+ 25 - 0
library/build.gradle

@@ -0,0 +1,25 @@
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion "21.1.0"
+
+    defaultConfig {
+        applicationId "com.afollestad.materialdialogs"
+        minSdkVersion 14
+        targetSdkVersion 21
+        versionCode 1
+        versionName "1.0"
+    }
+    buildTypes {
+        release {
+            runProguard false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    compile 'com.android.support:appcompat-v7:21.0.0'
+}

+ 90 - 0
library/library.iml

@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="MaterialDialogs" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="android-gradle" name="Android-Gradle">
+      <configuration>
+        <option name="GRADLE_PROJECT_PATH" value=":library" />
+      </configuration>
+    </facet>
+    <facet type="android" name="Android">
+      <configuration>
+        <option name="SELECTED_BUILD_VARIANT" value="debug" />
+        <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
+        <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
+        <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugTest" />
+        <option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
+        <option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugTestSources" />
+        <option name="ALLOW_USER_CONFIGURATION" value="false" />
+        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
+        <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
+        <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
+        <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
+        <option name="LIBRARY_PROJECT" value="true" />
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/test/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/test/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/test/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/test/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/test/debug" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
+      <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
+      <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
+    </content>
+    <orderEntry type="jdk" jdkName="Android API 21 Platform" jdkType="Android SDK" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" exported="" name="support-annotations-21.0.0" level="project" />
+    <orderEntry type="library" exported="" name="support-v4-21.0.0" level="project" />
+    <orderEntry type="library" exported="" name="appcompat-v7-21.0.0" level="project" />
+  </component>
+</module>
+

+ 17 - 0
library/proguard-rules.pro

@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/aidanfollestad/Documents/android-sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}

+ 13 - 0
library/src/androidTest/java/com/afollestad/materialdialogs/ApplicationTest.java

@@ -0,0 +1,13 @@
+package com.afollestad.materialdialogs;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+    public ApplicationTest() {
+        super(Application.class);
+    }
+}

+ 6 - 0
library/src/main/AndroidManifest.xml

@@ -0,0 +1,6 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.afollestad.materialdialogs">
+
+    <application android:allowBackup="true" />
+
+</manifest>

BIN
library/src/main/assets/Roboto-Medium.ttf


BIN
library/src/main/assets/Roboto-Regular.ttf


+ 8 - 0
library/src/main/java/com/afollestad/materialdialogs/Alignment.java

@@ -0,0 +1,8 @@
+package com.afollestad.materialdialogs;
+
+/**
+ * @author Aidan Follestad (afollestad)
+ */
+public enum Alignment {
+    LEFT, CENTER, RIGHT
+}

+ 10 - 0
library/src/main/java/com/afollestad/materialdialogs/DialogAction.java

@@ -0,0 +1,10 @@
+package com.afollestad.materialdialogs;
+
+/**
+ * @author Aidan Follestad (afollestad)
+ */
+public enum DialogAction {
+    POSITIVE,
+    NEUTRAL,
+    NEGATIVE
+}

+ 47 - 0
library/src/main/java/com/afollestad/materialdialogs/MaterialButton.java

@@ -0,0 +1,47 @@
+package com.afollestad.materialdialogs;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+/**
+ * @author Aidan Follestad (afollestad)
+ */
+public class MaterialButton extends TextView {
+
+    public MaterialButton(Context context) {
+        super(context);
+        init();
+    }
+
+    public MaterialButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public MaterialButton(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    private void init() {
+        setClickable(true);
+        TypedArray a = getContext().getTheme().obtainStyledAttributes(new int[]{android.R.attr.selectableItemBackground});
+        try {
+            setBackgroundCompat(a.getDrawable(0));
+        } finally {
+            a.recycle();
+        }
+    }
+
+    private void setBackgroundCompat(Drawable d) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+            setBackgroundDrawable(d);
+        } else {
+            setBackground(d);
+        }
+    }
+}

+ 665 - 0
library/src/main/java/com/afollestad/materialdialogs/MaterialDialog.java

@@ -0,0 +1,665 @@
+package com.afollestad.materialdialogs;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.support.annotation.ArrayRes;
+import android.support.annotation.ColorRes;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.StringRes;
+import android.text.method.LinkMovementMethod;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.LinearLayout;
+import android.widget.RadioButton;
+import android.widget.TextView;
+
+import com.afollestad.materialdialogs.base.DialogBase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author Aidan Follestad (afollestad)
+ */
+public class MaterialDialog extends DialogBase implements View.OnClickListener {
+
+    private Context mContext;
+    private CharSequence positiveText;
+    private TextView positiveButton;
+    private CharSequence neutralText;
+    private TextView neutralButton;
+    private CharSequence negativeText;
+    private TextView negativeButton;
+    private View view;
+    private int positiveColor;
+    private SimpleCallback callback;
+    private ListCallback listCallback;
+    private ListCallback listCallbackSingle;
+    private ListCallbackMulti listCallbackMulti;
+    private View customView;
+    private float buttonHeight;
+    private String[] items;
+    private boolean isStacked;
+    private int selectedIndex;
+    private Integer[] selectedIndices;
+
+    private Typeface regularFont;
+    private Typeface mediumFont;
+
+    MaterialDialog(Builder builder) {
+        super(new ContextThemeWrapper(builder.context, builder.theme == Theme.LIGHT ? R.style.Light : R.style.Dark));
+
+        this.mContext = builder.context;
+        this.view = LayoutInflater.from(builder.context).inflate(R.layout.material_dialog, null);
+        this.customView = builder.customView;
+        this.callback = builder.callback;
+        this.listCallback = builder.listCallback;
+        this.listCallbackSingle = builder.listCallbackSingle;
+        this.listCallbackMulti = builder.listCallbackMulti;
+        this.positiveText = builder.positiveText;
+        this.neutralText = builder.neutralText;
+        this.negativeText = builder.negativeText;
+        this.positiveColor = builder.positiveColor;
+        this.items = builder.items;
+        this.setCancelable(builder.cancelable);
+        this.regularFont = Typeface.createFromAsset(getContext().getResources().getAssets(), "Roboto-Regular.ttf");
+        this.mediumFont = Typeface.createFromAsset(getContext().getResources().getAssets(), "Roboto-Medium.ttf");
+        this.selectedIndex = builder.selectedIndex;
+        this.selectedIndices = builder.selectedIndicies;
+
+        TextView title = (TextView) view.findViewById(R.id.title);
+        TextView content = (TextView) view.findViewById(R.id.content);
+
+        content.setText(builder.content);
+        content.setMovementMethod(new LinkMovementMethod());
+        content.setVisibility(View.VISIBLE);
+        content.setTypeface(regularFont);
+        content.setTextColor(Utils.resolveColor(getContext(), R.attr.content_color));
+        content.setLineSpacing(0f, builder.contentLineSpacingMultiplier);
+        if (this.positiveColor == 0) {
+            content.setLinkTextColor(Utils.resolveColor(getContext(), R.attr.button_color));
+        } else {
+            content.setLinkTextColor(this.positiveColor);
+        }
+        if (builder.contentAlignment == Alignment.CENTER) {
+            content.setGravity(Gravity.CENTER_HORIZONTAL);
+        } else if (builder.contentAlignment == Alignment.RIGHT) {
+            content.setGravity(Gravity.RIGHT);
+        }
+
+        if (customView != null) {
+            title = (TextView) view.findViewById(R.id.titleCustomView);
+            buttonHeight = mContext.getResources().getDimension(R.dimen.button_height_customview);
+            view.findViewById(R.id.mainFrame).setVisibility(View.GONE);
+            view.findViewById(R.id.customViewScrollParent).setVisibility(View.VISIBLE);
+            view.findViewById(R.id.customViewDivider).setVisibility(View.VISIBLE);
+            view.findViewById(R.id.customViewDivider).setBackgroundColor(Utils.resolveColor(getContext(), R.attr.divider_color));
+            View firstChild = customView;
+            View lastChild = customView;
+            final int frameMargin = (int) mContext.getResources().getDimension(R.dimen.dialog_frame_margin);
+            if (customView instanceof ViewGroup) {
+                ViewGroup group = (ViewGroup) customView;
+                firstChild = group.getChildAt(0);
+                lastChild = group.getChildAt(group.getChildCount() - 1);
+            }
+            if (builder.title == null || builder.title.toString().trim().isEmpty())
+                setMargin(firstChild, frameMargin, -1, -1, -1);
+            setMargin(lastChild, -1, frameMargin, -1, -1);
+            ((LinearLayout) view.findViewById(R.id.customViewFrame)).addView(customView);
+        } else {
+            buttonHeight = mContext.getResources().getDimension(R.dimen.button_height);
+            view.findViewById(R.id.mainFrame).setVisibility(View.VISIBLE);
+            view.findViewById(R.id.customViewScrollParent).setVisibility(View.GONE);
+            view.findViewById(R.id.customViewDivider).setVisibility(View.GONE);
+        }
+
+        // Title is set after it's determined whether to use first title or custom view title
+        if (builder.title == null || builder.title.toString().trim().isEmpty()) {
+            title.setVisibility(View.GONE);
+        } else {
+            title.setText(builder.title);
+            title.setTypeface(mediumFont);
+            if (builder.titleColor != -1) {
+                title.setTextColor(builder.titleColor);
+            } else {
+                title.setTextColor(Utils.resolveColor(getContext(), R.attr.title_color));
+            }
+            if (builder.titleAlignment == Alignment.CENTER) {
+                title.setGravity(Gravity.CENTER_HORIZONTAL);
+            } else if (builder.titleAlignment == Alignment.RIGHT) {
+                title.setGravity(Gravity.RIGHT);
+            }
+        }
+
+        invalidateList();
+        invalidateActions();
+        checkIfStackingNeeded();
+        setViewInternal(view);
+    }
+
+    /**
+     * Invalidates the radio buttons in the single choice mode list so that only the radio button that
+     * was previous selected is checked.
+     */
+    private void invalidateSingleChoice(int newSelection) {
+        LinearLayout list = (LinearLayout) view.findViewById(R.id.listFrame);
+        for (int i = 0; i < list.getChildCount(); i++) {
+            View v = list.getChildAt(i);
+            @SuppressLint("WrongViewCast")
+            RadioButton rb = (RadioButton) v.findViewById(R.id.control);
+            rb.setChecked(newSelection == i);
+        }
+    }
+
+    /**
+     * Constructs the dialog's list content and sets up click listeners.
+     */
+    @SuppressLint("WrongViewCast")
+    private void invalidateList() {
+        if (items == null || items.length == 0) return;
+        view.findViewById(R.id.content).setVisibility(View.GONE);
+
+        View title = view.findViewById(R.id.title);
+        setMargin(title, -1, (int) mContext.getResources().getDimension(R.dimen.button_padding), 0, 0);
+        View mainFrame = view.findViewById(R.id.mainFrame);
+        int dpPadding = (int) mContext.getResources().getDimension(R.dimen.button_frame_margin);
+        setMargin(mainFrame, dpPadding, 0, dpPadding, dpPadding);
+
+        view.findViewById(R.id.listFrameScrollParent).setVisibility(View.VISIBLE);
+        LinearLayout list = (LinearLayout) view.findViewById(R.id.listFrame);
+        LayoutInflater li = LayoutInflater.from(mContext);
+
+        final int itemColor = Utils.resolveColor(getContext(), R.attr.item_color);
+        for (int index = 0; index < items.length; index++) {
+            View il;
+            if (listCallbackSingle != null) {
+                il = li.inflate(R.layout.dialog_listitem_singlechoice, null);
+                if (selectedIndex > -1) {
+                    ((RadioButton) il.findViewById(R.id.control)).setChecked(selectedIndex == index);
+                }
+            } else if (listCallbackMulti != null) {
+                il = li.inflate(R.layout.dialog_listitem_multichoice, null);
+                if (selectedIndices != null) {
+                    if (Arrays.asList(selectedIndices).contains(index))
+                        ((CheckBox) il.findViewById(R.id.control)).setChecked(true);
+                }
+            } else {
+                il = li.inflate(R.layout.dialog_listitem, null);
+            }
+            TextView tv = (TextView) il.findViewById(R.id.title);
+            tv.setText(items[index]);
+            tv.setTextColor(itemColor);
+            il.setTag(index + ":" + items[index]);
+            il.setOnClickListener(this);
+            list.addView(il);
+        }
+    }
+
+    /**
+     * Measures the action button's and their text to decide whether or not the button should be stacked.
+     */
+    private void checkIfStackingNeeded() {
+        if (((negativeButton == null || negativeButton.getVisibility() == View.GONE) &&
+                (neutralButton == null || neutralButton.getVisibility() == View.GONE))) {
+            // Stacking isn't necessary if you only have one button
+            return;
+        }
+        Paint paint = positiveButton.getPaint();
+        float buttonMinWidth = mContext.getResources().getDimension(R.dimen.button_min_width);
+        float totalWidth = paint.measureText(positiveButton.getText().toString());
+        if (this.neutralText != null)
+            totalWidth += paint.measureText(neutralButton.getText().toString());
+        if (this.negativeText != null)
+            totalWidth += paint.measureText(negativeButton.getText().toString());
+        isStacked = totalWidth > (buttonMinWidth * 3);
+        invalidateActions();
+    }
+
+    /**
+     * Invalidates the positive/neutral/negative action buttons. Decides whether they should be visible
+     * and sets their properties (such as height, text color, etc.).
+     */
+    private void invalidateActions() {
+        if (items != null && listCallbackSingle == null && listCallbackMulti == null) {
+            // If the dialog is a plain list dialog, no buttons are shown.
+            view.findViewById(R.id.buttonDefaultFrame).setVisibility(View.GONE);
+            view.findViewById(R.id.buttonStackedFrame).setVisibility(View.GONE);
+            return;
+        }
+
+        final int buttonFrameMargin = (int) mContext.getResources().getDimension(R.dimen.button_frame_margin);
+        if (isStacked) {
+            view.findViewById(R.id.buttonDefaultFrame).setVisibility(View.GONE);
+            view.findViewById(R.id.buttonStackedFrame).setVisibility(View.VISIBLE);
+            if (customView == null)
+                setMargin(view.findViewById(R.id.buttonStackedFrame), -1, buttonFrameMargin, -1, -1);
+        } else {
+            view.findViewById(R.id.buttonDefaultFrame).setVisibility(View.VISIBLE);
+            view.findViewById(R.id.buttonStackedFrame).setVisibility(View.GONE);
+            if (customView == null)
+                setMargin(view.findViewById(R.id.buttonDefaultFrame), -1, buttonFrameMargin, -1, -1);
+        }
+
+        positiveButton = (TextView) view.findViewById(
+                isStacked ? R.id.buttonStackedPositive : R.id.buttonDefaultPositive);
+        setHeight(positiveButton, buttonHeight);
+        positiveButton.setTypeface(mediumFont);
+        if (this.positiveText == null)
+            this.positiveText = mContext.getString(R.string.accept);
+        positiveButton.setText(this.positiveText);
+        positiveButton.setTextColor(getActionTextStateList(this.positiveColor));
+
+        positiveButton.setTag(POSITIVE);
+        positiveButton.setOnClickListener(this);
+
+        neutralButton = (TextView) view.findViewById(
+                isStacked ? R.id.buttonStackedNeutral : R.id.buttonDefaultNeutral);
+        setHeight(neutralButton, buttonHeight);
+        neutralButton.setTypeface(mediumFont);
+        if (this.neutralText != null) {
+            neutralButton.setVisibility(View.VISIBLE);
+            neutralButton.setTextColor(getActionTextStateList(0));
+            neutralButton.setText(this.neutralText);
+            neutralButton.setTag(NEUTRAL);
+            neutralButton.setOnClickListener(this);
+        } else {
+            neutralButton.setVisibility(View.GONE);
+        }
+
+        negativeButton = (TextView) view.findViewById(
+                isStacked ? R.id.buttonStackedNegative : R.id.buttonDefaultNegative);
+        setHeight(negativeButton, buttonHeight);
+        negativeButton.setTypeface(mediumFont);
+        if (this.negativeText != null) {
+            negativeButton.setVisibility(View.VISIBLE);
+            negativeButton.setTextColor(getActionTextStateList(0));
+            negativeButton.setText(this.negativeText);
+            negativeButton.setTag(NEGATIVE);
+            negativeButton.setOnClickListener(this);
+        } else {
+            negativeButton.setVisibility(View.GONE);
+        }
+    }
+
+    @Override
+    public final void onClick(View v) {
+        String tag = (String) v.getTag();
+        if (tag.equals(POSITIVE)) {
+            if (listCallbackSingle != null) {
+                dismiss();
+                LinearLayout list = (LinearLayout) view.findViewById(R.id.listFrame);
+                for (int i = 0; i < list.getChildCount(); i++) {
+                    View itemView = list.getChildAt(i);
+                    @SuppressLint("WrongViewCast")
+                    RadioButton rb = (RadioButton) itemView.findViewById(R.id.control);
+                    if (rb.isChecked()) {
+                        listCallbackSingle.onSelection(this, i, ((TextView) itemView.findViewById(R.id.title)).getText().toString());
+                        break;
+                    }
+                }
+            } else if (listCallbackMulti != null) {
+                dismiss();
+                List<Integer> selectedIndices = new ArrayList<Integer>();
+                List<String> selectedTitles = new ArrayList<String>();
+                LinearLayout list = (LinearLayout) view.findViewById(R.id.listFrame);
+                for (int i = 0; i < list.getChildCount(); i++) {
+                    View itemView = list.getChildAt(i);
+                    CheckBox rb = (CheckBox) itemView.findViewById(R.id.control);
+                    if (rb.isChecked()) {
+                        selectedIndices.add(i);
+                        selectedTitles.add(((TextView) itemView.findViewById(R.id.title)).getText().toString());
+                    }
+                }
+                listCallbackMulti.onSelection(this,
+                        selectedIndices.toArray(new Integer[selectedIndices.size()]),
+                        selectedTitles.toArray(new String[selectedTitles.size()]));
+            } else if (callback != null) {
+                dismiss();
+                callback.onPositive(this);
+            }
+        } else if (tag.equals(NEGATIVE)) {
+            if (callback != null && callback instanceof Callback) {
+                dismiss();
+                ((Callback) callback).onNegative(this);
+            }
+        } else if (tag.equals(NEUTRAL)) {
+            if (callback != null && callback instanceof FullCallback) {
+                dismiss();
+                ((FullCallback) callback).onNeutral(this);
+            }
+        } else {
+            String[] split = tag.split(":");
+            int index = Integer.parseInt(split[0]);
+            if (listCallback != null) {
+                dismiss();
+                listCallback.onSelection(this, index, split[1]);
+            } else if (listCallbackSingle != null) {
+                RadioButton cb = (RadioButton) ((LinearLayout) v).getChildAt(0);
+                cb.performClick();
+                invalidateSingleChoice(index);
+            } else if (listCallbackMulti != null) {
+                CheckBox cb = (CheckBox) ((LinearLayout) v).getChildAt(0);
+                cb.performClick();
+            }
+        }
+    }
+
+    /**
+     * The class used to construct a MaterialDialog.
+     */
+    public static class Builder {
+
+        protected Activity context;
+        protected CharSequence title;
+        protected Alignment titleAlignment = Alignment.LEFT;
+        protected Alignment contentAlignment = Alignment.LEFT;
+        protected int titleColor = -1;
+        protected CharSequence content;
+        protected String[] items;
+        protected CharSequence positiveText;
+        protected CharSequence neutralText;
+        protected CharSequence negativeText;
+        protected View customView;
+        protected int positiveColor;
+        protected SimpleCallback callback;
+        protected ListCallback listCallback;
+        protected ListCallback listCallbackSingle;
+        private ListCallbackMulti listCallbackMulti;
+        protected Theme theme = Theme.LIGHT;
+        protected boolean cancelable = true;
+        protected float contentLineSpacingMultiplier = 1.0f;
+        protected int selectedIndex = -1;
+        protected Integer[] selectedIndicies = null;
+
+        public Builder(@NonNull Activity context) {
+            this.context = context;
+            this.positiveText = context.getString(android.R.string.ok);
+            final int materialBlue = context.getResources().getColor(R.color.material_blue_500);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+                TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
+                try {
+                    this.positiveColor = a.getColor(0, materialBlue);
+                } finally {
+                    a.recycle();
+                }
+            } else {
+                this.positiveColor = materialBlue;
+            }
+        }
+
+        public Builder title(@StringRes int titleRes) {
+            title(this.context.getString(titleRes));
+            return this;
+        }
+
+        public Builder title(CharSequence title) {
+            this.title = title;
+            return this;
+        }
+
+        public Builder titleAlignment(Alignment align) {
+            this.titleAlignment = align;
+            return this;
+        }
+
+        public Builder titleColorRes(@ColorRes int colorRes) {
+            titleColor(this.context.getResources().getColor(colorRes));
+            return this;
+        }
+
+        public Builder titleColor(int color) {
+            this.titleColor = color;
+            return this;
+        }
+
+        public Builder content(@StringRes int contentRes) {
+            content(this.context.getString(contentRes));
+            return this;
+        }
+
+        public Builder content(CharSequence content) {
+            this.content = content;
+            return this;
+        }
+
+        public Builder content(@StringRes int contentRes, Object... formatArgs) {
+            content(this.context.getString(contentRes, formatArgs));
+            return this;
+        }
+
+        public Builder contentAlignment(Alignment align) {
+            this.contentAlignment = align;
+            return this;
+        }
+
+        public Builder contentLineSpacing(float multiplier) {
+            this.contentLineSpacingMultiplier = multiplier;
+            return this;
+        }
+
+        public Builder items(@ArrayRes int itemsRes) {
+            items(this.context.getResources().getStringArray(itemsRes));
+            return this;
+        }
+
+        public Builder items(String[] items) {
+            this.items = items;
+            return this;
+        }
+
+        public Builder itemsCallback(ListCallback callback) {
+            this.listCallback = callback;
+            this.listCallbackSingle = null;
+            this.listCallbackMulti = null;
+            return this;
+        }
+
+        /**
+         * Pass anything below 0 (such as -1) for the selected index to leave all options unselected initially.
+         * Otherwise pass the index of an item that will be selected initially.
+         */
+        public Builder itemsCallbackSingleChoice(int selectedIndex, ListCallback callback) {
+            this.selectedIndex = selectedIndex;
+            this.listCallback = null;
+            this.listCallbackSingle = callback;
+            this.listCallbackMulti = null;
+            return this;
+        }
+
+        /**
+         * Pass null for the selected indices to leave all options unselected initially. Otherwise pass
+         * an array of indices that will be selected initially.
+         */
+        public Builder itemsCallbackMultiChoice(Integer[] selectedIndices, ListCallbackMulti callback) {
+            this.selectedIndicies = selectedIndices;
+            this.listCallback = null;
+            this.listCallbackSingle = null;
+            this.listCallbackMulti = callback;
+            return this;
+        }
+
+        public Builder positiveText(@StringRes int postiveRes) {
+            positiveText(this.context.getString(postiveRes));
+            return this;
+        }
+
+        public Builder positiveText(CharSequence message) {
+            this.positiveText = message;
+            return this;
+        }
+
+        public Builder neutralText(@StringRes int neutralRes) {
+            neutralText(this.context.getString(neutralRes));
+            return this;
+        }
+
+        public Builder neutralText(CharSequence message) {
+            this.neutralText = message;
+            return this;
+        }
+
+        public Builder negativeText(@StringRes int negativeRes) {
+            negativeText(this.context.getString(negativeRes));
+            return this;
+        }
+
+        public Builder negativeText(CharSequence message) {
+            this.negativeText = message;
+            return this;
+        }
+
+        public Builder customView(@LayoutRes int layoutRes) {
+            LayoutInflater li = LayoutInflater.from(this.context);
+            customView(li.inflate(layoutRes, null));
+            return this;
+        }
+
+        public Builder customView(View view) {
+            this.customView = view;
+            return this;
+        }
+
+        public Builder positiveColorRes(@ColorRes int colorRes) {
+            positiveColor(this.context.getResources().getColor(colorRes));
+            return this;
+        }
+
+        public Builder positiveColor(int color) {
+            this.positiveColor = color;
+            return this;
+        }
+
+        public Builder callback(SimpleCallback callback) {
+            this.callback = callback;
+            return this;
+        }
+
+        public Builder theme(Theme theme) {
+            this.theme = theme;
+            return this;
+        }
+
+        public Builder cancelable(boolean cancelable) {
+            this.cancelable = cancelable;
+            return this;
+        }
+
+        public MaterialDialog build() {
+            return new MaterialDialog(this);
+        }
+    }
+
+
+    private ColorStateList getActionTextStateList(int newPrimaryColor) {
+        final int buttonColor = Utils.resolveColor(getContext(), R.attr.button_color);
+        if (newPrimaryColor == 0) newPrimaryColor = buttonColor;
+        int[][] states = new int[][]{
+                new int[]{-android.R.attr.state_enabled}, // disabled
+                new int[]{} // enabled
+        };
+        int[] colors = new int[]{
+                Utils.adjustAlpha(buttonColor, 0.6f),
+                newPrimaryColor
+        };
+        return new ColorStateList(states, colors);
+    }
+
+    /**
+     * Retrieves the view of an action button, allowing you to modify properties such as whether or not it's enabled.
+     */
+    public final View getActionButton(DialogAction which) {
+        if (view == null) return null;
+        if (isStacked) {
+            switch (which) {
+                default:
+                    return view.findViewById(R.id.buttonStackedPositive);
+                case NEUTRAL:
+                    return view.findViewById(R.id.buttonStackedNeutral);
+                case NEGATIVE:
+                    return view.findViewById(R.id.buttonStackedNegative);
+            }
+        } else {
+            switch (which) {
+                default:
+                    return view.findViewById(R.id.buttonDefaultPositive);
+                case NEUTRAL:
+                    return view.findViewById(R.id.buttonDefaultNeutral);
+                case NEGATIVE:
+                    return view.findViewById(R.id.buttonDefaultNegative);
+            }
+        }
+    }
+
+    /**
+     * Updates an action button's title, causing invalidation to check if the action buttons should be stacked.
+     *
+     * @param which The action button to update.
+     * @param title The new title of the action button.
+     */
+    public final void setActionButton(DialogAction which, CharSequence title) {
+        switch (which) {
+            default:
+                this.positiveText = title;
+                break;
+            case NEUTRAL:
+                this.neutralText = title;
+                break;
+            case NEGATIVE:
+                this.negativeText = title;
+                break;
+        }
+        invalidateActions();
+    }
+
+    /**
+     * Updates an action button's title, causing invalidation to check if the action buttons should be stacked.
+     *
+     * @param which    The action button to update.
+     * @param titleRes The string resource of the new title of the action button.
+     */
+    public final void setActionButton(DialogAction which, @StringRes int titleRes) {
+        setActionButton(which, mContext.getString(titleRes));
+    }
+
+    /**
+     * Retrieves the custom view that was inflated or set to the MaterialDialog during building.
+     */
+    public final View getCustomView() {
+        return customView;
+    }
+
+
+    public static interface ListCallback {
+        void onSelection(MaterialDialog dialog, int which, String text);
+    }
+
+    public static interface ListCallbackMulti {
+        void onSelection(MaterialDialog dialog, Integer[] which, String[] text);
+    }
+
+    public static interface SimpleCallback {
+        void onPositive(MaterialDialog dialog);
+    }
+
+    public static interface Callback extends SimpleCallback {
+        void onPositive(MaterialDialog dialog);
+
+        void onNegative(MaterialDialog dialog);
+    }
+
+    public static interface FullCallback extends Callback {
+        void onNeutral(MaterialDialog dialog);
+    }
+}

+ 8 - 0
library/src/main/java/com/afollestad/materialdialogs/Theme.java

@@ -0,0 +1,8 @@
+package com.afollestad.materialdialogs;
+
+/**
+ * @author Aidan Follestad (afollestad)
+ */
+public enum Theme {
+    LIGHT, DARK
+}

+ 28 - 0
library/src/main/java/com/afollestad/materialdialogs/Utils.java

@@ -0,0 +1,28 @@
+package com.afollestad.materialdialogs;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+
+/**
+ * @author Aidan Follestad (afollestad)
+ */
+public class Utils {
+
+    public static int adjustAlpha(int color, float factor) {
+        int alpha = Math.round(Color.alpha(color) * factor);
+        int red = Color.red(color);
+        int green = Color.green(color);
+        int blue = Color.blue(color);
+        return Color.argb(alpha, red, green, blue);
+    }
+
+    public static int resolveColor(Context context, int attr) {
+        TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{attr});
+        try {
+            return a.getColor(0, 0);
+        } finally {
+            a.recycle();
+        }
+    }
+}

+ 142 - 0
library/src/main/java/com/afollestad/materialdialogs/base/DialogBase.java

@@ -0,0 +1,142 @@
+package com.afollestad.materialdialogs.base;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Message;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ListView;
+
+/**
+ * @author Aidan Follestad (afollestad)
+ */
+public class DialogBase extends AlertDialog {
+
+    protected final static String POSITIVE = "POSITIVE";
+    protected final static String NEGATIVE = "NEGATIVE";
+    protected final static String NEUTRAL = "NEUTRAL";
+
+    protected DialogBase(Context context) {
+        super(context);
+    }
+
+    public static void setHeight(View view, float height) {
+        setMargin(view, -1, -1, -1, -1, (int) height);
+    }
+
+    public static void setMargin(View view, int top, int bottom, int left, int right) {
+        setMargin(view, top, bottom, left, right, -1);
+    }
+
+    public static void setMargin(View view, int top, int bottom, int left, int right, int height) {
+        ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
+        if (top > -1) params.topMargin = top;
+        if (bottom > -1) params.bottomMargin = bottom;
+        if (left > -1) params.leftMargin = left;
+        if (right > -1) params.rightMargin = right;
+        if (height > -1) params.height = height;
+        view.setLayoutParams(params);
+    }
+
+    /**
+     * @deprecated Use getActionButton(com.afollestad.materialdialogs.DialogAction)} instead.
+     */
+    @Override
+    public Button getButton(int whichButton) {
+        throw new RuntimeException("Use getActionButton(MaterialDialog.Button) instead.");
+    }
+
+    /**
+     * @deprecated Not supported by the Material dialog.
+     */
+    @Override
+    public void setView(View view) {
+        throw new RuntimeException("This method is not supported by the MaterialDialog.");
+    }
+
+    protected void setViewInternal(View view) {
+        super.setView(view);
+    }
+
+    /**
+     * @deprecated Not supported by the Material dialog.
+     */
+    @Override
+    public void setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight, int viewSpacingBottom) {
+        throw new RuntimeException("This method is not supported by the MaterialDialog.");
+    }
+
+    /**
+     * @deprecated Not supported by the Material dialog.
+     */
+    @Override
+    public void setMessage(CharSequence message) {
+        throw new RuntimeException("This method is not supported by the MaterialDialog.");
+    }
+
+    /**
+     * @deprecated Not supported by the Material dialog.
+     */
+    @Override
+    public void setTitle(CharSequence title) {
+        throw new RuntimeException("This method is not supported by the MaterialDialog.");
+    }
+
+    /**
+     * @deprecated Not supported by the Material dialog.
+     */
+    @Override
+    public void setCustomTitle(View customTitleView) {
+        throw new RuntimeException("This method is not supported by the MaterialDialog.");
+    }
+
+    /**
+     * @deprecated Not supported by the Material dialog.
+     */
+    @Override
+    public void setIcon(Drawable icon) {
+        throw new RuntimeException("This method is not supported by the MaterialDialog.");
+    }
+
+    /**
+     * @deprecated Not supported by the Material dialog.
+     */
+    @Override
+    public void setIcon(int resId) {
+        throw new RuntimeException("This method is not supported by the MaterialDialog.");
+    }
+
+    /**
+     * @deprecated Not supported by the Material dialog.
+     */
+    @Override
+    public void setIconAttribute(int attrId) {
+        throw new RuntimeException("This method is not supported by the MaterialDialog.");
+    }
+
+    /**
+     * @deprecated Not supported by the Material dialog.
+     */
+    @Override
+    public void setButton(int whichButton, CharSequence text, Message msg) {
+        throw new RuntimeException("Use setActionButton(MaterialDialog.Button, CharSequence) instead.");
+    }
+
+    /**
+     * @deprecated Not supported by the Material dialog.
+     */
+    @Override
+    public void setButton(int whichButton, CharSequence text, OnClickListener listener) {
+        throw new RuntimeException("Use setActionButton(MaterialDialog.Button, CharSequence) instead.");
+    }
+
+    /**
+     * @deprecated Not supported by the Material dialog.
+     */
+    @Override
+    public ListView getListView() {
+        throw new RuntimeException("This method is not supported by the MaterialDialog.");
+    }
+}

BIN
library/src/main/res/drawable-hdpi/ic_action_close.png


BIN
library/src/main/res/drawable-mdpi/ic_action_close.png


BIN
library/src/main/res/drawable-xhdpi/ic_action_close.png


BIN
library/src/main/res/drawable-xxhdpi/ic_action_close.png


+ 22 - 0
library/src/main/res/layout/dialog_listitem.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:selectableItemBackground"
+    android:clickable="true"
+    android:gravity="center_vertical">
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/listitem_height"
+        tools:text="Item"
+        android:textSize="@dimen/listitem_textsize"
+        android:gravity="center_vertical"
+        android:layout_marginLeft="@dimen/dialog_frame_margin"
+        android:layout_marginRight="@dimen/dialog_frame_margin" />
+
+</LinearLayout>

+ 31 - 0
library/src/main/res/layout/dialog_listitem_multichoice.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:selectableItemBackground"
+    android:clickable="true"
+    android:gravity="center_vertical">
+
+    <CheckBox
+        android:id="@+id/control"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/listitem_height"
+        android:clickable="false"
+        android:gravity="center_vertical"
+        android:layout_marginLeft="@dimen/dialog_frame_margin"
+        android:layout_marginRight="@dimen/dialog_frame_margin" />
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/listitem_height"
+        tools:text="Item"
+        android:textSize="@dimen/listitem_textsize"
+        android:gravity="center_vertical"
+        android:layout_marginRight="@dimen/dialog_frame_margin" />
+
+</LinearLayout>

+ 31 - 0
library/src/main/res/layout/dialog_listitem_singlechoice.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:selectableItemBackground"
+    android:clickable="true"
+    android:gravity="center_vertical">
+
+    <RadioButton
+        android:id="@+id/control"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/listitem_height"
+        android:clickable="false"
+        android:gravity="center_vertical"
+        android:layout_marginLeft="@dimen/dialog_frame_margin"
+        android:layout_marginRight="@dimen/dialog_frame_margin" />
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/listitem_height"
+        tools:text="Item"
+        android:textSize="@dimen/listitem_textsize"
+        android:gravity="center_vertical"
+        android:layout_marginRight="@dimen/dialog_frame_margin" />
+
+</LinearLayout>

+ 143 - 0
library/src/main/res/layout/material_dialog.xml

@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        android:id="@+id/mainFrame"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/dialog_frame_margin"
+        android:layout_marginBottom="@dimen/button_frame_margin"
+        android:layout_marginLeft="@dimen/dialog_frame_margin"
+        android:layout_marginRight="@dimen/dialog_frame_margin">
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/button_frame_margin"
+            android:textSize="@dimen/title_textsize"
+            tools:text="Title" />
+
+        <TextView
+            android:id="@+id/content"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="@dimen/content_textsize"
+            tools:text="Content" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/customViewScrollParent"
+        android:layout_width="fill_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:visibility="gone">
+
+        <ScrollView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <LinearLayout
+                android:id="@+id/customViewFrame"
+                android:orientation="vertical"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="@dimen/dialog_frame_margin"
+                android:layout_marginRight="@dimen/dialog_frame_margin">
+
+                <TextView
+                    android:id="@+id/titleCustomView"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="@dimen/dialog_frame_margin"
+                    android:layout_marginBottom="@dimen/title_margin_customview"
+                    android:textSize="@dimen/title_textsize"
+                    tools:text="Title" />
+
+            </LinearLayout>
+
+        </ScrollView>
+
+    </LinearLayout>
+
+    <View
+        android:id="@+id/customViewDivider"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:id="@+id/listFrameScrollParent"
+        android:layout_width="fill_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:visibility="gone">
+
+        <ScrollView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <LinearLayout
+                android:id="@+id/listFrame"
+                android:orientation="vertical"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+        </ScrollView>
+
+    </LinearLayout>
+
+    <RelativeLayout
+        android:id="@+id/buttonDefaultFrame"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="@dimen/button_frame_margin"
+        android:layout_marginRight="@dimen/button_frame_margin">
+
+        <com.afollestad.materialdialogs.MaterialButton
+            android:id="@+id/buttonDefaultNeutral"
+            style="@style/ActionButton"
+            android:layout_alignParentLeft="true" />
+
+        <com.afollestad.materialdialogs.MaterialButton
+            android:id="@+id/buttonDefaultNegative"
+            style="@style/ActionButton"
+            android:layout_toLeftOf="@+id/buttonDefaultPositive" />
+
+        <com.afollestad.materialdialogs.MaterialButton
+            android:id="@+id/buttonDefaultPositive"
+            style="@style/ActionButton"
+            android:layout_alignParentRight="true" />
+
+    </RelativeLayout>
+
+    <LinearLayout
+        android:id="@+id/buttonStackedFrame"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="@dimen/button_frame_margin"
+        android:layout_marginRight="@dimen/button_frame_margin"
+        android:gravity="right">
+
+        <com.afollestad.materialdialogs.MaterialButton
+            android:id="@+id/buttonStackedPositive"
+            style="@style/ActionButton" />
+
+        <com.afollestad.materialdialogs.MaterialButton
+            android:id="@+id/buttonStackedNegative"
+            style="@style/ActionButton" />
+
+        <com.afollestad.materialdialogs.MaterialButton
+            android:id="@+id/buttonStackedNeutral"
+            style="@style/ActionButton" />
+
+    </LinearLayout>
+
+</LinearLayout>

+ 7 - 0
library/src/main/res/values-v14/dimens.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <!-- Regular button height + 4dp to compensate for padding issues on older versions -->
+    <dimen name="button_height">40dp</dimen>
+
+</resources>

+ 27 - 0
library/src/main/res/values-v14/styles.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <style name="Light" parent="android:Theme.Holo.Light">
+        <item name="android:actionModeStyle">@style/ActionModeStyle</item>
+
+        <item name="title_color">@color/title_light</item>
+        <item name="content_color">@color/content_light</item>
+        <item name="item_color">@color/item_light</item>
+        <item name="button_color">@color/button_light</item>
+        <item name="divider_color">@color/divider_black</item>
+    </style>
+
+    <style name="Dark" parent="android:Theme.Holo">
+        <item name="title_color">@color/title_dark</item>
+        <item name="content_color">@color/content_dark</item>
+        <item name="item_color">@color/item_dark</item>
+        <item name="button_color">@color/button_dark</item>
+        <item name="divider_color">@color/divider_white</item>
+    </style>
+
+    <style name="ActionButton" parent="@style/_ActionButtonParent">
+        <item name="android:layout_marginLeft">@dimen/button_padding</item>
+        <item name="android:layout_marginRight">@dimen/button_padding</item>
+    </style>
+
+</resources>

+ 6 - 0
library/src/main/res/values-v19/styles.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <style name="ActionButton" parent="@style/_ActionButtonParent" />
+
+</resources>

+ 22 - 0
library/src/main/res/values-v21/styles.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <style name="Light" parent="android:Theme.Material.Light.Dialog.Alert">
+        <item name="title_color">@color/title_light</item>
+        <item name="content_color">@color/content_light</item>
+        <item name="item_color">@color/item_light</item>
+        <item name="button_color">@color/button_light</item>
+        <item name="divider_color">@color/divider_black</item>
+    </style>
+
+    <style name="Dark" parent="android:Theme.Material.Dialog.Alert">
+        <item name="title_color">@color/title_dark</item>
+        <item name="content_color">@color/content_dark</item>
+        <item name="item_color">@color/item_dark</item>
+        <item name="button_color">@color/button_dark</item>
+        <item name="divider_color">@color/divider_white</item>
+    </style>
+
+    <style name="ActionButton" parent="@style/_ActionButtonParent" />
+
+</resources>

+ 10 - 0
library/src/main/res/values/attrs.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <attr name="title_color" format="color" />
+    <attr name="content_color" format="color" />
+    <attr name="item_color" format="color" />
+    <attr name="button_color" format="color" />
+    <attr name="divider_color" format="color" />
+
+</resources>

+ 21 - 0
library/src/main/res/values/colors.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <color name="title_light">#000000</color>
+    <color name="title_dark">#FFFFFF</color>
+
+    <color name="content_light">#444444</color>
+    <color name="content_dark">#EDEDED</color>
+
+    <color name="item_light">#535353</color>
+    <color name="item_dark">#EDEDED</color>
+
+    <color name="button_light">#3C3C3D</color>
+    <color name="button_dark">#FFFFFF</color>
+
+    <color name="divider_black">#10000000</color>
+    <color name="divider_white">#10FFFFFF</color>
+
+    <color name="material_blue_500">#5677fc</color>
+
+</resources>

+ 20 - 0
library/src/main/res/values/dimens.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <dimen name="dialog_frame_margin">24dp</dimen>
+
+    <dimen name="button_height">36dp</dimen>
+    <dimen name="button_height_customview">56dp</dimen>
+
+    <dimen name="title_margin_customview">24dp</dimen>
+    <dimen name="button_frame_margin">16dp</dimen>
+    <dimen name="button_min_width">56dp</dimen>
+    <dimen name="button_padding">8dp</dimen>
+
+    <dimen name="title_textsize">20sp</dimen>
+    <dimen name="content_textsize">16sp</dimen>
+    <dimen name="button_textsize">14sp</dimen>
+    <dimen name="listitem_textsize">16sp</dimen>
+    <dimen name="listitem_height">48dp</dimen>
+
+</resources>

+ 8 - 0
library/src/main/res/values/strings.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <string name="decline">Decline</string>
+    <string name="accept">Accept</string>
+    <string name="more_info">More Info</string>
+
+</resources>

+ 41 - 0
library/src/main/res/values/styles.xml

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <style name="Light" parent="android:Theme.Light">
+        <item name="android:actionModeStyle">@style/ActionModeStyle</item>
+
+        <item name="title_color">@color/title_light</item>
+        <item name="content_color">@color/content_light</item>
+        <item name="item_color">@color/item_light</item>
+        <item name="button_color">@color/button_light</item>
+        <item name="divider_color">@color/divider_black</item>
+    </style>
+
+    <style name="Dark" parent="android:Theme.Black">
+        <item name="title_color">@color/title_dark</item>
+        <item name="content_color">@color/content_dark</item>
+        <item name="item_color">@color/item_dark</item>
+        <item name="button_color">@color/button_dark</item>
+        <item name="divider_color">@color/divider_white</item>
+    </style>
+
+    <style name="ActionButton" parent="@style/_ActionButtonParent" />
+
+    <style name="_ActionButtonParent">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">@dimen/button_height</item>
+        <item name="android:text">@string/accept</item>
+        <item name="android:minWidth">@dimen/button_min_width</item>
+        <item name="android:textSize">@dimen/button_textsize</item>
+        <item name="android:textAllCaps">true</item>
+        <item name="android:gravity">center</item>
+        <item name="android:padding">@dimen/button_padding</item>
+    </style>
+
+    <style name="ActionModeStyle" parent="Widget.AppCompat.ActionMode">
+        <item name="android:background">#000</item>
+        <item name="actionModeCloseDrawable">@drawable/ic_action_close</item>
+        <item name="android:actionModeCloseDrawable">@drawable/ic_action_close</item>
+    </style>
+
+</resources>

+ 1 - 0
sample/.gitignore

@@ -0,0 +1 @@
+/build

BIN
sample/art/example1.png


BIN
sample/art/example2.png


BIN
sample/art/example3.png


BIN
sample/art/example4.png


BIN
sample/art/example5.png


BIN
sample/art/example6.png


BIN
sample/art/example7.png


BIN
sample/art/example8.png


BIN
sample/art/example_0.png


+ 25 - 0
sample/build.gradle

@@ -0,0 +1,25 @@
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion "21.1.0"
+
+    defaultConfig {
+        applicationId "com.afollestad.materialdialogssample"
+        minSdkVersion 14
+        targetSdkVersion 21
+        versionCode 9
+        versionName "1.0"
+    }
+    buildTypes {
+        release {
+            runProguard false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    compile project(':library')
+}

BIN
sample/keystore.jks


+ 17 - 0
sample/proguard-rules.pro

@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/aidanfollestad/Documents/android-sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}

+ 88 - 0
sample/sample.iml

@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="MaterialDialogs" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="android-gradle" name="Android-Gradle">
+      <configuration>
+        <option name="GRADLE_PROJECT_PATH" value=":sample" />
+      </configuration>
+    </facet>
+    <facet type="android" name="Android">
+      <configuration>
+        <option name="SELECTED_BUILD_VARIANT" value="debug" />
+        <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
+        <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
+        <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugTest" />
+        <option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
+        <option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugTestSources" />
+        <option name="ALLOW_USER_CONFIGURATION" value="false" />
+        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
+        <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
+        <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
+        <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/test/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/test/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/test/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/test/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/test/debug" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
+      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
+      <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
+      <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
+    </content>
+    <orderEntry type="jdk" jdkName="Android API 21 Platform" jdkType="Android SDK" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" exported="" name="support-annotations-21.0.0" level="project" />
+    <orderEntry type="module" module-name="library" exported="" />
+  </component>
+</module>
+

+ 13 - 0
sample/src/androidTest/java/com/afollestad/materialdialogssample/ApplicationTest.java

@@ -0,0 +1,13 @@
+package com.afollestad.materialdialogssample;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+    public ApplicationTest() {
+        super(Application.class);
+    }
+}

+ 18 - 0
sample/src/main/AndroidManifest.xml

@@ -0,0 +1,18 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.afollestad.materialdialogssample">
+
+    <application android:allowBackup="true" android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher" android:theme="@style/AppTheme">
+
+        <activity
+            android:name=".MainActivity"
+            android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+    </application>
+
+</manifest>

+ 310 - 0
sample/src/main/java/com/afollestad/materialdialogssample/MainActivity.java

@@ -0,0 +1,310 @@
+package com.afollestad.materialdialogssample;
+
+import android.os.Bundle;
+import android.support.v7.app.ActionBarActivity;
+import android.text.Editable;
+import android.text.Html;
+import android.text.InputType;
+import android.text.TextWatcher;
+import android.text.method.PasswordTransformationMethod;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import com.afollestad.materialdialogs.Alignment;
+import com.afollestad.materialdialogs.DialogAction;
+import com.afollestad.materialdialogs.MaterialDialog;
+import com.afollestad.materialdialogs.Theme;
+
+
+public class MainActivity extends ActionBarActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        findViewById(R.id.basicNoTitle).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showBasicNoTitle();
+            }
+        });
+
+        findViewById(R.id.basic).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showBasic();
+            }
+        });
+
+        findViewById(R.id.stacked).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showStacked();
+            }
+        });
+
+        findViewById(R.id.neutral).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showNeutral();
+            }
+        });
+
+        findViewById(R.id.callbacks).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showCallbacks();
+            }
+        });
+
+        findViewById(R.id.list).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showList();
+            }
+        });
+
+        findViewById(R.id.singleChoice).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showSingleChoice();
+            }
+        });
+
+        findViewById(R.id.multiChoice).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showMultiChoice();
+            }
+        });
+
+        findViewById(R.id.customView).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showCustomView();
+            }
+        });
+
+        findViewById(R.id.themed).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showThemed();
+            }
+        });
+    }
+
+    private void showBasicNoTitle() {
+        new MaterialDialog.Builder(this)
+                .content(R.string.shareLocationPrompt)
+                .positiveText(R.string.accept)  // the default is 'Accept', this line could be left out
+                .negativeText(R.string.decline)  // leaving this line out will remove the negative button
+                .build()
+                .show();
+    }
+
+    private void showBasic() {
+        new MaterialDialog.Builder(this)
+                .title(R.string.permissions)
+                .content(R.string.permissionsContent)
+                .positiveText(R.string.accept)  // the default is 'Accept', this line could be left out
+                .negativeText(R.string.decline)  // leaving this line out will remove the negative button
+                .build()
+                .show();
+    }
+
+    private void showStacked() {
+        new MaterialDialog.Builder(this)
+                .title(R.string.permissions)
+                .content(R.string.permissionsContent)
+                .positiveText(R.string.speedBoost)
+                .negativeText(R.string.noThanks)
+                .build()
+                .show();
+    }
+
+    private void showNeutral() {
+        new MaterialDialog.Builder(this)
+                .title(R.string.permissions)
+                .content(R.string.permissionsContent)
+                .positiveText(R.string.accept)
+                .negativeText(R.string.decline)
+                .neutralText(R.string.more_info)
+                .build()
+                .show();
+    }
+
+    private void showCallbacks() {
+        new MaterialDialog.Builder(this)
+                .title(R.string.permissions)
+                .content(R.string.permissionsContent)
+                .positiveText(R.string.accept)
+                .negativeText(R.string.decline)
+                .neutralText(R.string.more_info)
+                .callback(new MaterialDialog.FullCallback() {
+                    @Override
+                    public void onPositive(MaterialDialog dialog) {
+                        Toast.makeText(getApplicationContext(), "Positive!", Toast.LENGTH_SHORT).show();
+                    }
+
+                    @Override
+                    public void onNeutral(MaterialDialog dialog) {
+                        Toast.makeText(getApplicationContext(), "Neutral", Toast.LENGTH_SHORT).show();
+                    }
+
+                    @Override
+                    public void onNegative(MaterialDialog dialog) {
+                        Toast.makeText(getApplicationContext(), "Negative…", Toast.LENGTH_SHORT).show();
+                    }
+                })
+                .build()
+                .show();
+    }
+
+    private void showList() {
+        new MaterialDialog.Builder(this)
+                .title(R.string.socialNetworks)
+                .items(R.array.socialNetworks)
+                .itemsCallback(new MaterialDialog.ListCallback() {
+                    @Override
+                    public void onSelection(MaterialDialog dialog, int which, String text) {
+                        Toast.makeText(getApplicationContext(), which + ": " + text, Toast.LENGTH_SHORT).show();
+                    }
+                })
+                .build()
+                .show();
+    }
+
+    private void showSingleChoice() {
+        new MaterialDialog.Builder(this)
+                .title(R.string.socialNetworks)
+                .items(R.array.socialNetworks)
+                .itemsCallbackSingleChoice(2, new MaterialDialog.ListCallback() {
+                    @Override
+                    public void onSelection(MaterialDialog dialog, int which, String text) {
+                        Toast.makeText(getApplicationContext(), which + ": " + text, Toast.LENGTH_SHORT).show();
+                    }
+                })
+                .positiveText(R.string.choose)
+                .build()
+                .show();
+    }
+
+    private void showMultiChoice() {
+        new MaterialDialog.Builder(this)
+                .title(R.string.socialNetworks)
+                .items(R.array.socialNetworks)
+                .itemsCallbackMultiChoice(new Integer[] { 1, 3 }, new MaterialDialog.ListCallbackMulti() {
+                    @Override
+                    public void onSelection(MaterialDialog dialog, Integer[] which, String[] text) {
+                        StringBuilder str = new StringBuilder();
+                        for (int i = 0; i < which.length; i++) {
+                            str.append(which[i]);
+                            str.append(": ");
+                            str.append(text[i]);
+                            str.append('\n');
+                        }
+                        Toast.makeText(getApplicationContext(), str.toString(), Toast.LENGTH_LONG).show();
+                    }
+                })
+                .positiveText(R.string.choose)
+                .build()
+                .show();
+    }
+
+    EditText passwordInput;
+    View positiveAction;
+
+    private void showCustomView() {
+        MaterialDialog dialog = new MaterialDialog.Builder(this)
+                .title(R.string.googleWifi)
+                .positiveText(R.string.accept)
+                .customView(R.layout.dialog_customview)
+                .positiveText(R.string.connect)
+                .negativeText(android.R.string.cancel)
+                .callback(new MaterialDialog.Callback() {
+                    @Override
+                    public void onPositive(MaterialDialog dialog) {
+                        Toast.makeText(getApplicationContext(), "Password: " + passwordInput.getText().toString(), Toast.LENGTH_SHORT).show();
+                    }
+
+                    @Override
+                    public void onNegative(MaterialDialog dialog) {
+                    }
+                }).build();
+
+        positiveAction = dialog.getActionButton(DialogAction.POSITIVE);
+        passwordInput = (EditText) dialog.getCustomView().findViewById(R.id.password);
+        passwordInput.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+            }
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {
+                positiveAction.setEnabled(s.toString().trim().length() > 0);
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+            }
+        });
+
+        // Toggling the show password CheckBox will mask or unmask the password input EditText
+        ((CheckBox) dialog.getCustomView().findViewById(R.id.showPassword)).setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                passwordInput.setInputType(!isChecked ? InputType.TYPE_TEXT_VARIATION_PASSWORD : InputType.TYPE_CLASS_TEXT);
+                passwordInput.setTransformationMethod(!isChecked ? PasswordTransformationMethod.getInstance() : null);
+            }
+        });
+
+        dialog.show();
+        positiveAction.setEnabled(false); // disabled by default
+    }
+
+    private void showThemed() {
+        new MaterialDialog.Builder(this)
+                .title(R.string.permissions)
+                .content(R.string.permissionsContent)
+                .positiveText(R.string.accept)
+                .negativeText(R.string.decline)
+                .positiveColorRes(R.color.material_red_400)
+                .titleAlignment(Alignment.CENTER)
+                .titleColorRes(R.color.material_red_400)
+                .theme(Theme.DARK)
+                .build()
+                .show();
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main, menu);
+        return super.onCreateOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == R.id.about) {
+            new MaterialDialog.Builder(this)
+                    .title(R.string.about)
+                    .positiveText(R.string.dismiss)
+                    .content(Html.fromHtml(getString(R.string.about_body)))
+                    .contentLineSpacing(1.6f)
+                    .callback(new MaterialDialog.SimpleCallback() {
+                        @Override
+                        public void onPositive(MaterialDialog dialog) {
+                        }
+                    })
+                    .build()
+                    .show();
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}

BIN
sample/src/main/res/drawable-hdpi/ic_launcher.png


BIN
sample/src/main/res/drawable-mdpi/ic_launcher.png


BIN
sample/src/main/res/drawable-xhdpi/ic_launcher.png


BIN
sample/src/main/res/drawable-xxhdpi/ic_launcher.png


BIN
sample/src/main/res/drawable-xxxhdpi/ic_launcher.png


+ 83 - 0
sample/src/main/res/layout/activity_main.xml

@@ -0,0 +1,83 @@
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="@dimen/activity_horizontal_margin"
+        tools:context=".MainActivity">
+
+        <Button
+            android:id="@+id/basicNoTitle"
+            android:layout_width="match_parent"
+            android:layout_height="56dp"
+            android:text="@string/basic_notitle" />
+
+        <Button
+            android:id="@+id/basic"
+            android:layout_width="match_parent"
+            android:layout_height="56dp"
+            android:text="@string/basic" />
+
+        <Button
+            android:id="@+id/stacked"
+            android:layout_width="match_parent"
+            android:layout_height="56dp"
+            android:text="@string/stacked"
+            android:layout_marginTop="2dp" />
+
+        <Button
+            android:id="@+id/neutral"
+            android:layout_width="match_parent"
+            android:layout_height="56dp"
+            android:text="@string/neutral"
+            android:layout_marginTop="2dp" />
+
+        <Button
+            android:id="@+id/callbacks"
+            android:layout_width="match_parent"
+            android:layout_height="56dp"
+            android:text="@string/callbacks"
+            android:layout_marginTop="2dp" />
+
+        <Button
+            android:id="@+id/list"
+            android:layout_width="match_parent"
+            android:layout_height="56dp"
+            android:text="@string/list"
+            android:layout_marginTop="2dp" />
+
+        <Button
+            android:id="@+id/singleChoice"
+            android:layout_width="match_parent"
+            android:layout_height="56dp"
+            android:text="@string/singleChoice"
+            android:layout_marginTop="2dp" />
+
+        <Button
+            android:id="@+id/multiChoice"
+            android:layout_width="match_parent"
+            android:layout_height="56dp"
+            android:text="@string/multiChoice"
+            android:layout_marginTop="2dp" />
+
+        <Button
+            android:id="@+id/customView"
+            android:layout_width="match_parent"
+            android:layout_height="56dp"
+            android:text="@string/customView"
+            android:layout_marginTop="2dp" />
+
+        <Button
+            android:id="@+id/themed"
+            android:layout_width="match_parent"
+            android:layout_height="56dp"
+            android:text="@string/themed"
+            android:layout_marginTop="2dp" />
+
+    </LinearLayout>
+
+</ScrollView>

+ 64 - 0
sample/src/main/res/layout/dialog_customview.xml

@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="@dimen/customTitle"
+        android:textColor="#80000000"
+        android:text="@string/signalStrength" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="@dimen/customEntry"
+        android:layout_marginTop="4dp"
+        android:textColor="#000000"
+        android:text="@string/excellent" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="@dimen/customTitle"
+        android:textColor="#80000000"
+        android:layout_marginTop="18dp"
+        android:text="@string/security" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="@dimen/customEntry"
+        android:layout_marginTop="4dp"
+        android:textColor="#000000"
+        android:text="@string/securityType" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="@dimen/customTitle"
+        android:textColor="#80000000"
+        android:layout_marginTop="18dp"
+        android:text="@string/password" />
+
+    <EditText
+        android:id="@+id/password"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="@dimen/customEntry"
+        android:layout_marginTop="4dp"
+        android:textColor="#000000"
+        android:inputType="textPassword" />
+
+    <CheckBox
+        android:id="@+id/showPassword"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="@dimen/customEntry"
+        android:textColor="#000000"
+        android:text="@string/showPassword"
+        android:layout_marginTop="18dp" />
+
+</LinearLayout>

+ 8 - 0
sample/src/main/res/menu/main.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item
+        android:id="@+id/about"
+        android:title="@string/about" />
+
+</menu>

+ 10 - 0
sample/src/main/res/values/colors.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <!-- http://www.google.com/design/spec/style/color.html#color-color-palette -->
+    <color name="material_indigo_500">#3F51B5</color>
+    <color name="material_indigo_600">#3949AB</color>
+    <color name="material_pink_500">#E91E63</color>
+    <color name="material_red_400">#EF5350</color>
+
+</resources>

+ 7 - 0
sample/src/main/res/values/dimens.xml

@@ -0,0 +1,7 @@
+<resources>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+    <dimen name="customTitle">14sp</dimen>
+    <dimen name="customEntry">18sp</dimen>
+</resources>

+ 49 - 0
sample/src/main/res/values/strings.xml

@@ -0,0 +1,49 @@
+<resources>
+
+    <string name="app_name">MaterialDialogs</string>
+
+    <string name="basic">Basic</string>
+    <string name="stacked">Stacked Buttons</string>
+    <string name="neutral">Neutral Button</string>
+    <string name="callbacks">Callbacks</string>
+    <string name="list">Basic List</string>
+    <string name="singleChoice">Single Choice</string>
+    <string name="multiChoice">Multi Choice</string>
+    <string name="customView">Custom View</string>
+    <string name="themed">Themed</string>
+    <string name="signalStrength">Signal strength</string>
+    <string name="excellent">Excellent</string>
+    <string name="security">Security</string>
+    <string name="securityType">802.1x EAP</string>
+    <string name="password">Password</string>
+    <string name="showPassword">Show password</string>
+    <string name="permissions">Permissions</string>
+    <string name="permissionsContent">This app determines your phone\'s location and shares it with Google in order to serve personalized alerts to you. This allows for a better overall app experience.</string>
+    <string name="socialNetworks">Social Networks</string>
+    <string name="choose">Choose</string>
+    <string name="speedBoost">Turn on speed boost</string>
+    <string name="noThanks">No thanks</string>
+    <string name="googleWifi">Google Wifi</string>
+    <string name="connect">Connect</string>
+
+    <string name="about">About</string>
+    <string name="about_body"><![CDATA[
+        material-dialogs, a library  designed by <b>Aidan Follestad</b>.<br/>
+        <a href=\'http://aidanfollestad.com\'>Website</a>&nbsp;&nbsp;
+        <a href=\'https://twitter.com/afollestad\'>Twitter</a>&nbsp;&nbsp;
+        <a href=\'https://google.com/+AidanFollestad\'>Google+</a>&nbsp;&nbsp;
+        <a href=\'https://github.com/afollestad\'>GitHub</a>&nbsp;&nbsp;
+        <a href=\'https://www.linkedin.com/in/afollestad\'>LinkedIn</a>
+    ]]></string>
+    <string name="dismiss">Dismiss</string>
+    <string name="basic_notitle">Basic (No Title)</string>
+    <string name="shareLocationPrompt">This app wants to access your location.</string>
+
+    <string-array name="socialNetworks">
+        <item>Twitter</item>
+        <item>Google+</item>
+        <item>Instagram</item>
+        <item>Facebook</item>
+    </string-array>
+
+</resources>

+ 10 - 0
sample/src/main/res/values/styles.xml

@@ -0,0 +1,10 @@
+<resources>
+
+    <!-- http://www.google.com/design/spec/style/color.html#color-color-palette -->
+    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+        <item name="colorPrimary">@color/material_indigo_500</item>
+        <item name="colorPrimaryDark">@color/material_indigo_600</item>
+        <item name="colorAccent">@color/material_pink_500</item>
+    </style>
+
+</resources>

+ 1 - 0
settings.gradle

@@ -0,0 +1 @@
+include ':library', ':sample'