Example Android app that makes use of SparkJava Spark API library to authenticate via Hybrid or OpenID methods, search listings, view listings, view an individual listing with photos and standard fields, and view a user account. View app [screenshots](./Spark Android Screenshots.pdf) on a Nexus 7.
- JDK 1.6+
- Maven 3.0.3+
- SparkJava
- Android SDK -- at least SDK Version 14 should be downloaded.
- Optional Eclipse IDE with Android Development Tools and m2eclipse installed
- App designed for devices running Android 4.0 or later (API level 14 / Ice Cream Sandwich)
Once you register as a Spark developer and receive your Spark Client Id and Client Secret, open the sparkapi.properties file and set the API_KEY and API_SECRET properties. You must also set the USER_AGENT with the name of your app or your API requests will not be accepted.
Once an Android SDK is downloaded to your development machine using the Android SDK tool, it must be deployed to your local maven repository using the maven-android-sdk-deployer and command mvn install -P 4.0. The pom.xml is currently set to use the 4.0 SDK (Version 14) but more recent versions can also be used.
If you want to run on a virtual device, create an Android Virtual Device (AVD) using the android avdtool.
Get a handle to a SparkAPI object via the getInstance() singleton static method. If the instance has not been created, SparkAPI will be instantiated and returned with your configuration.
The pom.xml file contains the maven configuration. You must set the sdk path to the location of your local Android SDKs. If you want to run on an android device, make sure the <device>usb</device> tag is present. Otherwise, to run on the AVD, use the <device>emulator</device> tag.
To compile, install, and run on an Android device, use the target mvn clean install android:deploy android:run.
The SparkAPI object from the SparkJava API library is designed to work with Android WebView and WebViewClient objects to initiate and process Spark authentication.
Initiating an Authentication Request:
To initiate a Hybrid authentication request, call
WebView loadUrl()withgetSparkHybridOpenIdURLString.To initiate an OpenID authentication request, call
WebView loadUrl()withgetSparkOpenIdURLStringorgetSparkOpenIdAttributeExchangeURLString.
These authentication methods are typically placed in a WebViewClient object to respond to a URL request generated after the user provides their Spark credentials. See WebViewActivity.java for an example.
publicbooleanshouldOverrideUrlLoading (WebViewview, Stringurl)
{
StringopenIdSparkCode = null;
if(loginHybrid && (openIdSparkCode = SparkAPI.isHybridAuthorized(url)) != null)
{
Log.d(TAG, "openIdSparkCode>" + openIdSparkCode);
newOAuth2PostTask().execute(openIdSparkCode); returntrue;
}
returnfalse;
}
privateclassOAuth2PostTaskextendsAsyncTask<String, Void, SparkSession> {
protectedSparkSessiondoInBackground(String... openIdSparkCode) {
SparkSessionsession = null;
try
{
session = sparkClient.hybridAuthenticate(openIdSparkCode[0]);
}
catch(SparkAPIClientExceptione)
{
Log.e(TAG, "SparkApiClientException", e);
}
returnsession;
}
protectedvoidonPostExecute(SparkSessionsparkSession) { if(sparkSession != null)
{
processAuthentication(sparkSession, null);
Intentintent = newIntent(getApplicationContext(), ViewListingsActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent); }
}
}Below is an example API call to the /my/account Spark API endpoint from the example app. On response, the list view interface is updated.
privateclassMyAccountTaskextendsAsyncTask<Void, Void, Response> {
protectedResponsedoInBackground(Void... v) {
Responser = null;
try
{
r = SparkAPI.getInstance().get("/my/account",null);
}
catch(SparkAPIClientExceptione)
{
Log.e(TAG, "/my/account exception>", e);
}
returnr;
}
protectedvoidonPostExecute(Responser) {
JsonNodeaccount = r.getFirstResult();
if(account != null)
{
List<Map<String,String>> list = newArrayList<Map<String,String>>();
ActivityHelper.addListLine(list, "Name", account.get("Name").getTextValue());
ActivityHelper.addListLine(list, "Office", account.get("Office").getTextValue());
ActivityHelper.addListLine(list, "Company", account.get("Company").getTextValue());
ActivityHelper.addArrayLine(account, "Addresses", "Address", list, "Address");
ActivityHelper.addListLine(list, "MLS", account.get("Mls").getTextValue());
ActivityHelper.addArrayLine(account, "Emails", "Address", list, "Email");
ActivityHelper.addArrayLine(account, "Phones", "Number", list, "Phone");
ActivityHelper.addArrayLine(account, "Websites", "Uri", list, "Website");
ListAdapteradapter = newSimpleAdapter(getApplicationContext(), list,
R.layout.two_line_list_item, newString[] {"line1", "line2"}, newint[] {android.R.id.text1, android.R.id.text2});
setListAdapter(adapter);
}
}
}The example app provides a great starting point for building your own Spark-powered Android app. At a minimum, the core authentication features encapsulated by LoginActivity and WebViewActivity can be repurposed.
In your MainActivityonCreate method, you will need code similar to below that reads any saved tokens and bypasses Login if the session is valid to show your home Activity. If the session is not valid, the LoginActivity is presented.
publicclassMainActivityextendsActivity {
privatestaticfinalStringTAG = "MainActivity";
@OverrideprotectedvoidonCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
Intentintent = getMainIntent();
startActivity(intent);
}
privateIntentgetMainIntent()
{
Intentintent = null;
try
{
SecurePreferencesp = newSecurePreferences(this,UIConstants.SPARK_PREFERENCES, SparkAPI.getConfiguration().getApiSecret(), false);
StringaccessToken = p.getString(UIConstants.AUTH_ACCESS_TOKEN);
StringrefreshToken = p.getString(UIConstants.AUTH_REFRESH_TOKEN);
StringopenIdToken = p.getString(UIConstants.AUTH_OPENID);
if(accessToken != null && refreshToken != null)
{
SparkSessionsession = newSparkSession();
session.setAccessToken(accessToken);
session.setRefreshToken(refreshToken);
SparkAPI.getInstance().setSession(session);
intent = newIntent(this, ViewListingsActivity.class);
}
elseif(openIdToken != null)
{
SparkSessionsession = newSparkSession();
session.setOpenIdToken(openIdToken);
SparkAPI.getInstance().setSession(session);
intent = newIntent(this, MyAccountActivity.class);
}
elseintent = newIntent(this, LoginActivity.class);
}
catch(SparkAPIClientExceptione)
{
Log.e(TAG, "SparkApiClientException", e);
}
returnintent;
}In WebViewActivity, the processAuthentication method should also be modified to save any session state (securely to SharedPreferences or other storage) as well as redirect the user to the top Activity.
privateclassOAuth2PostTaskextendsAsyncTask<String, Void, SparkSession> {
...
protectedvoidonPostExecute(SparkSessionsparkSession) { if(sparkSession != null)
{
processAuthentication(sparkSession, null);
Intentintent = newIntent(getApplicationContext(), ViewListingsActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent); }
}
}
privatevoidprocessAuthentication(SparkSessionsession, Stringurl)
{
SecurePreferencesp = newSecurePreferences(this,UIConstants.SPARK_PREFERENCES, SparkAPI.getConfiguration().getApiSecret(), false);
editor.put(UIConstants.AUTH_ACCESS_TOKEN, session.getAccessToken());
editor.put(UIConstants.AUTH_REFRESH_TOKEN, session.getRefreshToken());
}- Apache commons-codec
- Apache commons-lang3
- Apache commons-logging
- Jackson JSON processor
- JodaTime
- log4j
- android-logging-log4j
- SecurePreferences
Tested OSs: Android 4.2 Jelly Bean (Version 17), Android 4.0 Ice Cream Sandwich (Version 14)
Tested Eclipse versions: 3.7 Indigo
Tested Devices: Nexus 7, Nexus 4