1. The onPostExecute method from AsyncTask is executed in the UI thread and I do not want that behaviour,
so it seems the best option to lauch a new Thread using without using the AsyncTask
2. First steps retrieving the JSON data from sent by the RESTful Web Service
--- /dev/null
+INDEX:
+
+1. Test1: Login UI with AsyncTask. The connection to the RESTful Web Service is made by an AsyncTask.
+
+2. Test2: Sequential login UI. The connection to the RESTful Web Service is made in the UI thread.
+ With an AsyncTask to treat updates from LocationManager.
+ DialogFragment API for popups/dialogs.
+ Method to determine whether one Location reading is better than the current Location from:
+ http://developer.android.com/guide/topics/location/obtaining-user-location.html
+ It is useful when you can use a window to improve the results retrieved from the LocationManager
+
+3. Test3: Sequential login UI.
+ Using Java Concurrent to treat updates from LocationManager.
+ DialogFragment API for popups/dialogs.
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="de.android.test3"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk android:minSdkVersion="14" />
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name" >
+ <activity
+ android:label="@string/app_name"
+ android:name=".Test3Activity"
+ android:screenOrientation="portrait"
+ android:configChanges="touchscreen|keyboard"
+ android:theme="@android:style/Theme.Black"
+ android:permission="android.permission.INTERNET"
+ android:launchMode="standard"
+ android:noHistory="true">
+ <intent-filter >
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:label="@string/app_name"
+ android:name=".NextActivity"
+ android:theme="@android:style/Theme.Black"
+ android:screenOrientation="portrait"
+ android:configChanges="touchscreen|keyboard"
+ android:permission="android.permission.INTERNET">
+ <intent-filter>
+ <action android:name="android.intent.action.RUN"/>
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ </application>
+
+</manifest>
\ No newline at end of file
--- /dev/null
+/* AUTO-GENERATED FILE. DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found. It
+ * should not be modified by hand.
+ */
+
+package de.android.test3;
+
+public final class R {
+ public static final class attr {
+ }
+ public static final class drawable {
+ public static final int alert_dialog_icon=0x7f020000;
+ public static final int ic_launcher=0x7f020001;
+ }
+ public static final class id {
+ public static final int cancel_button=0x7f050005;
+ public static final int frameLayout1=0x7f050000;
+ public static final int frameLayout2=0x7f050003;
+ public static final int login_button=0x7f050004;
+ public static final int password=0x7f050002;
+ public static final int username=0x7f050001;
+ }
+ public static final class layout {
+ public static final int main=0x7f030000;
+ public static final int nextactivity=0x7f030001;
+ }
+ public static final class string {
+ public static final int alert_dialog_cancel=0x7f040000;
+ public static final int app_name=0x7f040004;
+ public static final int button_cancel=0x7f040009;
+ public static final int button_login=0x7f040007;
+ public static final int button_ok=0x7f040008;
+ public static final int error_dialog_connection_error=0x7f040001;
+ public static final int error_dialog_userpwd_error=0x7f040002;
+ public static final int hello=0x7f040003;
+ public static final int password=0x7f040006;
+ public static final int username=0x7f040005;
+ }
+}
--- /dev/null
+-optimizationpasses 5
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-dontpreverify
+-verbose
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+-keep public class com.android.vending.licensing.ILicensingService
+
+-keepclasseswithmembernames class * {
+ native <methods>;
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclassmembers class * extends android.app.Activity {
+ public void *(android.view.View);
+}
+
+-keepclassmembers enum * {
+ public static **[] values();
+ public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable {
+ public static final android.os.Parcelable$Creator *;
+}
--- /dev/null
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-14
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:gravity="right"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:weightSum="1">
+
+ <FrameLayout
+ android:id="@+id/frameLayout1"
+ android:layout_width="match_parent"
+ android:layout_height="70dp">
+ </FrameLayout>
+
+ <EditText
+ android:id="@+id/username"
+ android:singleLine="true"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/username"/>
+
+ <EditText
+ android:id="@+id/password"
+ android:password="true"
+ android:singleLine="true"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/password"/>
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:id="@+id/frameLayout2"
+ android:layout_height="30dp">
+ </FrameLayout>
+ <Button
+ android:id="@+id/login_button"
+ android:onClick="onClickLogin"
+ android:layout_height="wrap_content"
+ android:text="@string/button_login"
+ android:layout_width="match_parent"
+ android:layout_gravity="center"/>
+ <Button
+ android:id="@+id/cancel_button"
+ android:onClick="onClickCancel"
+ android:gravity="right"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="right"
+ android:text="@string/button_cancel"/>
+
+</LinearLayout>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="right"
+ android:orientation="vertical" >
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/hello" />
+
+</LinearLayout>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="alert_dialog_cancel">
+ Do you really want to close the application?
+ </string>
+ <string name="error_dialog_connection_error">
+ Connection error with MobAd server.
+ </string>
+ <string name="error_dialog_userpwd_error">
+ The username or password you entered is incorrect.
+ </string>
+ <string name="hello">Hello World, Test3Activity!</string>
+ <string name="app_name">Test3</string>
+ <string name="username">Username</string>
+ <string name="password">Password</string>
+ <string name="button_login">Log In</string>
+ <string name="button_ok">OK</string>
+ <string name="button_cancel">Cancel</string>
+</resources>
\ No newline at end of file
--- /dev/null
+package de.android.test3;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.methods.HttpGet;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONTokener;
+
+import android.app.Activity;
+import android.content.Context;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.net.http.AndroidHttpClient;
+import android.os.Bundle;
+import android.util.Log;
+import android.webkit.CookieManager;
+import android.webkit.CookieSyncManager;
+
+public class NextActivity extends Activity {
+ private String myCookie;
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ CookieSyncManager.createInstance(this);
+ myCookie = CookieManager.getInstance().getCookie("192.168.1.34/userfront.php");
+ setContentView(R.layout.nextactivity);
+
+ Criteria criteria = new Criteria();
+ criteria.setAccuracy(Criteria.ACCURACY_FINE);
+ criteria.setAltitudeRequired(false);
+ criteria.setBearingAccuracy(Criteria.NO_REQUIREMENT);
+ criteria.setBearingRequired(false);
+ criteria.setCostAllowed(false);
+ criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH);
+ criteria.setPowerRequirement(Criteria.POWER_MEDIUM);
+ criteria.setSpeedAccuracy(Criteria.ACCURACY_LOW);
+ criteria.setSpeedRequired(true);
+ criteria.setVerticalAccuracy(Criteria.NO_REQUIREMENT);
+
+
+ // Acquire a reference to the system Location Manager
+ LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
+
+ // Define a listener that responds to location updates
+ LocationListener locationListener = new LocationListener() {
+ public void onLocationChanged(Location location) {
+ // Called when a new location is found by the network location provider.
+ makeUseOfNewLocation(location);
+ }
+
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ //1. Fin out the provider state. (see Copilot.java code GPSLocationListener)
+ //2. If it is TEMPORARILY_UNAVAILABLE:
+ //2.1. locationManager.removeUpdates(locationListener); <--- Stop wasting GPS or GSM connections
+ //2.2. Launch Timer with TimerTask 30 or 60 seconds before to enable the locationManager to find out if the provider status changed.
+ //3. If OUT_OF_SERVICE
+ //3.1. locationManager.removeUpdates(locationListener); <--- Stop wasting GPS or GSM connections
+ //3.2. Launch Timer with TimerTask 30 or 60 seconds before to enable the locationManager to find out if the provider status changed.
+ //4. If AVAILABLE
+ // Nothing to do here.
+ //Just when we are in the second or third point we have to stop draining battery because it is useless.
+
+ }
+
+ public void onProviderEnabled(String provider) {}
+
+ public void onProviderDisabled(String provider) {}
+ };
+
+ // Register the listener with the Location Manager to receive location updates
+ locationManager.requestLocationUpdates(0, 10, criteria, locationListener, null);
+ }
+
+ public void makeUseOfNewLocation(Location location) {
+ final MobieAdHttpClient webServiceConnection;
+ final ExecutorService exec = Executors.newSingleThreadExecutor();
+
+ String latitude = Double.toString(location.getLatitude());
+ String longitude = Double.toString(location.getLongitude());
+ String latitudeReplace = latitude.replace(".", ",");
+ String longitudeReplace = longitude.replace(".", ",");
+ final String URLAuth = "http://192.168.1.34/userfront.php/api/" + latitudeReplace + "/" + longitudeReplace + "/gpsads.json";
+ URL url = null;
+
+ try {
+ //RESTful WebService
+ url = new URL(URLAuth);
+ } catch (MalformedURLException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ webServiceConnection = new MobieAdHttpClient(this.myCookie, url);
+ exec.execute(webServiceConnection);
+ }
+
+
+ private class MobieAdHttpClient implements Runnable
+ {
+ private final String cookie;
+ private static final String TAG = "MobieAdHttpClient";
+ private AndroidHttpClient httpClient;
+ private final URL url;
+
+ public MobieAdHttpClient(final String cookie, final URL url) {
+ this.cookie = cookie;
+ this.url = url;
+ }
+
+ @Override
+ public void run()
+ {
+ final String USERAGENT ="MobieAds/1.0";
+ final HttpGet httpGet = new HttpGet();
+ HttpResponse httpResponse = null;
+
+ httpGet.setHeader("Cookie", this.cookie);
+ try {
+ httpGet.setURI(url.toURI());
+ } catch (URISyntaxException e) {
+ Log.e(TAG, "Error while creating URI from URL.", e);
+ }
+ this.httpClient = AndroidHttpClient.newInstance(USERAGENT);
+ try {
+ httpResponse = httpClient.execute(httpGet);
+ } catch (ClientProtocolException e) {
+ Log.e(TAG, "Error while executing HTTP client connection.", e);
+ } catch (IOException e) {
+ Log.e(TAG, "Error while executing HTTP client connection.", e);
+ }
+
+ this.httpClient.close();
+ //It should not be null anyway this check is not harmful
+ if (httpResponse != null)
+ {
+ switch (httpResponse.getStatusLine().getStatusCode()) {
+ case HttpStatus.SC_OK:
+ //OK
+ try {
+ BufferedReader reader = new BufferedReader(
+ new InputStreamReader(httpResponse.getEntity().getContent(), "UTF-8"));
+ StringBuilder builder = new StringBuilder();
+ String currentLine = null;
+ currentLine = reader.readLine();
+ while (currentLine != null) {
+ builder.append(currentLine).append("\n");
+ currentLine = reader.readLine();
+ }
+ JSONTokener tokener = new JSONTokener(builder.toString());
+ JSONArray finalResult = new JSONArray(tokener);
+ } catch (UnsupportedEncodingException e) {
+ Log.e(TAG, "Error while parsing the JSON response from the RESTful Web Service.", e);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Error while parsing the JSON response from the RESTful Web Service.", e);
+ } catch (IOException e) {
+ Log.e(TAG, "Error while parsing the JSON response from the RESTful Web Service.", e);
+ } catch (JSONException e) {
+ Log.e(TAG, "Error while parsing the JSON response from the RESTful Web Service.", e);
+ }
+ break;
+ case HttpStatus.SC_UNAUTHORIZED:
+ //ERROR IN USERNAME OR PASSWORD
+ break;
+ case HttpStatus.SC_BAD_REQUEST:
+ //WHAT THE HECK ARE YOU DOING?
+ break;
+ default:
+ Log.e(TAG, "Error while retrieving the HTTP status line.");
+ break;
+ }
+ }
+ }
+ }
+}
--- /dev/null
+package de.android.test3;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicNameValuePair;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.StrictMode;
+import android.util.Log;
+import android.view.View;
+import android.webkit.CookieManager;
+import android.webkit.CookieSyncManager;
+import android.widget.EditText;
+
+public class Test3Activity extends Activity {
+ private static final String TAG = "Test3Activity";
+ private StrictMode.ThreadPolicy currentPolicy;
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ CookieSyncManager.createInstance(this);
+ currentPolicy = StrictMode.getThreadPolicy();
+ StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.LAX);
+ setContentView(R.layout.main);
+ }
+
+ public void onClickLogin(View v) {
+ final String URLAuth = "http://192.168.1.34/userfront.php/api/login/auth.json";
+ final EditText password = (EditText) findViewById(R.id.password);
+ final EditText username = (EditText) findViewById(R.id.username);
+ final HttpClient httpClient = new DefaultHttpClient();
+ final HttpPost httpPost = new HttpPost(URLAuth);
+ HttpEntity httpEntity = null;
+ HttpResponse httpResponse = null;
+ final List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
+
+ //TODO: RESTful Web Service must use JSON instead of signin array :(
+ nameValuePairs.add(new BasicNameValuePair("signin[username]", username.getText().toString()));
+ nameValuePairs.add(new BasicNameValuePair("signin[password]", password.getText().toString()));
+ try {
+ httpEntity = new UrlEncodedFormEntity(nameValuePairs);
+ } catch (UnsupportedEncodingException e) {
+ Log.e(TAG, "Error while encoding POST parameters.", e);
+ return;
+ }
+ httpPost.setEntity(httpEntity);
+ httpPost.setHeader("User-Agent", "MobieAds/1.0");
+
+ try {
+ httpResponse = httpClient.execute(httpPost);
+ } catch (ClientProtocolException e) {
+ Log.e(TAG, "Error while executing HTTP client connection.", e);
+ createErrorDialog(R.string.error_dialog_connection_error);
+ return;
+ } catch (IOException e) {
+ Log.e(TAG, "Error while executing HTTP client connection.", e);
+ createErrorDialog(R.string.error_dialog_connection_error);
+ return;
+ }
+
+ switch (httpResponse.getStatusLine().getStatusCode()) {
+ case HttpStatus.SC_OK:
+ String cookie = httpResponse.getLastHeader("Set-Cookie").getValue();
+ CookieManager.getInstance().setCookie("192.168.1.34/userfront.php",cookie);
+ CookieSyncManager.getInstance().sync();
+ //Go to the next activity
+ StrictMode.setThreadPolicy(currentPolicy);
+ this.startActivity(new Intent(Intent.ACTION_RUN));
+ break;
+ case HttpStatus.SC_UNAUTHORIZED:
+ //Username or password are incorrect
+ createErrorDialog(R.string.error_dialog_userpwd_error);
+ break;
+ case HttpStatus.SC_BAD_REQUEST:
+ //What the heck are you doing?
+ createErrorDialog(R.string.error_dialog_userpwd_error);
+ break;
+ default:
+ Log.e(TAG, "Error while retrieving the HTTP status line.");
+ createErrorDialog(R.string.error_dialog_userpwd_error);
+ break;
+ }
+ }
+
+ public void onClickCancel(View v) {
+ createAlertDialog(R.string.alert_dialog_cancel);
+ }
+
+ void createAlertDialog(int title) {
+ DialogFragment newFragment = AlertDialogFragment.newInstance(title);
+ newFragment.show(getFragmentManager(), "alertDialog");
+ }
+
+ void createErrorDialog(int title) {
+ DialogFragment newFragment = ErrorDialogFragment.newInstance(title);
+ newFragment.show(getFragmentManager(), "errorDialog");
+ }
+
+ public void doPositiveClick() {
+ StrictMode.setThreadPolicy(currentPolicy);
+ finish();
+ }
+
+ public void doNegativeClick() {
+
+ }
+
+
+ public static class AlertDialogFragment extends DialogFragment {
+
+ public static AlertDialogFragment newInstance(int title) {
+ AlertDialogFragment frag = new AlertDialogFragment();
+ Bundle args = new Bundle();
+
+ args.putInt("title", title);
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ int title = getArguments().getInt("title");
+
+ return new AlertDialog.Builder(getActivity())
+ .setIcon(R.drawable.alert_dialog_icon)
+ .setTitle(title)
+ .setPositiveButton(R.string.button_ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ ((Test3Activity)getActivity()).doPositiveClick();
+ }
+ }
+ )
+ .setNegativeButton(R.string.button_cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ ((Test3Activity)getActivity()).doNegativeClick();
+ }
+ }
+ )
+ .create();
+ }
+ }
+
+
+ public static class ErrorDialogFragment extends DialogFragment {
+
+ public static ErrorDialogFragment newInstance(int title) {
+ ErrorDialogFragment frag = new ErrorDialogFragment();
+ Bundle args = new Bundle();
+
+ args.putInt("title", title);
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ int title = getArguments().getInt("title");
+
+ return new AlertDialog.Builder(getActivity())
+ .setIcon(R.drawable.alert_dialog_icon)
+ .setTitle(title)
+ .setPositiveButton(R.string.button_ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+
+ }
+ }
+ )
+ .create();
+ }
+ }
+}
\ No newline at end of file