├── .gitignore ├── CHANGELOG.md ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── codinguser │ │ └── android │ │ └── contactpicker │ │ ├── ContactDetailsFragment.java │ │ ├── ContactsListFragment.java │ │ ├── ContactsPickerActivity.java │ │ └── OnContactSelectedListener.java │ └── res │ ├── drawable-hdpi │ ├── ic_action_search.png │ ├── ic_launcher.png │ └── ic_person_add_white_48dp.png │ ├── drawable-ldpi │ └── ic_launcher.png │ ├── drawable-mdpi │ ├── ic_action_search.png │ ├── ic_launcher.png │ └── ic_person_add_white_48dp.png │ ├── drawable-xhdpi │ ├── ic_action_search.png │ └── ic_person_add_white_48dp.png │ ├── drawable-xxhdpi │ ├── ic_action_search.png │ └── ic_person_add_white_48dp.png │ ├── layout │ ├── activity_contacts.xml │ ├── fragment_contact_detail.xml │ ├── fragment_contacts_list.xml │ ├── list_item_contacts.xml │ ├── list_item_phone_number.xml │ └── main.xml │ ├── menu │ └── main.xml │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── proguard.cfg ├── sample ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── codinguser │ │ └── android │ │ └── contactpicker │ │ └── sample │ │ └── ContactPickerSampleActivity.java │ └── res │ └── values │ └── styles.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .settings/ 3 | bin/ 4 | target 5 | gen/ 6 | 7 | #IntelliJ IDEA 8 | .idea/ 9 | *.iml 10 | gen-external-apklibs 11 | 12 | #Gradle 13 | build 14 | .gradle 15 | local.properties 16 | gradle.properties 17 | gradle/local.properties 18 | .settings 19 | .metadata 20 | 21 | # cpu profile generated by Android Studio 22 | /captures -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | =============================================================================== 3 | 4 | Version 3.0.0 *(2015-xx-xx)* 5 | ---------------------------- 6 | * Updated to Material Design 7 | * Added option to create new contact 8 | * Added contact search menu option 9 | * Added sample application for testing 10 | * Raised minimum supported API level to 8 11 | * Migrated build system to Gradle (from maven) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Why? 2 | 3 | Often in Android development, there is need to support the following workflow: 4 | 5 | 1. Display list of contacts 6 | 2. User selects a contact 7 | 3. Display contact details 8 | 4. User selects a phone number 9 | 5. Perform an action with the number 10 | 11 | There is a standard way to call the contact list in Android, but this does not 12 | always feel well-integrated in your app and does not work well expecially for tabbed 13 | Android applications. 14 | 15 | ContactPicker is an Android library which allows you to easily integrate the above 16 | workflow in your application with minimal effort. It uses the new Fragments API 17 | and asynchronous contact loading introduced in the newer versions of Android. 18 | 19 | *This library requires a minimum of Android API level 8 and above since it uses the new contact APIs introduced in Android 2.0* 20 | 21 | 22 | # Installation 23 | 24 | In order to use the library, add the following line to your Gradle dependencies: 25 |
 
26 | compile 'com.codinguser.android:contactpicker:3.0.0@aar'
27 |  
28 | # Usage 29 | 30 | There are two ways to use the library. 31 | 32 | 1. If you just want to open a contacts picker from within your app and then get a number, 33 | do this: 34 | 35 | ```java 36 | private static final int GET_PHONE_NUMBER = 3007; 37 | 38 | public void getContact() { 39 | startActivityForResult(new Intent(this, ContactsPickerActivity.class), GET_PHONE_NUMBER); 40 | } 41 | 42 | // Listen for results. 43 | @Override 44 | protected void onActivityResult(int requestCode, int resultCode, Intent data){ 45 | // See which child activity is calling us back. 46 | switch (requestCode) { 47 | case GET_PHONE_NUMBER: 48 | // This is the standard resultCode that is sent back if the 49 | // activity crashed or didn't doesn't supply an explicit result. 50 | if (resultCode == RESULT_CANCELED){ 51 | Toast.makeText(this, "No phone number found", Toast.LENGTH_SHORT).show(); 52 | } 53 | else { 54 | String phoneNumber = (String) data.getExtras().get(ContactsPickerActivity.KEY_PHONE_NUMBER); 55 | //Do what you wish to do with phoneNumber e.g. 56 | //Toast.makeText(this, "Phone number found: " + phoneNumber , Toast.LENGTH_SHORT).show(); 57 | } 58 | default: 59 | break; 60 | } 61 | } 62 | 63 | ``` 64 | 65 | 2. If you have a tabbed application with multiple activities in Android and you 66 | wish to make a contacts list as one of the tabs, then your contacts activity should 67 | subclass the `ContactsPickerActivity` and override the `OnContactNumberSelected` method 68 | in order to get notified when a number is selected. The default implementation of 69 | `OnContactNumberSelected` just sets the number as a result and finishes the activity, 70 | so make sure to override it to do what you want. 71 | 72 | # License 73 | The code is provided under an MIT license, so get hacking!! 74 | 75 | # Acknowledgements 76 | Thanks to Avi Hayun for feedback used to improve the library 77 | 78 | # Contact 79 | For any inquiries, you can reach me at ngewif@codinguser.com 80 | 81 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | def versionMajor = 3 4 | def versionMinor = 0 5 | def versionPatch = 0 6 | def versionBuild = 0 7 | 8 | ext{ 9 | minSdkVersion = 8 10 | targetSdkVersion = 22 11 | compileSdkVersion = 22 12 | buildToolsVersion = "22.0.1" 13 | versionCode = versionMajor * 10000 + versionMinor * 1000 + versionPatch * 100 + versionBuild 14 | versionName = "${versionMajor}.${versionMinor}.${versionPatch}" 15 | } 16 | 17 | buildscript { 18 | repositories { 19 | jcenter() 20 | mavenLocal() 21 | mavenCentral() 22 | } 23 | 24 | dependencies { 25 | classpath 'com.android.tools.build:gradle:1.3.0' 26 | 27 | // NOTE: Do not place your application dependencies here; they belong 28 | // in the individual module build.gradle files 29 | } 30 | } 31 | 32 | allprojects { 33 | repositories { 34 | jcenter() 35 | mavenCentral() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinguser/android_contact_picker/75f2c16053f28b01ebb1bd421eea88527ef85715/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jul 09 11:57:23 CEST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'maven' 3 | apply plugin: 'signing' 4 | 5 | android { 6 | compileSdkVersion rootProject.compileSdkVersion 7 | buildToolsVersion rootProject.buildToolsVersion 8 | 9 | defaultConfig { 10 | minSdkVersion rootProject.minSdkVersion 11 | targetSdkVersion rootProject.targetSdkVersion 12 | versionCode = rootProject.versionCode// * 10000 + project.versionMinor * 1000 + project.versionPatch * 100 + project.versionBuild 13 | versionName = rootProject.versionName //"${parent.versionMajor}.${parent.versionMinor}.${parent.versionPatch}" 14 | } 15 | 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | 23 | lintOptions { 24 | abortOnError false 25 | } 26 | 27 | libraryVariants.all { variant -> 28 | variant.outputs.each {output -> 29 | output.outputFile = new File(output.outputFile.parent, "contact_picker_library_v${defaultConfig.versionName}.aar") 30 | } 31 | } 32 | 33 | compileOptions { //we want switch with strings during xml parsing 34 | encoding "UTF-8" 35 | sourceCompatibility JavaVersion.VERSION_1_7 36 | targetCompatibility JavaVersion.VERSION_1_7 37 | } 38 | } 39 | 40 | dependencies { 41 | compile fileTree(dir: 'libs', include: ['*.jar']) 42 | compile 'com.android.support:support-v4:22.2.0' 43 | compile 'com.android.support:appcompat-v7:22.2.0' 44 | } 45 | 46 | group = "com.codinguser.android" 47 | version = rootProject.versionName 48 | 49 | // 50 | //task javadocJar(type: Jar, dependsOn: javadoc){ 51 | // classifier = 'javadoc' 52 | // from javadoc.destinationDir 53 | //} 54 | // 55 | //task sourcesJar(type: Jar){ 56 | // classifier = 'sources' 57 | // from sourceSets.main.allSource 58 | //} 59 | // 60 | //artifacts{ 61 | // archives javadocJar, sourcesJar 62 | //} 63 | 64 | configurations { 65 | archives { 66 | extendsFrom configurations.default 67 | } 68 | } 69 | 70 | signing{ 71 | required { has("release") && gradle.taskGraph.hasTask("uploadArchives") } 72 | sign configurations.archives 73 | } 74 | 75 | uploadArchives{ 76 | repositories{ 77 | mavenDeployer { 78 | beforeDeployment { MavenDeployment deployment -> 79 | signing.signPom(deployment) 80 | } 81 | 82 | repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { 83 | authentication(userName: ossrhUsername, password: ossrhPassword) 84 | } 85 | 86 | snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { 87 | authentication(userName: ossrhUsername, password: ossrhPassword) 88 | } 89 | 90 | pom.project { 91 | name 'Android Contact Picker Library' 92 | packaging 'aar' 93 | artifactId 'contactpicker'// optionally artifactId can be defined here 94 | description 'Android library for picking a contact and retrieving a phone number' 95 | url 'https://github.com/codinguser/android_contact_picker' 96 | 97 | scm { 98 | connection 'scm:git:git://github.com/codinguser/android_contact_picker.git' 99 | developerConnection 'scm:git:git@github.com:codinguser/android_contact_picker.git' 100 | url 'https://github.com/codinguser/android_contact_picker' 101 | } 102 | 103 | licenses { 104 | license { 105 | name 'MIT License' 106 | url 'http://opensource.org/licenses/MIT' 107 | } 108 | } 109 | 110 | developers { 111 | developer { 112 | id 'codinguser' 113 | name 'Ngewi Fet' 114 | email 'ngewif@gmail.com' 115 | url 'http://www.codinguser.com' 116 | } 117 | developer { 118 | id 'chaiavi' 119 | name 'Avi Hayun' 120 | email 'avraham2@gmail.com' 121 | url 'http://chaiware.org' 122 | } 123 | } 124 | } 125 | } 126 | } 127 | } 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /library/src/main/java/com/codinguser/android/contactpicker/ContactDetailsFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 - 2015 by Ngewi Fet 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.codinguser.android.contactpicker; 24 | 25 | import android.app.Activity; 26 | import android.content.Context; 27 | import android.database.Cursor; 28 | import android.net.Uri; 29 | import android.os.Bundle; 30 | import android.provider.ContactsContract; 31 | import android.provider.ContactsContract.CommonDataKinds.Phone; 32 | import android.support.v4.app.ListFragment; 33 | import android.support.v4.widget.SimpleCursorAdapter; 34 | import android.view.LayoutInflater; 35 | import android.view.View; 36 | import android.view.ViewGroup; 37 | import android.widget.ListAdapter; 38 | import android.widget.ListView; 39 | import android.widget.TextView; 40 | 41 | public class ContactDetailsFragment extends ListFragment { 42 | private TextView mDisplayName; 43 | private OnContactSelectedListener mContactsListener; 44 | private Cursor mCursor; 45 | 46 | @Override 47 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 48 | return inflater.inflate(R.layout.fragment_contact_detail, container, false); 49 | } 50 | 51 | @Override 52 | public void onActivityCreated(Bundle savedInstanceState) { 53 | super.onActivityCreated(savedInstanceState); 54 | 55 | long personId = getArguments().getLong(ContactsPickerActivity.SELECTED_CONTACT_ID);// getIntent().getLongExtra("id", 0); 56 | Activity activity = getActivity(); 57 | 58 | Uri phonesUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI; 59 | String[] projection = new String[] { 60 | Phone._ID, Phone.DISPLAY_NAME, 61 | Phone.TYPE, Phone.NUMBER, Phone.LABEL }; 62 | String selection = ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?"; 63 | String[] selectionArgs = new String[] { Long.toString(personId) }; 64 | 65 | mCursor = activity.getContentResolver().query(phonesUri, 66 | projection, selection, selectionArgs, null); 67 | 68 | mDisplayName = (TextView) activity.findViewById(R.id.display_name); 69 | if (mCursor.moveToFirst()){ 70 | mDisplayName.setText(mCursor.getString(mCursor.getColumnIndex(Phone.DISPLAY_NAME))); 71 | } 72 | 73 | ListAdapter adapter = new PhoneNumbersAdapter(this.getActivity(), 74 | R.layout.list_item_phone_number, mCursor, 75 | new String[] {Phone.TYPE, Phone.NUMBER }, 76 | new int[] { R.id.label, R.id.phone_number }); 77 | setListAdapter(adapter); 78 | } 79 | 80 | @Override 81 | public void onAttach(Activity activity) { 82 | super.onAttach(activity); 83 | 84 | try { 85 | mContactsListener = (OnContactSelectedListener) activity; 86 | } catch (ClassCastException e) { 87 | throw new ClassCastException(activity.toString() + " must implement OnContactSelectedListener"); 88 | } 89 | } 90 | 91 | @Override 92 | public void onListItemClick(ListView l, View v, int position, long id) { 93 | super.onListItemClick(l, v, position, id); 94 | 95 | TextView tv = (TextView) v.findViewById(R.id.phone_number); 96 | String number = tv.getText().toString(); 97 | String name = mDisplayName.getText().toString(); 98 | 99 | mContactsListener.onContactNumberSelected(number, name); 100 | } 101 | 102 | class PhoneNumbersAdapter extends SimpleCursorAdapter { 103 | 104 | public PhoneNumbersAdapter(Context context, int layout, Cursor c, 105 | String[] from, int[] to) { 106 | super(context, layout, c, from, to, 0); 107 | } 108 | 109 | @Override 110 | public void bindView(View view, Context context, Cursor cursor) { 111 | super.bindView(view, context, cursor); 112 | 113 | TextView tx = (TextView) view.findViewById(R.id.label); 114 | int type = cursor.getInt(cursor.getColumnIndex(Phone.TYPE)); 115 | String label = cursor.getString(cursor.getColumnIndex(Phone.LABEL)); 116 | label = Phone.getTypeLabel(getResources(), type, label).toString(); 117 | 118 | tx.setText(label); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /library/src/main/java/com/codinguser/android/contactpicker/ContactsListFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 - 2015 by Ngewi Fet 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.codinguser.android.contactpicker; 24 | 25 | import android.annotation.SuppressLint; 26 | import android.app.Activity; 27 | import android.content.Context; 28 | import android.content.Intent; 29 | import android.database.Cursor; 30 | import android.net.Uri; 31 | import android.os.AsyncTask; 32 | import android.os.Build; 33 | import android.os.Bundle; 34 | import android.provider.ContactsContract; 35 | import android.provider.ContactsContract.CommonDataKinds.Phone; 36 | import android.provider.ContactsContract.Contacts; 37 | import android.provider.ContactsContract.Data; 38 | import android.support.v4.app.ListFragment; 39 | import android.support.v4.app.LoaderManager; 40 | import android.support.v4.content.CursorLoader; 41 | import android.support.v4.content.Loader; 42 | import android.support.v4.view.MenuItemCompat; 43 | import android.support.v4.widget.SimpleCursorAdapter; 44 | import android.support.v7.widget.SearchView; 45 | import android.text.TextUtils; 46 | import android.view.LayoutInflater; 47 | import android.view.Menu; 48 | import android.view.MenuInflater; 49 | import android.view.MenuItem; 50 | import android.view.View; 51 | import android.view.ViewGroup; 52 | import android.widget.AlphabetIndexer; 53 | import android.widget.ListView; 54 | import android.widget.SectionIndexer; 55 | import android.widget.TextView; 56 | 57 | import java.lang.ref.WeakReference; 58 | 59 | public class ContactsListFragment extends ListFragment implements 60 | LoaderManager.LoaderCallbacks, SearchView.OnQueryTextListener, SearchView.OnCloseListener { 61 | 62 | private OnContactSelectedListener mContactsListener; 63 | private SimpleCursorAdapter mAdapter; 64 | private String mSearchString = null; 65 | private SearchView mSearchView; 66 | 67 | @SuppressLint("InlinedApi") 68 | private static String DISPLAY_NAME_COMPAT = Build.VERSION.SDK_INT 69 | >= Build.VERSION_CODES.HONEYCOMB ? 70 | Contacts.DISPLAY_NAME_PRIMARY : 71 | Contacts.DISPLAY_NAME; 72 | 73 | 74 | private static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { 75 | Contacts._ID, 76 | DISPLAY_NAME_COMPAT, 77 | Contacts.HAS_PHONE_NUMBER, 78 | Contacts.LOOKUP_KEY 79 | }; 80 | 81 | @Override 82 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 83 | return inflater.inflate(R.layout.fragment_contacts_list, container, false); 84 | } 85 | 86 | @Override 87 | public void onActivityCreated(Bundle savedInstanceState) { 88 | super.onActivityCreated(savedInstanceState); 89 | 90 | setHasOptionsMenu(true); 91 | 92 | getLoaderManager().initLoader(0, null, this); 93 | 94 | mAdapter = new IndexedListAdapter( 95 | this.getActivity(), 96 | R.layout.list_item_contacts, 97 | null, 98 | new String[] {ContactsContract.Contacts.DISPLAY_NAME}, 99 | new int[] {R.id.display_name}); 100 | 101 | setListAdapter(mAdapter); 102 | getListView().setFastScrollEnabled(true); 103 | 104 | } 105 | 106 | @Override 107 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 108 | inflater.inflate(R.menu.main, menu); 109 | mSearchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.action_search)); 110 | mSearchView.setOnQueryTextListener(this); 111 | mSearchView.setOnCloseListener(this); 112 | } 113 | 114 | @Override 115 | public boolean onOptionsItemSelected(MenuItem item) { 116 | int actionId = item.getItemId(); 117 | if (actionId == R.id.action_add_contact){ 118 | Intent intent = new Intent(Intent.ACTION_INSERT, 119 | ContactsContract.Contacts.CONTENT_URI); 120 | startActivity(intent); 121 | return true; 122 | } 123 | 124 | if (actionId == android.R.id.home){ 125 | getActivity().finish(); 126 | } 127 | 128 | return super.onOptionsItemSelected(item); 129 | } 130 | 131 | @Override 132 | public void onListItemClick(ListView l, View v, int position, long id) { 133 | ViewHolder viewHolder = (ViewHolder) v.getTag(); 134 | String phoneNumber = viewHolder.phoneNumber.getText().toString(); 135 | String name = viewHolder.contactName.getText().toString(); 136 | 137 | if (phoneNumber.equals(getString(R.string.label_multiple_numbers))){ 138 | mContactsListener.onContactNameSelected(id); 139 | } else { 140 | mContactsListener.onContactNumberSelected(phoneNumber, name); 141 | } 142 | } 143 | 144 | @Override 145 | public void onAttach(Activity activity) { 146 | super.onAttach(activity); 147 | 148 | try { 149 | mContactsListener = (OnContactSelectedListener) activity; 150 | } catch (ClassCastException e) { 151 | throw new ClassCastException(activity.toString() + " must implement OnContactSelectedListener"); 152 | } 153 | } 154 | 155 | @Override 156 | public Loader onCreateLoader(int arg0, Bundle arg1) { 157 | Uri baseUri; 158 | 159 | if (mSearchString != null) { 160 | baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, 161 | Uri.encode(mSearchString)); 162 | } else { 163 | baseUri = Contacts.CONTENT_URI; 164 | } 165 | 166 | String selection = "((" + DISPLAY_NAME_COMPAT + " NOTNULL) AND (" 167 | + Contacts.HAS_PHONE_NUMBER + "=1) AND (" 168 | + DISPLAY_NAME_COMPAT + " != '' ))"; 169 | 170 | String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; 171 | 172 | return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, selection, null, sortOrder); 173 | } 174 | 175 | @Override 176 | public void onLoadFinished(Loader loader, Cursor data) { 177 | mAdapter.swapCursor(data); 178 | } 179 | 180 | @Override 181 | public void onLoaderReset(Loader loader) { 182 | mAdapter.swapCursor(null); 183 | } 184 | 185 | @Override 186 | public boolean onClose() { 187 | if (!TextUtils.isEmpty(mSearchView.getQuery())) { 188 | mSearchView.setQuery(null, true); 189 | } 190 | return true; 191 | } 192 | 193 | @Override 194 | public boolean onQueryTextSubmit(String s) { 195 | return true; 196 | } 197 | 198 | @Override 199 | public boolean onQueryTextChange(String newText) { 200 | String newFilter = !TextUtils.isEmpty(newText) ? newText : null; 201 | 202 | if (mSearchString == null && newFilter == null) { 203 | return true; 204 | } 205 | if (mSearchString != null && mSearchString.equals(newFilter)) { 206 | return true; 207 | } 208 | mSearchString = newFilter; 209 | getLoaderManager().restartLoader(0, null, this); 210 | return true; 211 | } 212 | 213 | static class ViewHolder{ 214 | TextView contactName; 215 | TextView phoneLabel; 216 | TextView phoneNumber; 217 | View separator; 218 | PhoneNumberLookupTask phoneNumberLookupTask; 219 | } 220 | 221 | class IndexedListAdapter extends SimpleCursorAdapter implements SectionIndexer{ 222 | 223 | AlphabetIndexer alphaIndexer; 224 | 225 | public IndexedListAdapter(Context context, int layout, Cursor c, 226 | String[] from, int[] to) { 227 | super(context, layout, c, from, to, 0); 228 | } 229 | 230 | @Override 231 | public Cursor swapCursor(Cursor c) { 232 | if (c != null) { 233 | alphaIndexer = new AlphabetIndexer(c, 234 | c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME), 235 | " ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 236 | } 237 | 238 | return super.swapCursor(c); 239 | } 240 | 241 | @Override 242 | public View getView(int position, View convertView, ViewGroup parent) { 243 | ViewHolder viewHolder; 244 | if (convertView == null){ 245 | LayoutInflater inflater = getLayoutInflater(null); 246 | convertView = inflater.inflate(R.layout.list_item_contacts, parent, false); 247 | viewHolder = new ViewHolder(); 248 | viewHolder.contactName = (TextView) convertView.findViewById(R.id.display_name); 249 | viewHolder.phoneLabel = (TextView) convertView.findViewById(R.id.phone_label); 250 | viewHolder.phoneNumber = (TextView) convertView.findViewById(R.id.phone_number); 251 | viewHolder.separator = convertView.findViewById(R.id.label_separator); 252 | convertView.setTag(viewHolder); 253 | } else { 254 | viewHolder = (ViewHolder) convertView.getTag(); 255 | viewHolder.phoneNumberLookupTask.cancel(true); 256 | } 257 | 258 | return super.getView(position, convertView, parent); 259 | } 260 | 261 | @Override 262 | public void bindView(View view, Context context, Cursor cursor) { 263 | super.bindView(view, context, cursor); 264 | 265 | long contactId = cursor.getLong(cursor.getColumnIndexOrThrow(Contacts._ID)); 266 | ViewHolder viewHolder = (ViewHolder) view.getTag(); 267 | viewHolder.phoneNumberLookupTask = new PhoneNumberLookupTask(view); 268 | viewHolder.phoneNumberLookupTask.execute(contactId); 269 | } 270 | 271 | @Override 272 | public int getPositionForSection(int section) { 273 | return alphaIndexer.getPositionForSection(section); 274 | } 275 | 276 | @Override 277 | public int getSectionForPosition(int position) { 278 | return alphaIndexer.getSectionForPosition(position); 279 | } 280 | 281 | @Override 282 | public Object[] getSections() { 283 | return alphaIndexer == null ? null : alphaIndexer.getSections(); 284 | } 285 | } 286 | 287 | /** 288 | * Task for looking up the phone number and displaying it next to the contact. 289 | * This task holds a weak reference to the view so that if it is recycled while task is running, 290 | * then the task does nothing. 291 | */ 292 | private class PhoneNumberLookupTask extends AsyncTask { 293 | final WeakReference mViewReference; 294 | 295 | String mPhoneNumber; 296 | String mPhoneLabel; 297 | 298 | public PhoneNumberLookupTask(View view){ 299 | mViewReference = new WeakReference<>(view); 300 | } 301 | 302 | @Override 303 | protected Void doInBackground(Long... ids) { 304 | String[] projection = new String[] {Phone.DISPLAY_NAME, Phone.TYPE, Phone.NUMBER, Phone.LABEL}; 305 | long contactId = ids[0]; 306 | 307 | final Cursor phoneCursor = getActivity().getContentResolver().query( 308 | Phone.CONTENT_URI, 309 | projection, 310 | Data.CONTACT_ID + "=?", 311 | new String[]{String.valueOf(contactId)}, 312 | null); 313 | 314 | if(phoneCursor != null && phoneCursor.moveToFirst() && phoneCursor.getCount() == 1) { 315 | final int contactNumberColumnIndex = phoneCursor.getColumnIndex(Phone.NUMBER); 316 | mPhoneNumber = phoneCursor.getString(contactNumberColumnIndex); 317 | int type = phoneCursor.getInt(phoneCursor.getColumnIndexOrThrow(Phone.TYPE)); 318 | mPhoneLabel = phoneCursor.getString(phoneCursor.getColumnIndex(Phone.LABEL)); 319 | mPhoneLabel = Phone.getTypeLabel(getResources(), type, mPhoneLabel).toString(); 320 | phoneCursor.close(); 321 | } 322 | 323 | return null; 324 | } 325 | 326 | @Override 327 | protected void onPostExecute(Void param) { 328 | View view = mViewReference.get(); 329 | if (view != null ){ 330 | ViewHolder viewHolder = (ViewHolder) view.getTag(); 331 | if (mPhoneNumber != null){ 332 | viewHolder.phoneNumber.setText(mPhoneNumber); 333 | viewHolder.phoneLabel.setText(mPhoneLabel); 334 | viewHolder.phoneLabel.setVisibility(View.VISIBLE); 335 | viewHolder.separator.setVisibility(View.VISIBLE); 336 | } 337 | else { 338 | viewHolder.phoneNumber.setText(getString(R.string.label_multiple_numbers)); 339 | viewHolder.phoneLabel.setVisibility(View.GONE); 340 | viewHolder.separator.setVisibility(View.GONE); 341 | } 342 | 343 | } 344 | } 345 | } 346 | 347 | 348 | } 349 | -------------------------------------------------------------------------------- /library/src/main/java/com/codinguser/android/contactpicker/ContactsPickerActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 - 2015 by Ngewi Fet 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.codinguser.android.contactpicker; 24 | 25 | import android.content.Intent; 26 | import android.os.Bundle; 27 | import android.support.v4.app.Fragment; 28 | import android.support.v4.app.FragmentManager; 29 | import android.support.v4.app.FragmentTransaction; 30 | import android.support.v7.app.ActionBar; 31 | import android.support.v7.app.AppCompatActivity; 32 | 33 | public class ContactsPickerActivity extends AppCompatActivity implements OnContactSelectedListener { 34 | public static final String SELECTED_CONTACT_ID = "contact_id"; 35 | public static final String KEY_PHONE_NUMBER = "phone_number"; 36 | public static final String KEY_CONTACT_NAME = "contact_name"; 37 | 38 | /** 39 | * Starting point 40 | * Loads the {@link ContactsListFragment} 41 | */ 42 | @Override 43 | protected void onCreate(Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | setContentView(R.layout.activity_contacts); 46 | 47 | FragmentManager fragmentManager = this.getSupportFragmentManager(); 48 | FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); 49 | ContactsListFragment fragment = new ContactsListFragment(); 50 | 51 | fragmentTransaction.replace(R.id.fragment_container, fragment); 52 | fragmentTransaction.commit(); 53 | 54 | ActionBar actionBar = getSupportActionBar(); 55 | if (actionBar != null) { 56 | actionBar.setDisplayHomeAsUpEnabled(true); 57 | actionBar.setTitle("Select contact"); 58 | } 59 | } 60 | 61 | /** 62 | * Callback when the contact is selected from the list of contacts. 63 | * Loads the {@link ContactDetailsFragment} 64 | */ 65 | @Override 66 | public void onContactNameSelected(long contactId) { 67 | /* Now that we know which Contact was selected we can go to the details fragment */ 68 | 69 | Fragment detailsFragment = new ContactDetailsFragment(); 70 | Bundle args = new Bundle(); 71 | args.putLong(ContactsPickerActivity.SELECTED_CONTACT_ID, contactId); 72 | detailsFragment.setArguments(args); 73 | FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 74 | 75 | transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); 76 | // Replace whatever is in the fragment_container view with this fragment, 77 | // and add the transaction to the back stack 78 | transaction.replace(R.id.fragment_container, detailsFragment); 79 | 80 | transaction.addToBackStack(null); 81 | // Commit the transaction 82 | transaction.commit(); 83 | } 84 | 85 | /** 86 | * Callback when the contact number is selected from the contact details view 87 | * Sets the activity result with the contact information and finishes 88 | */ 89 | @Override 90 | public void onContactNumberSelected(String contactNumber, String contactName) { 91 | Intent intent = new Intent(); 92 | intent.putExtra(KEY_PHONE_NUMBER, contactNumber); 93 | intent.putExtra(KEY_CONTACT_NAME, contactName); 94 | 95 | setResult(RESULT_OK, intent); 96 | finish(); 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /library/src/main/java/com/codinguser/android/contactpicker/OnContactSelectedListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 - 2015 by Ngewi Fet 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.codinguser.android.contactpicker; 24 | 25 | public interface OnContactSelectedListener { 26 | 27 | /** 28 | * Callback when the contact is selected from the list of contacts 29 | * @param contactId Long ID of the contact which was selected. 30 | */ 31 | void onContactNameSelected(long contactId); 32 | 33 | /** 34 | * Callback when the contact number is selected from the contact details view 35 | * @param contactNumber String with the number which was selected 36 | * @param contactName Name of the contact which was selected as String 37 | */ 38 | void onContactNumberSelected(String contactNumber, String contactName); 39 | } 40 | -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinguser/android_contact_picker/75f2c16053f28b01ebb1bd421eea88527ef85715/library/src/main/res/drawable-hdpi/ic_action_search.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinguser/android_contact_picker/75f2c16053f28b01ebb1bd421eea88527ef85715/library/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/ic_person_add_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinguser/android_contact_picker/75f2c16053f28b01ebb1bd421eea88527ef85715/library/src/main/res/drawable-hdpi/ic_person_add_white_48dp.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinguser/android_contact_picker/75f2c16053f28b01ebb1bd421eea88527ef85715/library/src/main/res/drawable-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinguser/android_contact_picker/75f2c16053f28b01ebb1bd421eea88527ef85715/library/src/main/res/drawable-mdpi/ic_action_search.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinguser/android_contact_picker/75f2c16053f28b01ebb1bd421eea88527ef85715/library/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/ic_person_add_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinguser/android_contact_picker/75f2c16053f28b01ebb1bd421eea88527ef85715/library/src/main/res/drawable-mdpi/ic_person_add_white_48dp.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinguser/android_contact_picker/75f2c16053f28b01ebb1bd421eea88527ef85715/library/src/main/res/drawable-xhdpi/ic_action_search.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/ic_person_add_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinguser/android_contact_picker/75f2c16053f28b01ebb1bd421eea88527ef85715/library/src/main/res/drawable-xhdpi/ic_person_add_white_48dp.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinguser/android_contact_picker/75f2c16053f28b01ebb1bd421eea88527ef85715/library/src/main/res/drawable-xxhdpi/ic_action_search.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/ic_person_add_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codinguser/android_contact_picker/75f2c16053f28b01ebb1bd421eea88527ef85715/library/src/main/res/drawable-xxhdpi/ic_person_add_white_48dp.png -------------------------------------------------------------------------------- /library/src/main/res/layout/activity_contacts.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 31 | -------------------------------------------------------------------------------- /library/src/main/res/layout/fragment_contact_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 31 | 32 | 43 | 44 | 49 | 50 | -------------------------------------------------------------------------------- /library/src/main/res/layout/fragment_contacts_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 30 | 35 | 36 | 43 | 44 | -------------------------------------------------------------------------------- /library/src/main/res/layout/list_item_contacts.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 34 | 35 | 46 | 47 | 50 | 56 | 57 | 63 | 71 | 72 | -------------------------------------------------------------------------------- /library/src/main/res/layout/list_item_phone_number.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 32 | 33 | 41 | 42 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /library/src/main/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 30 | 31 | 34 | 35 | -------------------------------------------------------------------------------- /library/src/main/res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 11 | 12 | 18 | -------------------------------------------------------------------------------- /library/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3f51b5 4 | #303f9f 5 | -------------------------------------------------------------------------------- /library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 26 | 27 | 28 | Contact Picker Sample 29 | Search 30 | Multiple numbers 31 | 32 | -------------------------------------------------------------------------------- /library/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 27 | 34 | -------------------------------------------------------------------------------- /proguard.cfg: -------------------------------------------------------------------------------- 1 | -optimizationpasses 5 2 | -dontusemixedcaseclassnames 3 | -dontskipnonpubliclibraryclasses 4 | -dontpreverify 5 | -verbose 6 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 7 | 8 | -keep public class * extends android.app.Activity 9 | -keep public class * extends android.app.Application 10 | -keep public class * extends android.app.Service 11 | -keep public class * extends android.content.BroadcastReceiver 12 | -keep public class * extends android.content.ContentProvider 13 | -keep public class * extends android.app.backup.BackupAgentHelper 14 | -keep public class * extends android.preference.Preference 15 | -keep public class com.android.vending.licensing.ILicensingService 16 | 17 | -keepclasseswithmembernames class * { 18 | native ; 19 | } 20 | 21 | -keepclasseswithmembers class * { 22 | public (android.content.Context, android.util.AttributeSet); 23 | } 24 | 25 | -keepclasseswithmembers class * { 26 | public (android.content.Context, android.util.AttributeSet, int); 27 | } 28 | 29 | -keepclassmembers class * extends android.app.Activity { 30 | public void *(android.view.View); 31 | } 32 | 33 | -keepclassmembers enum * { 34 | public static **[] values(); 35 | public static ** valueOf(java.lang.String); 36 | } 37 | 38 | -keep class * implements android.os.Parcelable { 39 | public static final android.os.Parcelable$Creator *; 40 | } 41 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.compileSdkVersion 5 | buildToolsVersion rootProject.buildToolsVersion 6 | 7 | defaultConfig { 8 | applicationId "com.codinguser.android.contactpicker.sample" 9 | minSdkVersion rootProject.minSdkVersion 10 | targetSdkVersion rootProject.targetSdkVersion 11 | versionCode rootProject.versionCode 12 | versionName rootProject.versionName 13 | } 14 | 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | 22 | lintOptions { 23 | abortOnError false 24 | } 25 | 26 | applicationVariants.all { variant -> 27 | variant.outputs.each { output -> 28 | output.outputFile = new File( 29 | output.outputFile.parent, "contact_picker_sample_v${variant.versionName}.apk") 30 | } 31 | } 32 | 33 | compileOptions { //we want switch with strings during xml parsing 34 | encoding "UTF-8" 35 | sourceCompatibility JavaVersion.VERSION_1_7 36 | targetCompatibility JavaVersion.VERSION_1_7 37 | } 38 | } 39 | 40 | 41 | dependencies { 42 | compile fileTree(dir: 'libs', include: ['*.jar']) 43 | compile 'com.android.support:support-v4:22.2.0' 44 | compile project(':library') 45 | } 46 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 28 | 29 | 30 | 31 | 34 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /sample/src/main/java/com/codinguser/android/contactpicker/sample/ContactPickerSampleActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 by Ngewi Fet 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | package com.codinguser.android.contactpicker.sample; 24 | 25 | import android.os.Bundle; 26 | import android.support.v7.app.ActionBar; 27 | import android.widget.Toast; 28 | 29 | import com.codinguser.android.contactpicker.ContactsPickerActivity; 30 | import com.codinguser.android.contactpicker.OnContactSelectedListener; 31 | 32 | /** 33 | * Extends the ContactsPickerActivity class from the library for demonstration purposes. 34 | * Instead of delivering an intent with the selected contact info, a toast is displayed 35 | */ 36 | public class ContactPickerSampleActivity extends ContactsPickerActivity implements OnContactSelectedListener { 37 | 38 | @Override 39 | protected void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | ActionBar actionBar = getSupportActionBar(); 42 | if (actionBar != null){ 43 | actionBar.setDisplayHomeAsUpEnabled(false); 44 | } 45 | } 46 | 47 | /** 48 | * Callback when the contact number is selected from the contact details view 49 | * Sets the activity result with the contact information and finishes 50 | */ 51 | @Override 52 | public void onContactNumberSelected(String contactNumber, String contactName) { 53 | Toast.makeText(this, String.format("Selected:\n %s: %s\nAn intent would be delivered to your app", 54 | contactName, contactNumber), 55 | Toast.LENGTH_LONG).show(); 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 27 | 35 | 42 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':library', ':sample' 2 | --------------------------------------------------------------------------------