Fetching the best Location in android

As easy it may look but finding the accurate location of an android device is not straight forward.

Three imporatnt things to consider are :-

1. Lag time in getting the location first time (very important)
2. Available LocationProviders
3. Checking if the new location is accurate or not



Solving point 1:



// let Android select the right location provider for you

bestProvider = locationManager.getBestProvider(myCriteria, true);



//If no provider set as network provider

if (bestProvider == null) {

  bestProvider = locationManager.getProvider(

     LocationManager.NETWORK_PROVIDER).getName();

}




The first painful part is getting your location from your locationProvider the very first time. At times some providers like GPS can take more than few minutes to show results. Therefore always check if your provider is null, if yes, then go for the network provider, since it is the fastest locationProvider. 

This is for fetching the location the first time. Later on you can change the provider as per your functionality.

Solving point 2.

List<String> matchingProviders = locationManager.getAllProviders();
ArrayList<Location> lastLocations = new ArrayList<Location>();

for (String provider: matchingProviders) 
{
  Location location = locationManager.getLastKnownLocation(provider);
  if (location != null) 
  {
    lastLocations.add(location);
  }
}
findBestLocation(lastLocations);


Now once you got your first location, you can check for your best locationProvider as given above.
From every locationProvider fetch last location and compare if it is accurate and best(last line)


Solving point 3.

/** Determines whether one Location reading is better than the current Location fix
 * @param location  The new Location that you want to evaluate
 * @param currentBestLocation  The current Location fix, to which you want to compare the new one
 */
protected boolean isBetterLocation(Location location, Location currentBestLocation) {
   if (currentBestLocation == null) {
       // A new location is always better than no location
       return true;
   }

   // Check whether the new location fix is newer or older
   long timeDelta = location.getTime() - currentBestLocation.getTime();
   boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
   boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
   boolean isNewer = timeDelta > 0;

   // If it's been more than two minutes since the current location, use the new location
   // because the user has likely moved
   if (isSignificantlyNewer) {
       return true;
   // If the new location is more than two minutes older, it must be worse
   } else if (isSignificantlyOlder) {
       return false;
   }

   // Check whether the new location fix is more or less accurate
   int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
   boolean isLessAccurate = accuracyDelta > 0;
   boolean isMoreAccurate = accuracyDelta < 0;
   boolean isSignificantlyLessAccurate = accuracyDelta > 200;

   // Check if the old and new location are from the same provider
   boolean isFromSameProvider = isSameProvider(location.getProvider(),
           currentBestLocation.getProvider());

   // Determine location quality using a combination of timeliness and accuracy
   if (isMoreAccurate) {
       return true;
   } else if (isNewer && !isLessAccurate) {
       return true;
   } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
       return true;
   }
   return false;
}


As given in android dev site, it is always a good idea to compare your new location retrieved in your locationListener with the old location. New location fetched doesn't mean it is accurate. It has to be compared and hence checked if it is better.
The above function does the comparison. You can change it according to your needs

Assigning default values to spinners

When using spinners in android you might have the problem of setting it to a default value before you render it.

The way to go about is :-



ArrayAdapter<String> spinnerAdap = (ArrayAdapter<String>) mySpinner.getAdapter();
int spinnerPosition = messgAdap.getPosition(theDefaultValue);
mySpinner.setSelection(spinnerPosition);



Example: 

1.Your spinner has values from 1,2,3...10 as a String ArrayList. To set its default value to 3 you have to fetch the position of "3" as above.

2.You fetch the adapter from the spinner and get the position of the "default" value, "3" here, from the adapter.

2. Set the spinner to position of "3"

Passing data in Android using sharedPreferences



For sharing simple data types between Activities or services we can make use or sharedPreferences , in android.

SharedPreferences or preferences are useful for simple dataTypes, where they are stored as Key/Values pairs in a xml file.

The below example will have two simple activities. One for input and the other for output display. We will shared data between the two activities using sharedPreferences.


NOTE: Used android 2.3.3 SDK


Project structure :






1. AndroidManifest.xml



1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.app"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="10" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".OutputActivity"
            android:label="Output screen"/>
    </application>

</manifest>



The manifest file has only two activities. One for taking user input and another for displaying the output.


2. main.xml (main layout)



1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?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:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Enter the values" />

    <EditText
        android:id="@+id/valueOne"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10" >
        <requestFocus />
    </EditText>

    <EditText
        android:id="@+id/valueTwo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10" />

    <EditText
        android:id="@+id/valueThree"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10" />

    <EditText
        android:id="@+id/valueFour"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10" />

    <Button
        android:id="@+id/buttonID"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/submit" />

</LinearLayout>



The main.xml layout has four input fields and a submit button. Each field will be stored using sharedPreferences and then later used in the OutputActivity.


3. output.xml (layout)



 1

 2

 3

 4

 5

 6

 7

 8

 9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical" >



    <TextView

        android:id="@+id/textView1"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="Output Private"

        android:textAppearance="?android:attr/textAppearanceMedium" />



    <TextView

        android:id="@+id/privateTxt"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:textAppearance="?android:attr/textAppearanceMedium" />



    <TextView

        android:id="@+id/textView2"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="Output world read"

        android:textAppearance="?android:attr/textAppearanceMedium" />



    <TextView

        android:id="@+id/readTxt"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:textAppearance="?android:attr/textAppearanceMedium" />



    <TextView

        android:id="@+id/textView3"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="Output world write"

        android:textAppearance="?android:attr/textAppearanceMedium" />



    <TextView

        android:id="@+id/writeTxt"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:textAppearance="?android:attr/textAppearanceMedium" />



    <TextView

        android:id="@+id/textView4"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="Output world read/write"

        android:textAppearance="?android:attr/textAppearanceMedium" />



    <TextView

        android:id="@+id/readWriteTxt"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:textAppearance="?android:attr/textAppearanceMedium" />



</LinearLayout>




The output.xml layout has four textView fields. These four fields will display the four values shared by MainActivity using sharedPreferences.


4. MainActivity.java




  1

  2

  3

  4

  5

  6

  7

  8

  9

 10

 11

 12

 13

 14

 15

 16

 17

 18

 19

 20

 21

 22

 23

 24

 25

 26

 27

 28

 29

 30

 31

 32

 33

 34

 35

 36

 37

 38

 39

 40

 41

 42

 43

 44

 45

 46

 47

 48

 49

 50

 51

 52

 53

 54

 55

 56

 57

 58

 59

 60

 61

 62

 63

 64

 65

 66

 67

 68

 69

 70

 71

 72

 73

 74

 75

 76

 77

 78

 79

 80

 81

 82

 83

 84

 85

 86

 87

 88

 89

 90

 91

 92

 93

 94

 95

 96

 97

 98

 99

100

101

102

103

104

105

106
package com.example.app;



import android.app.Activity;

import android.content.Context;

import android.content.Intent;

import android.content.SharedPreferences;

import android.content.SharedPreferences.Editor;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;





/**

 * 

 *  Activity class which demos the use of sharedPreferences for sharing data among different Activities 

 * 

 * @author Akshat

 *

 */

public class MainActivity extends Activity

{

 

 

 public static final String PREFS_PRIVATE = "PREFS_PRIVATE";

 public static final String PREFS_WORLD_READ = "PREFS_WORLD_READABLE";

 public static final String PREFS_WORLD_WRITE = "PREFS_WORLD_WRITABLE";

 public static final String PREFS_WORLD_READ_WRITE = "PREFS_WORLD_READABLE_WRITABLE";

 public static final String KEY_PRIVATE = "KEY_PRIVATE";

 public static final String KEY_WORLD_READ = "KEY_WORLD_READ";

 public static final String KEY_WORLD_WRITE = "KEY_WORLD_WRITE";

 public static final String KEY_WORLD_READ_WRITE = "KEY_WORLD_READ_WRITE";

 

 

 private SharedPreferences prefsPrivate;

 private SharedPreferences prefsWorldRead;

 private SharedPreferences prefsWorldWrite;

 private SharedPreferences prefsWorldReadWrite;

 

 

 

 

    /** Called when the activity is first created. */

    @Override

    public void onCreate(Bundle savedInstanceState)

    {

        super.onCreate(savedInstanceState);

       

        //Set the view for this activity to main.xml

        setContentView(R.layout.main);

       

        //Get hold of the submit button

        Button button = (Button) findViewById(R.id.buttonID);

      

        //Listener added to the submit button

        button.setOnClickListener(new Button.OnClickListener(){



   @Override

   public void onClick(final View v)

   {

    

    //Get the input values

    EditText valueOneTxt = (EditText) findViewById(R.id.valueOne);

    EditText valueTwoTxt = (EditText) findViewById(R.id.valueTwo);

    EditText valueThreeTxt = (EditText) findViewById(R.id.valueThree);

    EditText valueFourTxt = (EditText) findViewById(R.id.valueFour);

    

    

    // Create shared sharedPreferences

    // We created four different sharedPreferences with different modes

    prefsPrivate = getSharedPreferences(MainActivity.PREFS_PRIVATE, Context.MODE_PRIVATE);

    prefsWorldRead = getSharedPreferences(MainActivity.PREFS_WORLD_READ, Context.MODE_WORLD_READABLE);

    prefsWorldWrite = getSharedPreferences(MainActivity.PREFS_WORLD_WRITE, Context.MODE_WORLD_WRITEABLE);

    prefsWorldReadWrite = getSharedPreferences(MainActivity.PREFS_WORLD_READ_WRITE, Context.MODE_WORLD_READABLE+Context.MODE_WORLD_WRITEABLE);

    

    

    // To add values to the sharedPreferences, we get editors from the sharedPreferences

    Editor privateEdit = prefsPrivate.edit();

    Editor worldReadEdit = prefsWorldRead.edit();

    Editor worldWriteEdit = prefsWorldWrite.edit();

    Editor worldReadWriteEdit = prefsWorldReadWrite.edit();

    

    

    

    // With the Editor you can set String, boolean, float, int, and long types as key/value pairs

    // We save the four input values into the four different sharedPreferences

    privateEdit.putString(MainActivity.KEY_PRIVATE, valueOneTxt.getText().toString());

    worldReadEdit.putString(MainActivity.KEY_WORLD_READ, valueTwoTxt.getText().toString());

    worldWriteEdit.putString(MainActivity.KEY_WORLD_WRITE, valueThreeTxt.getText().toString());

    worldReadWriteEdit.putString(MainActivity.KEY_WORLD_READ_WRITE, valueFourTxt.getText().toString());

    

    //Commit the sharedPreferences into a file

    privateEdit.commit();

    worldReadEdit.commit();

    worldWriteEdit.commit();

    worldReadWriteEdit.commit();

    

    //Start another activity by passing an explicit intent

    Intent intent = new Intent(MainActivity.this, OutputActivity.class);    

    startActivity(intent);

   }

         

        });

      

    }

}




5. OutputActivity.java


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.example.app;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.TextView;
public class OutputActivity extends Activity
{
private SharedPreferences prefsPrivate;
private SharedPreferences prefsWorldRead;
private SharedPreferences prefsWorldWrite;
private SharedPreferences prefsWorldReadWrite;
@Override
protected void onCreate(Bundle savedInstanceState) 
{
super.onCreate(savedInstanceState);
//Set the view for this activity to output.xml
setContentView(R.layout.output);
// Get hold of sharedPreferences
prefsPrivate = getSharedPreferences(MainActivity.PREFS_PRIVATE, Context.MODE_PRIVATE);
prefsWorldRead = getSharedPreferences(MainActivity.PREFS_WORLD_READ, Context.MODE_WORLD_READABLE);
prefsWorldWrite = getSharedPreferences(MainActivity.PREFS_WORLD_WRITE, Context.MODE_WORLD_WRITEABLE);
prefsWorldReadWrite = getSharedPreferences(MainActivity.PREFS_WORLD_READ_WRITE, Context.MODE_WORLD_READABLE+Context.MODE_WORLD_WRITEABLE);
//Get hold of output textview - to display values
TextView privateTxt = (TextView) findViewById(R.id.privateTxt);
TextView readTxt = (TextView) findViewById(R.id.readTxt);
TextView writeTxt = (TextView) findViewById(R.id.writeTxt);
TextView readWriteTxt = (TextView) findViewById(R.id.readWriteTxt);
// Set values form sharedPreferences to the textViews
privateTxt.setText(prefsPrivate.getString(MainActivity.KEY_PRIVATE, "NA"));
readTxt.setText(prefsWorldRead.getString(MainActivity.KEY_WORLD_READ, "NA"));
writeTxt.setText(prefsWorldWrite.getString(MainActivity.KEY_WORLD_WRITE, "NA"));
readWriteTxt.setText(prefsWorldReadWrite.getString(MainActivity.KEY_WORLD_READ_WRITE, "NA"));
}
}




When the app is run, sharedPreferences xml files are created under /data/data/PACKAGE_NAME/



-----------------------------------------------------------------------------------------------------------


Screenshots:

MainActivity






OutputActivity




HTTP post from an android application using URLConnection



A simple android application which makes a HTTP POST request to a servlet.

The comments in the source are quite self explanatory. Leave a feedback for any clarifications.


SDK version used : 2.3.3

The project structure :







1. AndroidManifest.xml





<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.httpapp"

    android:versionCode="1"

    android:versionName="1.0" >



    <uses-sdk android:minSdkVersion="10" />

    <uses-permission android:name="android.permission.INTERNET"/>



    <application

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name" >

        <activity

            android:name=".HttpAppActivity"

            android:label="@string/app_name" >

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />



                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

    </application>



</manifest>





We have to add the permission for internet access.


2. main.xml


This is your layout for your main activity



<?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:orientation="vertical" >





    <TextView

        android:id="@+id/textView1"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="@string/firstname"

        android:textAppearance="?android:attr/textAppearanceLarge" />







    <EditText

        android:id="@+id/fnameID"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:ems="10" />





    <TextView

        android:id="@+id/textView4"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="@string/lastname"

        android:textAppearance="?android:attr/textAppearanceLarge" />





    <EditText

        android:id="@+id/lnameID"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:ems="10" >



        <requestFocus />

    </EditText>



    <Button

        android:id="@+id/submitID"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="@string/submit" />







    <TextView

        android:id="@+id/resultID"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_weight="0.41" />



</LinearLayout>




3. strings.xml


<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="hello">Hello World</string>
    <string name="app_name">HTTP app</string>
    <string name="submit">submit</string>
    <string name="firstname">First name</string>
    <string name="lastname">Last name</string>

</resources>



4. HttpAppActivity.java






package com.httpapp;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class HttpAppActivity extends Activity {
    
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        // Get the submit button and attach a listener to its click event
        Button submitBtn = (Button)findViewById(R.id.submitID);
        submitBtn.setOnClickListener(new Button.OnClickListener(){

            public void onClick(View v)
            {
           
            EditText fnameText = (EditText)findViewById(R.id.fnameID); // Get a handle to the input box for first name
            EditText lnameText = (EditText)findViewById(R.id.lnameID); // Get a handle to the input box for last name
           
           
           
            //Make a call to our helper class
            String result = ServletInterface.executeHttpRequest(fnameText.getText().toString(), lnameText.getText().toString());
           
           
           
            TextView resultView = (TextView) findViewById(R.id.resultID); // Get the textView where we will display the result
            resultView.setText(result); //set the returned result to the view
            }
        });
            
    }

}




5. ServletInterface.java







package com.httpapp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;

import android.util.Log;

/**
*  This class is the interface for making calls to a Servlet in a remote server
*
*/
public class ServletInterface 
{
public static final String SERVER_URL = "http://10.0.2.2:8080"; // The server(Servlet) which will process the request. Note 10.0.2.2 is the localhost IP for emulator
    public static final String CHAR_SET = "UTF-8"; // Encoding used for the parameters

  
    
    public static String executeHttpRequest(String fname, String lname) 
    {
   
    OutputStream output = null;
    String response = ""; 
   
    try 
    {
    Log.i("HTTP :", "Preparing data");
    // ------------------------------------------------------START: PREPARE CONNETION AND REQUEST ------------------------------- //
   
    // Prepare the data string  [ firstName=JOHN&lastName=SMITH ]
String data = URLEncoder.encode("firstName", CHAR_SET) + "=" + URLEncoder.encode(fname, CHAR_SET);
data += "&" + URLEncoder.encode("lastName", CHAR_SET) + "=" + URLEncoder.encode(lname, CHAR_SET);
URLConnection connection = new URL(SERVER_URL).openConnection(); // Create a connection to server using URL
connection.setDoOutput(true); // This means POST method to be used
connection.setRequestProperty("Accept-Charset", CHAR_SET); //For servers to know what encoding is used for the parameters
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + CHAR_SET);
output = null; 
output = connection.getOutputStream(); //open a Output stream from the connection for posting data
   output.write(data.getBytes(CHAR_SET)); //Post data
   
   //------------------------------------------------------ END: PREPARE CONNETION AND REQUEST --------------------------------- //
   
   
   Log.i("HTTP :", "sending data");
   InputStream responseStream = connection.getInputStream(); //This is when the request is actually fired
   
   
   
 //------------------------------------------------------ START: READ RESPONSE ------------------------------------------------ //
   
   Log.i("HTTP :", "Reading response");
            BufferedReader rd = new BufferedReader(new InputStreamReader(responseStream,CHAR_SET)); // Connect a BufferReader to the inputStream
            String line = null;
            
            while ((line = rd.readLine()) != null) // Read the response line-by-line from the bufferedReader
            {
               response += line;
            }
   
 //------------------------------------------------------ END: READ RESPONSE ------------------------------------------------- //
   
   

}
    catch (UnsupportedEncodingException e) 
    {
e.printStackTrace();
}
    catch(IOException io)
    {
    //Log and check exp
    }
    finally
    {
    if (output != null) try { output.close(); } catch (IOException ignoreIO) {}
    }
   
   
return response;
   
    }
}




= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =


At the servlet end you can read the parameters using :



String fname = (String) request.getParameter("firstName");

String lname = (String) request.getParameter("lastName");