├── .classpath
├── .gitignore
├── .project
├── .settings
└── org.eclipse.jdt.core.prefs
├── AndroidManifest.xml
├── README.md
├── build.gradle
├── google-services.json
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── ic_launcher-web.png
├── libs
└── android-support-v4.jar
├── lint.xml
├── proguard-project.txt
├── project.properties
├── res
├── drawable-hdpi
│ ├── ic_action_search.png
│ └── ic_launcher.png
├── drawable-ldpi
│ └── ic_launcher.png
├── drawable-mdpi
│ ├── ic_action_search.png
│ └── ic_launcher.png
├── drawable-xhdpi
│ ├── ic_action_search.png
│ └── ic_launcher.png
├── layout
│ └── activity_main.xml
├── menu
│ └── activity_main.xml
└── values
│ ├── strings.xml
│ └── styles.xml
├── settings.gradle
└── src
└── com
└── mixpanel
└── example
└── hello
├── MainActivity.java
└── SessionManager.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | /*/build/
3 | build/
4 | # Crashlytics configuations
5 | com_crashlytics_export_strings.xml
6 |
7 | # Local configuration file (sdk path, etc)
8 | local.properties
9 |
10 | # Gradle generated files
11 | .gradle/
12 |
13 | # Signing files
14 | .signing/
15 |
16 | # User-specific configurations
17 | .idea
18 | *.iml
19 |
20 | # OS-specific files
21 | .DS_Store
22 | .DS_Store?
23 | ._*
24 | .Spotlight-V100
25 | .Trashes
26 | ehthumbs.db
27 | Thumbs.db
28 |
29 | .idea/libraries/support_v4_22_2_1.xml
30 |
31 | app/app.iml
32 | app/config.txt
33 |
34 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | Mixpanel Example Android Application
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
3 | org.eclipse.jdt.core.compiler.compliance=1.5
4 | org.eclipse.jdt.core.compiler.source=1.5
5 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
14 |
15 |
18 |
19 |
20 |
23 |
24 |
25 |
28 |
29 |
30 |
35 |
36 |
38 |
40 |
41 |
44 |
46 |
47 |
51 |
53 |
54 |
57 |
59 |
60 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
72 |
74 |
75 |
76 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sample Android Application for Mixpanel Integration
2 |
3 | This repository contains a sample application demonstrating how you
4 | can use Mixpanel in your Android apps.
5 |
6 | ## Getting Started
7 |
8 | The sample application can be build using Android Studio.
9 |
10 | ### Using Android Studio
11 | Integration in Android Studio is very simple.
12 |
13 | - Clone this git repository (the instructions are at the top of the page)
14 | - Within Android Studio, go to "File > Import Project"
15 | - Select the directory you just cloned.
16 | - When the gradle popup shows up, choose "use gradle default wrapper (recommended)" and click OK
17 |
18 | You should now be able to run the sample application
19 |
20 | You will also need to add your Mixpanel API token and your Android
21 | Push Sender id to the source code, and enable Mixpanel to send Google
22 | Cloud Messages on your behalf.
23 |
24 | ## Add your Mixpanel Token to your code and google-services.json file to your project
25 | There is one value in MainActivity.java that you'll need to update
26 | before you can send data to Mixpanel. You'll need to update the source code with
27 | your Mixpanel API Token to send data.
28 |
29 | ### For Your Mixpanel Token
30 |
31 | - Log in to your account at https://www.mixpanel.com
32 | - Select the project you'll be working with from the drop-down at the top left
33 | - Click the gear link at the top right to show the project settings dialog
34 | - Copy the "Token" string from the dialog
35 |
36 | Change the value of MainActivity.MIXPANEL_API_TOKEN to the value you
37 | copied from the web page.
38 |
39 | ### For enabling push notifications using FCM
40 |
41 | - Log in to your Firebase Console at https://console.firebase.google.com
42 | - Select your project and click on the gear (top left corner) to access your Project settings
43 | - Go to "Cloud Messaging" tab
44 | - Scroll down and click on "google-services.json" to download your config file.
45 | - Place your file on your app module directory.
46 |
47 | ## Set up Firebase Cloud Messaging services in Mixpanel
48 |
49 | To send Firebase Cloud Messages, you will also have to connect Mixpanel
50 | to your Google APIs account. To do this you'll need to make sure Cloud
51 | Messaging is enabled, and provide an FCM Server key to Mixpanel
52 |
53 | This process is documented in more detail, including screenshots, at
54 |
55 | https://mixpanel.com/docs/people-analytics/android-push
56 |
57 | ### To Provide your Firebase Server Key to Mixpanel
58 |
59 | - Log in to your Firebase Console Console at https://console.firebase.google.com
60 | - Select your project and click on the gear (top left corner) to access your Project settings
61 | - Go to "Cloud Messaging" tab
62 | - Copy your "Server key"
63 | - Log in to Mixpanel at http://www.mixpanel.com, and select the project associated with this application
64 | - Click the gear icon in the upper right corner of the screen to show the project settings dialog
65 | - Click on the Messages tab of the project settings dialog
66 | - Paste your Firebase Server Key into the "Android FCM Server Key" field
67 |
68 | Once you've added your keys to the source code and set up Mixpanel to
69 | send Firebase Cloud Messages, you're ready to build and deploy your application.
70 |
71 | ## Getting More Information
72 |
73 | The Mixpanel Android integration API documentation is available on the Mixpanel website.
74 |
75 | For an overview of Mixpanel Android library features
76 | : https://mixpanel.com/help/reference/android
77 |
78 | For details about setting up and implementing Google Cloud Messaging Notifications
79 | : https://mixpanel.com/help/reference/android-push-notifications
80 |
81 | For a detailed Android API reference
82 | : http://mixpanel.github.io/mixpanel-android/index.html
83 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | dependencies {
7 | classpath 'com.google.gms:google-services:4.0.2'
8 | classpath 'com.android.tools.build:gradle:3.3.1'
9 | }
10 | }
11 |
12 | apply plugin: 'com.android.application'
13 |
14 | repositories {
15 | mavenLocal()
16 | google()
17 | mavenCentral()
18 | }
19 |
20 | dependencies {
21 | implementation 'com.google.firebase:firebase-messaging:17.3.4'
22 | implementation 'com.mixpanel.android:mixpanel-android:5.8.0'
23 | implementation 'com.android.installreferrer:installreferrer:1.1'
24 | }
25 |
26 |
27 | android {
28 | defaultConfig {
29 | applicationId "com.mixpanel.example.hello"
30 | minSdkVersion 14
31 | targetSdkVersion 27
32 | versionCode 1
33 | versionName "1.0"
34 | }
35 |
36 | compileSdkVersion 28
37 |
38 | sourceSets {
39 | main {
40 | manifest.srcFile 'AndroidManifest.xml'
41 | java.srcDirs = ['src']
42 | resources.srcDirs = ['src']
43 | aidl.srcDirs = ['src']
44 | renderscript.srcDirs = ['src']
45 | res.srcDirs = ['res']
46 | assets.srcDirs = ['assets']
47 | }
48 |
49 | // Move the tests to tests/java, tests/res, etc...
50 | androidTest.setRoot('tests')
51 |
52 | // Move the build types to build-types/
53 | // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
54 | // This moves them out of them default location under src//... which would
55 | // conflict with src/ being used by the main source set.
56 | // Adding new build types or product flavors should be accompanied
57 | // by a similar customization.
58 | debug.setRoot('build-types/debug')
59 | release.setRoot('build-types/release')
60 | }
61 | }
62 |
63 | allprojects {
64 | repositories {
65 | google()
66 | }
67 | }
68 |
69 | apply plugin: 'com.google.gms.google-services'
70 |
--------------------------------------------------------------------------------
/google-services.json:
--------------------------------------------------------------------------------
1 | // YOUR GOOGLE-SERVICES.JSON FILE
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mixpanel/sample-android-mixpanel-integration/ece237f3cc550a413b77728769875caa2dafab9c/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Feb 07 13:38:04 CET 2019
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-4.10.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mixpanel/sample-android-mixpanel-integration/ece237f3cc550a413b77728769875caa2dafab9c/ic_launcher-web.png
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mixpanel/sample-android-mixpanel-integration/ece237f3cc550a413b77728769875caa2dafab9c/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-19
15 |
16 | # Put the path to your mixpanel library sources here
17 | # (For example, libs/mixpanel-android)
18 | android.library.reference.1=PATH_TO_MIXPANEL_ON_YOUR_SYSTEM
19 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_action_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mixpanel/sample-android-mixpanel-integration/ece237f3cc550a413b77728769875caa2dafab9c/res/drawable-hdpi/ic_action_search.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mixpanel/sample-android-mixpanel-integration/ece237f3cc550a413b77728769875caa2dafab9c/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mixpanel/sample-android-mixpanel-integration/ece237f3cc550a413b77728769875caa2dafab9c/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_action_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mixpanel/sample-android-mixpanel-integration/ece237f3cc550a413b77728769875caa2dafab9c/res/drawable-mdpi/ic_action_search.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mixpanel/sample-android-mixpanel-integration/ece237f3cc550a413b77728769875caa2dafab9c/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_action_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mixpanel/sample-android-mixpanel-integration/ece237f3cc550a413b77728769875caa2dafab9c/res/drawable-xhdpi/ic_action_search.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mixpanel/sample-android-mixpanel-integration/ece237f3cc550a413b77728769875caa2dafab9c/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
12 |
13 |
18 |
19 |
24 |
25 |
30 |
31 |
36 |
37 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/res/menu/activity_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Hello Mixpanel
4 | First Name
5 | Last Name
6 | Email Address
7 | Send to Mixpanel
8 | Send Revenue
9 | Settings
10 | Hello Mixpanel
11 | Set background image
12 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ":"
2 |
--------------------------------------------------------------------------------
/src/com/mixpanel/example/hello/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.mixpanel.example.hello;
2 |
3 | import android.app.Activity;
4 | import android.content.ContentResolver;
5 | import android.content.Intent;
6 | import android.content.SharedPreferences;
7 | import android.graphics.Bitmap;
8 | import android.graphics.BitmapFactory;
9 | import android.graphics.drawable.BitmapDrawable;
10 | import android.net.Uri;
11 | import android.os.Bundle;
12 | import android.util.Base64;
13 | import android.util.Log;
14 | import android.view.Menu;
15 | import android.view.View;
16 | import android.widget.EditText;
17 |
18 | import com.mixpanel.android.mpmetrics.MixpanelAPI;
19 |
20 | import org.json.JSONException;
21 | import org.json.JSONObject;
22 |
23 | import java.io.FileNotFoundException;
24 | import java.io.InputStream;
25 | import java.util.Calendar;
26 | import java.util.Date;
27 | import java.util.Random;
28 |
29 | /**
30 | * A little application that allows people to update their Mixpanel information,
31 | * and receive push notifications from a Mixpanel project.
32 | *
33 | * For more information about integrating Mixpanel with your Android application,
34 | * please check out:
35 | *
36 | * https://mixpanel.com/docs/integration-libraries/android
37 | *
38 | * For instructions on enabling push notifications from Mixpanel, please see
39 | *
40 | * https://mixpanel.com/docs/people-analytics/android-push
41 | *
42 | * @author mixpanel
43 | *
44 | */
45 | public class MainActivity extends Activity {
46 |
47 | /*
48 | * You will use a Mixpanel API token to allow your app to send data to Mixpanel. To get your token
49 | * - Log in to Mixpanel, and select the project you want to use for this application
50 | * - Click the gear icon in the lower left corner of the screen to view the settings dialog
51 | * - In the settings dialog, you will see the label "Token", and a string that looks something like this:
52 | *
53 | * 2ef3c08e8466df98e67ea0cfa1512e9f
54 | *
55 | * Paste it below (where you see "YOUR API TOKEN")
56 | */
57 | public static final String MIXPANEL_API_TOKEN = "fa45c697d1c664abb760166578f91f24";
58 |
59 | /*
60 | * In order for your app to receive push notifications, you will need to enable
61 | * the Google Cloud Messaging for Android service in your Google APIs console. To do this:
62 | *
63 | * - Navigate to https://code.google.com/apis/console
64 | * - Select "Services" from the menu on the left side of the screen
65 | * - Scroll down until you see the row labeled "Google Cloud Messaging for Android"
66 | * - Make sure the switch next to the service name says "On"
67 | *
68 | * To identify this application with your Google API account, you'll also need your sender id from Google.
69 | * You can get yours by logging in to the Google APIs Console at https://code.google.com/apis/console
70 | * Once you have logged in, your sender id will appear as part of the URL in your browser's address bar.
71 | * The URL will look something like this:
72 | *
73 | * https://code.google.com/apis/console/b/0/#project:256660625236
74 | * ^^^^^^^^^^^^
75 | *
76 | * The twelve-digit number after 'project:' is your sender id. Paste it below (where you see "YOUR SENDER ID")
77 | *
78 | * There are also some changes you will need to make to your AndroidManifest.xml file to
79 | * declare the permissions and receiver capabilities you'll need to get your push notifications working.
80 | * You can take a look at this application's AndroidManifest.xml file for an example of what is needed.
81 | */
82 |
83 | @Override
84 | public void onCreate(Bundle savedInstanceState) {
85 | super.onCreate(savedInstanceState);
86 | final String trackingDistinctId = getTrackingDistinctId();
87 |
88 | // Initialize the Mixpanel library for tracking and push notifications.
89 | mMixpanel = MixpanelAPI.getInstance(this, MIXPANEL_API_TOKEN);
90 |
91 |
92 | // We also identify the current user with a distinct ID, and
93 | // register ourselves for push notifications from Mixpanel.
94 |
95 | mMixpanel.identify(trackingDistinctId); //this is the distinct_id value that
96 | // will be sent with events. If you choose not to set this,
97 | // the SDK will generate one for you
98 |
99 | mMixpanel.getPeople().identify(trackingDistinctId); //this is the distinct_id
100 | // that will be used for people analytics. You must set this explicitly in order
101 | // to dispatch people data.
102 |
103 | // People analytics must be identified separately from event analytics.
104 | // The data-sets are separate, and may have different unique keys (distinct_id).
105 | // We recommend using the same distinct_id value for a given user in both,
106 | // and identifying the user with that id as early as possible.
107 |
108 |
109 | setContentView(R.layout.activity_main);
110 | }
111 |
112 | @Override
113 | public boolean onCreateOptionsMenu(Menu menu) {
114 | getMenuInflater().inflate(R.menu.activity_main, menu);
115 | return true;
116 | }
117 |
118 | @Override
119 | protected void onResume() {
120 | super.onResume();
121 |
122 | final long nowInHours = hoursSinceEpoch();
123 | final int hourOfTheDay = hourOfTheDay();
124 |
125 | // For our simple test app, we're interested tracking
126 | // when the user views our application.
127 |
128 | // It will be interesting to segment our data by the date that they
129 | // first viewed our app. We use a
130 | // superProperty (so the value will always be sent with the
131 | // remainder of our events) and register it with
132 | // registerSuperPropertiesOnce (so no matter how many times
133 | // the code below is run, the events will always be sent
134 | // with the value of the first ever call for this user.)
135 | // all the change we make below are LOCAL. No API requests are made.
136 | try {
137 | final JSONObject properties = new JSONObject();
138 | properties.put("first viewed on", nowInHours);
139 | properties.put("user domain", "(unknown)"); // default value
140 | mMixpanel.registerSuperPropertiesOnce(properties);
141 | } catch (final JSONException e) {
142 | throw new RuntimeException("Could not encode hour first viewed as JSON");
143 | }
144 |
145 | // Now we send an event to Mixpanel. We want to send a new
146 | // "App Resumed" event every time we are resumed, and
147 | // we want to send a current value of "hour of the day" for every event.
148 | // As usual,all of the user's super properties will be appended onto this event.
149 | try {
150 | final JSONObject properties = new JSONObject();
151 | properties.put("hour of the day", hourOfTheDay);
152 | mMixpanel.track("App Resumed", properties);
153 | } catch(final JSONException e) {
154 | throw new RuntimeException("Could not encode hour of the day in JSON");
155 | }
156 |
157 | // If you have surveys or notifications, and you have set AutoShowMixpanelUpdates set to false,
158 | // the onResume function is a good place to call the functions to display surveys or
159 | // in app notifications. It is safe to call both these methods right after each other,
160 | // since they do nothing if a notification or survey is already showing.
161 | mMixpanel.getPeople().showNotificationIfAvailable(this);
162 | }
163 |
164 | // Associated with the "Send to Mixpanel" button in activity_main.xml
165 | // In this method, we update a Mixpanel people profile using MixpanelAPI.People.set()
166 | // and set some persistent properties that will be sent with
167 | // all future track() calls using MixpanelAPI.registerSuperProperties()
168 | public void sendToMixpanel(final View view) {
169 |
170 | final EditText firstNameEdit = (EditText) findViewById(R.id.edit_first_name);
171 | final EditText lastNameEdit = (EditText) findViewById(R.id.edit_last_name);
172 | final EditText emailEdit = (EditText) findViewById(R.id.edit_email_address);
173 |
174 | final String firstName = firstNameEdit.getText().toString();
175 | final String lastName = lastNameEdit.getText().toString();
176 | final String email = emailEdit.getText().toString();
177 |
178 | final MixpanelAPI.People people = mMixpanel.getPeople();
179 |
180 | // Update the basic data in the user's People Analytics record.
181 | // Unlike events, People Analytics always stores the most recent value
182 | // provided.
183 | people.set("$first_name", firstName);
184 | people.set("$last_name", lastName);
185 | people.set("$email", email);
186 |
187 | // We also want to keep track of how many times the user
188 | // has updated their info.
189 | people.increment("Update Count", 1L);
190 |
191 | // Mixpanel events are separate from Mixpanel people records,
192 | // but it might be valuable to be able to query events by
193 | // user domain (for example, if they represent customer organizations).
194 | //
195 | // We use the user domain as a superProperty here, but we call registerSuperProperties
196 | // instead of registerSuperPropertiesOnce so we can overwrite old values
197 | // as we get new information.
198 | try {
199 | final JSONObject domainProperty = new JSONObject();
200 | domainProperty.put("user domain", domainFromEmailAddress(email));
201 | mMixpanel.registerSuperProperties(domainProperty);
202 | } catch (final JSONException e) {
203 | throw new RuntimeException("Cannot write user email address domain as a super property");
204 | }
205 |
206 | // In addition to viewing the updated record in mixpanel's UI, it might
207 | // be interesting to see when and how many and what types of users
208 | // are updating their information, so we'll send an event as well.
209 | // You can call track with null if you don't have any properties to add
210 | // to an event (remember all the established superProperties will be added
211 | // before the event is dispatched to Mixpanel)
212 | mMixpanel.track("update info button clicked", null);
213 | }
214 |
215 | // This is an example of how you can use Mixpanel's revenue tracking features from Android.
216 | public void recordRevenue(final View view) {
217 | final MixpanelAPI.People people = mMixpanel.getPeople();
218 | // Call trackCharge() with a floating point amount
219 | // (for example, the amount of money the user has just spent on a purchase)
220 | // and an optional set of properties describing the purchase.
221 | people.trackCharge(1.50, null);
222 | }
223 |
224 | @Override
225 | protected void onDestroy() {
226 | super.onDestroy();
227 |
228 | // To preserve battery life, the Mixpanel library will store
229 | // events rather than send them immediately. This means it
230 | // is important to call flush() to send any unsent events
231 | // before your application is taken out of memory.
232 | mMixpanel.flush();
233 | }
234 |
235 | ////////////////////////////////////////////////////
236 |
237 | public void setBackgroundImage(final View view) {
238 | final Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
239 | photoPickerIntent.setType("image/*");
240 | startActivityForResult(photoPickerIntent, PHOTO_WAS_PICKED);
241 | }
242 |
243 | @Override
244 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
245 | if (PHOTO_WAS_PICKED == requestCode && null != data) {
246 | final Uri imageUri = data.getData();
247 | if (null != imageUri) {
248 | // AsyncTask, please...
249 | final ContentResolver contentResolver = getContentResolver();
250 | try {
251 | final InputStream imageStream = contentResolver.openInputStream(imageUri);
252 | System.out.println("DRAWING IMAGE FROM URI " + imageUri);
253 | final Bitmap background = BitmapFactory.decodeStream(imageStream);
254 | getWindow().setBackgroundDrawable(new BitmapDrawable(getResources(), background));
255 | } catch (final FileNotFoundException e) {
256 | Log.e(LOGTAG, "Image apparently has gone away", e);
257 | }
258 | }
259 | }
260 | }
261 |
262 | private String getTrackingDistinctId() {
263 | final SharedPreferences prefs = getPreferences(MODE_PRIVATE);
264 |
265 | String ret = prefs.getString(MIXPANEL_DISTINCT_ID_NAME, null);
266 | if (ret == null) {
267 | ret = generateDistinctId();
268 | final SharedPreferences.Editor prefsEditor = prefs.edit();
269 | prefsEditor.putString(MIXPANEL_DISTINCT_ID_NAME, ret);
270 | prefsEditor.commit();
271 | }
272 |
273 | return ret;
274 | }
275 |
276 | // These disinct ids are here for the purposes of illustration.
277 | // In practice, there are great advantages to using distinct ids that
278 | // are easily associated with user identity, either from server-side
279 | // sources, or user logins. A common best practice is to maintain a field
280 | // in your users table to store mixpanel distinct_id, so it is easily
281 | // accesible for use in attributing cross platform or server side events.
282 | private String generateDistinctId() {
283 | final Random random = new Random();
284 | final byte[] randomBytes = new byte[32];
285 | random.nextBytes(randomBytes);
286 | return Base64.encodeToString(randomBytes, Base64.NO_WRAP | Base64.NO_PADDING);
287 | }
288 |
289 | ///////////////////////////////////////////////////////
290 | // conveniences
291 |
292 | private int hourOfTheDay() {
293 | final Calendar calendar = Calendar.getInstance();
294 | return calendar.get(Calendar.HOUR_OF_DAY);
295 | }
296 |
297 | private long hoursSinceEpoch() {
298 | final Date now = new Date();
299 | final long nowMillis = now.getTime();
300 | return nowMillis / 1000 * 60 * 60;
301 | }
302 |
303 | private String domainFromEmailAddress(String email) {
304 | String ret = "";
305 | final int atSymbolIndex = email.indexOf('@');
306 | if ((atSymbolIndex > -1) && (email.length() > atSymbolIndex)) {
307 | ret = email.substring(atSymbolIndex + 1);
308 | }
309 |
310 | return ret;
311 | }
312 |
313 | private MixpanelAPI mMixpanel;
314 | private static final String MIXPANEL_DISTINCT_ID_NAME = "Mixpanel Example $distinctid";
315 | private static final int PHOTO_WAS_PICKED = 2;
316 | private static final String LOGTAG = "Mixpanel Example Application";
317 | }
318 |
--------------------------------------------------------------------------------
/src/com/mixpanel/example/hello/SessionManager.java:
--------------------------------------------------------------------------------
1 | package com.mixpanel.example.hello;
2 |
3 | import android.content.Context;
4 | import android.os.Handler;
5 | import android.os.HandlerThread;
6 | import android.os.Looper;
7 | import android.os.Message;
8 | import android.util.Log;
9 |
10 | import org.json.JSONArray;
11 | import org.json.JSONException;
12 | import org.json.JSONObject;
13 |
14 | import java.io.BufferedReader;
15 | import java.io.FileInputStream;
16 | import java.io.FileNotFoundException;
17 | import java.io.FileOutputStream;
18 | import java.io.IOException;
19 | import java.io.InputStreamReader;
20 | import java.util.ArrayList;
21 | import java.util.Iterator;
22 | import java.util.List;
23 | import java.util.UUID;
24 |
25 |
26 | /**
27 | * This class serves as an example of how session tracking may be done on Android. The length of a session
28 | * is defined as the time between a call to startSession() and a call to endSession() after which there is
29 | * not another call to startSession() for at least 15 seconds. If a session has been started and
30 | * another startSession() function is called, it is a no op.
31 | *
32 | * This class is not officially supported by Mixpanel, and you may need to modify it for your own application.
33 | *
34 | * Example Usage:
35 | *
36 | *