Saturday, October 15, 2011

Share on Twitter Integration in Android application

 The last few months were so busy for me learning more about android development and best practice. I can say i learned a lot while developing two android application that covered almost many aspect from handling Intents, Services, BroadcastReciever, Activities and many other android components.

In this post, I am sharing how I made a pretty friendly share on twitter interface that can be used to send tweets via your android application.
For that purpose, we are going to use Twitter4J which is one of the 3 Twitter API libraries ( Scribe, Twitter API ME).
But before we start with the android coding, we need to create a new application on twitter. Really easy to do,  go to https://dev.twitter.com/apps/new and fill in the form. Don't forget to put in the 'Callback URL', for instant put anything like http://myAndroidApp.dev . It is really important to put a Callback URL so twitter can identify your application as a Browser Application which we are going to need in our android project. The Callback URL that the application is going to use will be defined in the callback URL that we’ll pass on when authenticating the user.

After setting up the Twitter application, we move to our android application.
We are going to need the following libraries :
 Now lets start some coding! First we make the XML for the tweet screen like the following :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent" android:background="@color/cian">
    <android.webkit.WebView android:id="@+id/web_view"
        android:layout_width="fill_parent" android:layout_height="fill_parent"
        android:visibility="gone" />
    <LinearLayout android:layout_centerInParent="true" android:id="@+id/progress_spinner"
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:background="@drawable/popup_frame" android:visibility="gone"
        android:orientation="horizontal">
        <ProgressBar android:layout_width="50.0dip"
            android:layout_height="50.0dip" android:indeterminate="true"
            style="\?android:attr/progressBarStyleInverse" />
        <TextView android:text="@string/progress_loading"
            android:layout_width="wrap_content" android:layout_height="wrap_content" 
            android:layout_gravity="center" />
    </LinearLayout>
    <ImageView android:layout_width="wrap_content" android:id="@+id/twitter_logo"
        android:layout_height="wrap_content" android:src="@drawable/twitter_dialog_title" />
    <EditText android:id="@+id/textStatus" android:typeface="serif"
        android:maxLength="160" android:layout_width="fill_parent"
        android:layout_margin="5px" android:layout_height="wrap_content"
        android:padding="5px" android:minLines="5" android:layout_below="@+id/twitter_logo"/>
    <ImageButton android:layout_height="wrap_content"
        android:id="@+id/btn_sendTweet" android:background="@drawable/btn_tweet"
        android:text="Tweet" android:layout_width="wrap_content"
        android:layout_centerHorizontal="true" android:layout_below="@+id/textStatus"/>
</RelativeLayout>

the layout will look like this, you can customize that layout as you like.
The webView is set to visibility "gone" as we will need it in our activity to get the authorization from the user to post tweets.
So, when we want to send a tweet, that screen will appear as a Dilog, then we can fill in the tweet text manually or grammatically. when clicking the Tweet button, we need to check if the user is authenticated. We can use a code like this :
public static boolean isAuthenticated(SharedPreferences prefs) {

        ACCESS_KEY = prefs.getString("TWITTER_ACCESS_KEY", "none");
        ACCESS_SECRET = prefs.getString("TWITTER_ACCESS_SECRET", "none");

        try {
            AccessToken accessToken = new AccessToken(ACCESS_KEY, ACCESS_SECRET);
            Twitter twitter = new TwitterFactory().getInstance();
            twitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
            twitter.setOAuthAccessToken(accessToken);

            twitter.getAccountSettings();
            Log.d(TAG, twitter.getScreenName());
            return true;
        } catch (TwitterException e) {
            return false;
        } catch (Exception e) {
            Log.e(TAG, e.getMessage(), e.getCause());
            return false;
        }
    }
As access token provided by twitter never expire, we can store it in the SharedPreferences and reuse it with same user again and again. When the method returns true, that means that the user is authenticated and we can begin the sending the tweet process, we use this function for that :
public static void sendTweet(SharedPreferences prefs, String msg)
            throws TwitterException, Exception {

        ACCESS_KEY = prefs.getString("TWITTER_ACCESS_KEY", "");
        ACCESS_SECRET = prefs.getString("TWITTER_ACCESS_SECRET", "");
        AccessToken accessToken = new AccessToken(ACCESS_KEY, ACCESS_SECRET);
        Twitter twitter = new TwitterFactory().getInstance();
        twitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
        twitter.setOAuthAccessToken(accessToken);
        twitter.updateStatus(msg);
}

If the user is not authenticated or didn't grant access to our application, we gotta acquire an authentication url as first step, something like this :
    private String getAuthUrl() {
        String authUrl = null;
        try {
            authUrl = httpOauthprovider.retrieveRequestToken(httpOauthConsumer, CALLBACK_URL);
        } catch (OAuthMessageSignerException e) {
            Log.d(TAG, e.getMessage(), e.fillInStackTrace());
        } catch (OAuthNotAuthorizedException e) {
            Log.d(TAG, e.getMessage(), e.fillInStackTrace());
        } catch (OAuthExpectationFailedException e) {
            Log.d(TAG, e.getMessage(), e.fillInStackTrace());
        } catch (OAuthCommunicationException e) {
            Log.d(TAG, e.getMessage(), e.fillInStackTrace());
        }
        return authUrl;
    }
now, here is the tricky part. We are going to use the webView that we made in our layout file. Nothing better than the code to explain the next step. :
private void doOauth() { //init the web view
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setVisibility(View.VISIBLE);
        webView.bringToFront();
        webView.requestFocus(View.FOCUS_DOWN);
        String authUrl = getAuthUrl();
        if (authUrl != null) { //if the auth url is valid, we proceed
            webView.setWebViewClient(new WebViewClient() {
                @Override
                public void onPageStarted(WebView view, String url,
                        Bitmap bitmap) { 
                    mSpinner.bringToFront();
                    mSpinner.setVisibility(View.VISIBLE);
                }
                @Override
                public void onReceivedSslError(WebView view,
                        SslErrorHandler handler, SslError error) { // bypass the SSl check
                    handler.proceed(); // the default behavior of the WebView is to block the access                    
                }
                @Override
                public void onPageFinished(WebView view, String url) {
                    mSpinner.setVisibility(View.GONE);
                    webView.bringToFront();
                    if (url.startsWith(CALLBACK_URL)) { // the user is authenticated and we are being redirected 
                     
                        if (url.indexOf("oauth_token=") != -1) {
                            
                            String verifier = extractParamFromUrl(url, OAuth.OAUTH_VERIFIER);
                            try {

                                httpOauthprovider.retrieveAccessToken(httpOauthConsumer, verifier);
                                ACCESS_KEY = httpOauthConsumer.getToken();
                                ACCESS_SECRET = httpOauthConsumer.getTokenSecret();
                                SharedPreferences.Editor ed = mPrefs.edit();
                                ed.putString("TWITTER_ACCESS_KEY",ACCESS_KEY);
                                ed.putString("TWITTER_ACCESS_SECRET", ACCESS_SECRET);
                                ed.commit();
                                
                                view.setVisibility(View.INVISIBLE);
                                updateStatus();

                            } catch (OAuthMessageSignerException e) {
                                Log.d(TAG, e.getMessage(), e.fillInStackTrace());
                            } catch (OAuthNotAuthorizedException e) {
                                Log.d(TAG, e.getMessage(), e.fillInStackTrace());
                            } catch (OAuthExpectationFailedException e) {
                                Log.d(TAG, e.getMessage(), e.fillInStackTrace());
                            } catch (OAuthCommunicationException e) {
                                Log.d(TAG, e.getMessage(), e.fillInStackTrace());
                            }
                        } else if (url.indexOf("error=") != -1) {
                            view.setVisibility(View.GONE);
                            Toast.makeText(getBaseContext(), R.string.toast_twitter_error,Toast.LENGTH_LONG).show();
                        }
                    }
                }

                private String extractParamFromUrl(String url, String paramName) {
                    String queryString = url.substring(url.indexOf("?", 0) + 1,
                            url.length());
                    QueryStringParser queryStringParser = new QueryStringParser(
                            queryString);
                    return queryStringParser.getQueryParamValue(paramName);
                }
            });
            webView.loadUrl(authUrl);
        } else {
            webView.setVisibility(View.GONE);
            Toast.makeText(getBaseContext(), R.string.toast_twitter_error,Toast.LENGTH_LONG).show();
        }
    }

Ok,As you can see, we are using 0Auth to get all the needed tokens, httpOauthConsumer and httpOauthprovider are declared on our activity like the following:
private static CommonsHttpOAuthConsumer httpOauthConsumer = new CommonsHttpOAuthConsumer(
            TwitterUtils.CONSUMER_KEY, TwitterUtils.CONSUMER_SECRET);
    private static CommonsHttpOAuthProvider httpOauthprovider = new CommonsHttpOAuthProvider(
            REQUEST_URL, ACCESS_TOKEN_URL, AUTH_URL);
all the tokens are handled withing the web view, when the user authorize the application, it will follow him to the provided Callback URL, the one we set when acquiring the authentification Url
authUrl = httpOauthprovider.retrieveRequestToken(httpOauthConsumer, CALLBACK_URL);
the CALLBACK_URL will be something like this "myAndroidApp://twitterOauth"

When the user is not authenticated or didn't authorize the application, the AuthURL will direct us to the Twitter authentication page which look like the following :

When the user name and password are accepted, the Twitter will redirect the user to the CALLBACK_URL that we set earlier, this screen will be shown then :
When we detect the Callback url in the webView, we store the OAuth tokens (ACCESS_KEY & ACCESS_SECRET) that we are using with the Twitter API.
Now we are ready to send the tweet and close the activity to get back to our android application. That will be it, not so hard !!
Here are the full TwitterConnector Activity and the TwitterUtils Class, enjoy.

TwitterUtils.java
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.auth.AccessToken;
import android.content.SharedPreferences;
import android.util.Log;

public class TwitterUtils {

    public final static String CONSUMER_KEY = "YOUR_TWITTER_APP_CONSUMER_KEY";
    public final static String CONSUMER_SECRET = "YOUR_TWITTER_APP_CONSUMER_SECRET";
    private static final String TAG = "Twitter";

    private static String ACCESS_KEY;
    private static String ACCESS_SECRET;

    public static boolean isAuthenticated(SharedPreferences prefs) {

        ACCESS_KEY = prefs.getString("TWITTER_ACCESS_KEY", "none");
        ACCESS_SECRET = prefs.getString("TWITTER_ACCESS_SECRET", "none");

        try {
            AccessToken accessToken = new AccessToken(ACCESS_KEY, ACCESS_SECRET);
            Twitter twitter = new TwitterFactory().getInstance();
            twitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
            twitter.setOAuthAccessToken(accessToken);

            twitter.getAccountSettings();
            Log.d(TAG, twitter.getScreenName());
            return true;
        } catch (TwitterException e) {
            return false;
        } catch (Exception e) {
            Log.e(TAG, e.getMessage(), e.getCause());
            return false;
        }
    }

    public static void sendTweet(SharedPreferences prefs, String msg)
            throws TwitterException, Exception {

        ACCESS_KEY = prefs.getString("TWITTER_ACCESS_KEY", "");
        ACCESS_SECRET = prefs.getString("TWITTER_ACCESS_SECRET", "");
        AccessToken accessToken = new AccessToken(ACCESS_KEY, ACCESS_SECRET);
        Twitter twitter = new TwitterFactory().getInstance();
        twitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
        twitter.setOAuthAccessToken(accessToken);
        twitter.updateStatus(msg);
    }
}

TwitterConnector.java
import java.io.IOException;

import oauth.signpost.OAuth;
import oauth.signpost.commonshttp.CommonsHttpOAuthConsumer;
import oauth.signpost.commonshttp.CommonsHttpOAuthProvider;
import oauth.signpost.exception.OAuthCommunicationException;
import oauth.signpost.exception.OAuthExpectationFailedException;
import oauth.signpost.exception.OAuthMessageSignerException;
import oauth.signpost.exception.OAuthNotAuthorizedException;
import twitter4j.TwitterException;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.webkit.SslErrorHandler;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.Toast;


public class TwitterConnector extends Activity {
    
    private static final String REQUEST_URL = "http://twitter.com/oauth/request_token";
    private static final String ACCESS_TOKEN_URL = "http://twitter.com/oauth/access_token";
    private static final String AUTH_URL = "http://twitter.com/oauth/authorize";
    private static final String CALLBACK_URL = "myAndroidApp://twitterOauth";
    private static final String TAG = "Twitter";

    private static CommonsHttpOAuthConsumer httpOauthConsumer = new CommonsHttpOAuthConsumer(
            TwitterUtils.CONSUMER_KEY, TwitterUtils.CONSUMER_SECRET);
    private static CommonsHttpOAuthProvider httpOauthprovider = new CommonsHttpOAuthProvider(
            REQUEST_URL, ACCESS_TOKEN_URL, AUTH_URL);
    
    private SharedPreferences mPrefs;

    private String tweet;
    private String url;
    private String title;
    private static String ACCESS_KEY;
    private static String ACCESS_SECRET;

    private EditText tweetEditText;
    private ImageButton sendTweetButton;
    private WebView webView;
    private LinearLayout mSpinner;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        
        setContentView(R.layout.twitter_share);
        mSpinner = (LinearLayout)findViewById(R.id.progress_spinner);
        webView = (WebView)findViewById(R.id.web_view);
        url = getIntent().getStringExtra("URL");
        title = getIntent().getStringExtra("TITLE");
        
        int tweetLength = url.length() + title.length() + 3;

        try {
            tweet = title + " : " + TinyURL.shorter(url);
        } catch (IOException e) {
            tweet = title
                    .substring(0, title.length() - (tweetLength - 160 - 1))
                    + " : " + url ;
        }

        tweetEditText = (EditText) findViewById(R.id.textStatus);
        tweetEditText.setText(tweet);

        mPrefs = getSharedPreferences("MA", Context.MODE_PRIVATE);

        sendTweetButton = (ImageButton) findViewById(R.id.btn_sendTweet);
        sendTweetButton.setOnClickListener(sendTweetClickListener);
    }

    @Override
    protected void onResume() {
        super.onResume();
    }
    
    private OnClickListener sendTweetClickListener = new OnClickListener() {

        @Override
        public void onClick(View v) {
            
            if (TwitterUtils.isAuthenticated(getSharedPreferences("MA", Context.MODE_PRIVATE))) {
                updateStatus();
            } else {
                doOauth();
            }
        }
    };

    private void doOauth() {
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setVisibility(View.VISIBLE);
        webView.bringToFront();
        webView.requestFocus(View.FOCUS_DOWN);
        String authUrl = getAuthUrl();
        if (authUrl != null) {
            webView.setWebViewClient(new WebViewClient() {
                @Override
                public void onPageStarted(WebView view, String url,
                        Bitmap bitmap) {
                    mSpinner.bringToFront();
                    mSpinner.setVisibility(View.VISIBLE);
                }
                @Override
                public void onReceivedSslError(WebView view,
                        SslErrorHandler handler, SslError error) {
                    handler.proceed();                    
                }
                @Override
                public void onPageFinished(WebView view, String url) {
                    mSpinner.setVisibility(View.GONE);
                    webView.bringToFront();
                    if (url.startsWith(CALLBACK_URL)) {

                        if (url.indexOf("oauth_token=") != -1) {
                            
                            String verifier = extractParamFromUrl(url, OAuth.OAUTH_VERIFIER);
                            try {

                                httpOauthprovider.retrieveAccessToken(httpOauthConsumer, verifier);
                                ACCESS_KEY = httpOauthConsumer.getToken();
                                ACCESS_SECRET = httpOauthConsumer.getTokenSecret();
                                SharedPreferences.Editor ed = mPrefs.edit();
                                ed.putString("TWITTER_ACCESS_KEY",ACCESS_KEY);
                                ed.putString("TWITTER_ACCESS_SECRET", ACCESS_SECRET);
                                ed.commit();
                                
                                view.setVisibility(View.INVISIBLE);
                                updateStatus();

                            } catch (OAuthMessageSignerException e) {
                                Log.d(TAG, e.getMessage(), e.fillInStackTrace());
                            } catch (OAuthNotAuthorizedException e) {
                                Log.d(TAG, e.getMessage(), e.fillInStackTrace());
                            } catch (OAuthExpectationFailedException e) {
                                Log.d(TAG, e.getMessage(), e.fillInStackTrace());
                            } catch (OAuthCommunicationException e) {
                                Log.d(TAG, e.getMessage(), e.fillInStackTrace());
                            }
                        } else if (url.indexOf("error=") != -1) {
                            view.setVisibility(View.GONE);
                            Toast.makeText(getBaseContext(), R.string.toast_twitter_error,Toast.LENGTH_LONG).show();
                        }
                    }
                }

                private String extractParamFromUrl(String url, String paramName) {
                    String queryString = url.substring(url.indexOf("?", 0) + 1,
                            url.length());
                    QueryStringParser queryStringParser = new QueryStringParser(
                            queryString);
                    return queryStringParser.getQueryParamValue(paramName);
                }
            });
            webView.loadUrl(authUrl);
        } else {
            webView.setVisibility(View.GONE);
            Toast.makeText(getBaseContext(), R.string.toast_twitter_error,Toast.LENGTH_LONG).show();
        }
    }

    protected void updateStatus(){
        mSpinner.bringToFront();
        mSpinner.setVisibility(View.VISIBLE);
        try {
            TwitterUtils.sendTweet(getSharedPreferences("MA", Context.MODE_PRIVATE), tweetEditText.getText()
                    .toString());
            Toast.makeText(getBaseContext(), R.string.toast_tweeted,Toast.LENGTH_LONG).show();
            finish();
        } catch (TwitterException e) {
            Log.d(TAG, e.getMessage(), e.fillInStackTrace());
            Toast.makeText(getBaseContext(), R.string.toast_twitter_error,Toast.LENGTH_LONG).show();
        } catch (Exception e) {
            Log.d(TAG, e.getMessage(), e.fillInStackTrace());
            Toast.makeText(getBaseContext(), R.string.toast_twitter_error,Toast.LENGTH_LONG).show();
        }
        mSpinner.setVisibility(View.GONE);
    }
    
    private String getAuthUrl() {
        String authUrl = null;
        try {
            authUrl = httpOauthprovider.retrieveRequestToken(httpOauthConsumer, CALLBACK_URL);
        } catch (OAuthMessageSignerException e) {
            Log.d(TAG, e.getMessage(), e.fillInStackTrace());
        } catch (OAuthNotAuthorizedException e) {
            Log.d(TAG, e.getMessage(), e.fillInStackTrace());
        } catch (OAuthExpectationFailedException e) {
            Log.d(TAG, e.getMessage(), e.fillInStackTrace());
        } catch (OAuthCommunicationException e) {
            Log.d(TAG, e.getMessage(), e.fillInStackTrace());
        }
        return authUrl;
    }
}

Friday, June 3, 2011

Converting MySql data base to SQLite data base

Hello Again, 
my goal here is to make a SQLite data base from an existing MySql db i have.
I was digging around the net, I found many tools that can do this mysql to SQLite migration. You can find it here in http://www.sqlite.org/cvstrac/wiki?p=ConverterTools .

Also, David provided a howto in http://code2design.com/tutorial/convert_mysql_tables_to_sqlite_tables, you may refer to that link to see how to do it manually, and i can tell you it is just a pain in the `back`. Or you can copy and paste the following shell script in your cmd.

First, disable all the AUTO_INCREMENT in your MySql data base, as stated by David
Second, in SQLite "auto_increment" is a given if the key is a PRIMARY KEY. So we can delete the "auto_increment" part of the MySQL query.
Second, let's move to the shell Script.
The shell script source is here in http://forums.mysql.com/read.php?145,68269,92627 but i made some modification to it and here is the result :
1- Open you cmd (Under Linux of course) then tape the following :

                                                                                                         
 ***@***:~$ sudo /bin/sh
[sudo] password for ***:
 #                                                           
                                                                                                        

Now, copy the following into the cmd then press Enter key.

mysqldump --user=root --password=rootpass --compact --compatible=ansi --default-character-set=binary db_name |
grep -v ' KEY "' |
grep -v ' UNIQUE KEY "' |
perl -e 'local $/;$_=<>;s/,\n\)/\n\)/gs;print "begin;\n";print;print "commit;\n"' |
perl -pe '
if (/^(INSERT.+?)\(/) {
$a=$1;
s/\\'\''/'\'\''/g;
s/\\n/\n/g;
s/\),\(/\);\n$a\(/g;
}
' |
sqlite3 output.db

Explanation :

The mysqldump client can be used to dump a database or a collection of
databases for backup or for transferring the data to another SQL server
(not necessarily a MySQL server). The dump contains SQL statements to
create the table and/or populate the table.
In the first line, you will need to specify the db user, the password if there is any else remove that option, and other option, refer here for more details about mysqldump http://linuxcommand.org/man_pages/mysqldump1.html. And finally we put the db name that we want to convert. (db_name in this case).
The next lines will edit the sql file to remove lines that contains KEY and UNIQUE KEY and other fixes (I ain't that expert handling command lines in linux :) )
the last line here is where we are going to create our sqlite file, in this case we named the file output.db but you u can name it what ever you want.

Now, if every thing went alright, you will have the file output.db created under the directory /bin with root rights. You will have to move that file and change its rights.


 # exit                         
 ***@***:~$ sudo mv /bin/output.db output.db
 ***@***:~$ chmod 755 output.db


I think we are done here. Now you can open your SQLite file using any tool you want, like Sqliteman or any other.

In the next tutorial, I am going to demonstrate how to use this SQLite data base in android. (Coming SOON).
Feel free to leave your comments.

Friday, April 15, 2011

Populating a ListView from external source on Android

Hello again,
There are many tutorials on how to create and manage ListView on Android, the difficulty here is whether you can find a method that will satisfy your needs.
Here is a tutorial on how to best create a ListView and customize it.

First, lets take a look on our Php script that will provide our data.
if (isset($_POST['JSON'])) {
    header('Content-Type: application/json; charset=iso-8859-1');
    $response = array(
        array('id' => '149', 'title' => 'Revolution',
            'artist' => 'Bob Marley', 'duration' => '271'),
        array('id' => '150', 'title' => 'Natural Mystic',
            'artist' => 'Bob Marley', 'duration' => '312'),
        array('id' => '151', 'title' => 'Redemption Song',
            'artist' => 'Bob Marley', 'duration' => '268'),
        array('id' => '152', 'title' => 'No More Trouble',
            'artist' => 'Bob Marley', 'duration' => '285'),
    );
    print_r(json_encode($response));
}
As you can see, we are going to get some tracks from our data source.
Now let's create our android application.
Here is the class that will hold one track, Track.java:
public class Track {
 private int id;
 private String title;
 private String artist;
 private int duration;

 public Track(int id, String title, String artist, int duration) {
  this.id = id;
  this.title = title;
  this.artist = artist;
  this.duration = duration;
 }
 /**
  * ... getters here
  */
}
- The layout for the listView in main.xml

    

- The layout for one row in the list view, this will give us more options to better customize our view, row.xml, the example here is from romain guy here

     
    
        
        
    

Now we are done with the view layout, let's put every thing together.
It is time to write our custom TrackAdapter that extends TrackAdapter
private class TrackAdapter extends ArrayAdapter {
        // a list that will store our tracks
 private List trackList;
 public LineAdapter(Context context, int textViewResourceId,
   List items) {
  super(context, textViewResourceId, items);
  this.trackList = items;
 }
        // this methode returns a single row from the list view
 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
  View v = convertView;
  if (v == null) {
   LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   v = vi.inflate(R.layout.row, null);
  }
  Track track = trackList.get(position);
  if (track != null) {
   TextView tt = (TextView) v.findViewById(R.id.toptext);
   TextView bt = (TextView) v.findViewById(R.id.bottomtext);
   if (tt != null) {
    tt.setText("Artist: " + track.getArtist());
   }
   if (bt != null) {
    bt.setText("Title: " + track.getTitle());
   }
  }
  return v;
 }
}
This is a private class and should be added under our MainActivity class.

Now, let's fetch the track list from our external source. I have already made a tutorial on how to get data using a Http post method in my previous blog here so we gonna use that except that we are not sending anything to the server. the doRequest methode wont have any parameters.
private List getTracks() {
 String response = Server.doRequest();
 JSONArray json;
 List trackList = new ArrayList();
 int id, duration;
 String title, artist;
 try {
  json = new JSONArray(response);
  for (int i = 0; i < json.length(); i++) {
   id = json.optJSONObject(i).getInt("id");
   duration = json.optJSONObject(i).getInt("duration");
   title = json.optJSONObject(i).getString("title");
   artist = json.optJSONObject(i).getString("artist");
   trackList.add(new Track(id, title, artist, duration));
  }
 } catch (JSONException e) {
  Log.e("Json", e.getMessage(), e.fillInStackTrace());
 }
 return trackList;
}
Finally, all we need to do is make some calls in the onCreate method
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    m_tracks = new ArrayList();
    m_tracks = getTracks();
    this.m_adapter = new TrackAdapter(this, R.layout.row, m_tracks);
    setListAdapter(this.m_adapter);
}
Also we can add ItemClickListner to the list and even more stuffs. For a more detailed tutorial you can check softwarepassion.com Feel free to leave your comments.

Wednesday, April 13, 2011

JSON via Http on Android

There are many ways to handle JSON data on android. Here is my way to do that.
In this tutorial, I am going to get external JSON data with some Http post request and response.

First, let's create the class that will handle the incoming and outgoing data.
public class HttpUtils {
 private static final String URL = "http://path/to/php/script.php";

 public static String doRequest(String username, String password) {
  HttpClient client = new DefaultHttpClient();
  HttpConnectionParams.setConnectionTimeout(client.getParams(), 10000);
  JSONObject jsonObject = new JSONObject();
  HttpPost post;
  HttpResponse response;
  String responseStr = "[{}]";
  try {
   post = new HttpPost(URL);
   Log.i("Http", "Requesting " + URL);
   
   //Puting the username and password as JSON data
   jsonObject.put("username", username);
   jsonObject.put("password", password);
   StringEntity se = new StringEntity("JSON=" + jsonObject.toString());
   se.setContentType(new BasicHeader(HTTP.CONTENT_TYPE,"application/x-www-form-urlencoded"));
   post.setEntity(se);
   Log.i("Http", "Sending :" + slurp(post.getEntity().getContent()));
   response = client.execute(post);

   // Checking response
   if (response.getStatusLine().getStatusCode() == 200) {
    // Get the data in the entity
    InputStream in = response.getEntity().getContent();
    responseStr = slurp(in);
    Log.i("Http", "Getting :" + responseStr);
   } else {
    throw new HttpException("HttpMethod Returned Status Code: "
      + response.getStatusLine().getStatusCode()
      + " when attempting: " + URL);
   }
  } catch (Exception e) {
   Log.e("Http", e.getMessage(), e.fillInStackTrace());
  }

  return responseStr;
 }

 /**
  * Convert an InputStream to String
  * 
  * @param in the InputStream to be converted to String
  * @return the result string
  * @throws IOException
  */
 private static String slurp(InputStream in) throws IOException {
  StringBuffer out = new StringBuffer();
  byte[] b = new byte[4096];
  for (int n; (n = in.read(b)) != -1;) {
   out.append(new String(b, 0, n));
  }
  return out.toString();
 }
}
As you can see, we used a JSONObject to post some data to our php script witch look like this :
if (isset($_POST['JSON'])) {
    header('Content-Type: application/json; charset=iso-8859-1');
    $data = json_decode($_POST['JSON'], true);
    $username = $data['username'];
    $password = $data['password'];
    if ($username == 'ElWardi' && $password == 'json') {
        $response = array(
            array('success' => true),
            array('name' => 'PFA', 'description' => 'PFA test'),
            array('name' => 'PFA1', 'description' => 'PFA1 test'),
            array('name' => 'PFA2', 'description' => 'PFA2 test'),
            array('name' => 'PFA3', 'description' => 'PFA3 test'),
            array('name' => 'PFA4', 'description' => 'PFA4 test')
        );
    } else {
        $response = array(
            array('success' => false)
        );
    }
    print_r(json_encode($response));
}
[Update] : The php script will encode an array of data and send it back to the android application. The encoded data will look like this :
[{"success":true},
{"name":"PFA","description":"PFA test"},
{"name":"PFA1","description":"PFA1 test"},
{"name":"PFA2","description":"PFA2 test"},
{"name":"PFA3","description":"PFA3 test"},
{"name":"PFA4","description":"PFA4 test"}]
Now, all the rest to do is call the doRequest method then parse the response data. I made that in the MainActivity.java :
public class MainActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        String response = HttpUtils.doRequest("ElWardi", "json");
  JSONArray json;
  try {
   /*
    *  Build the JSAONArray from a string
    */
   json = new JSONArray(response);
   Log.i("JSON", "Number of data fetched : " + json.length()); // data count
   /*
    * get the data from the JSONArray if the authentifcation data are valid
    */
   if (json.optJSONObject(0).getBoolean("success")) {
    for (int i = 1; i < json.length(); i++) {
     Log.i("MyData", "Name : " + json.optJSONObject(i).getString("name")
       + "Description : " + json.optJSONObject(i).getString("description"));
    }
   }else 
    Log.e("MyData", "Some entered data (username or password) are invalid"); 
  } catch (JSONException e) {
   Log.e("JSON", e.getMessage(), e.fillInStackTrace());
  }
    }
}
You can see the results on the LogCat or any other debugging tool you use.
Well, I guess this will be all for my first blog here, I hope you will find it quite useful :).
Feel Free to leave your comments :)

Monday, April 4, 2011

First Sight !

If you want to be really good at something, it's going to involve relentlessly pushing past your comfort zone, as well as frustration, struggle, setbacks and failures. That's true as long as you want to continue to improve, or even maintain a high level of excellence. The reward is that being really good at something you've earned through your own hard work can be immensely satisfying.
Here, then, are the six keys to achieving excellence we've found are most effective for our clients:
  1. Pursue what you love. Passion is an incredible motivator. It fuels focus, resilience, and perseverance.
  2. Do the hardest work first. We all move instinctively toward pleasure and away from pain. Most great performers, Ericsson and others have found, delay gratification and take on the difficult work of practice in the mornings, before they do anything else. That's when most of us have the most energy and the fewest distractions.
  3. Practice intensely, without interruption for short periods of no longer than 90 minutes and then take a break. Ninety minutes appears to be the maximum amount of time that we can bring the highest level of focus to any given activity. The evidence is equally strong that great performers practice no more than 4 ½ hours a day.
  4. Seek expert feedback, in intermittent doses. The simpler and more precise the feedback, the more equipped you are to make adjustments. Too much feedback, too continuously can create cognitive overload, increase anxiety, and interfere with learning.
  5. Take regular renewal breaks. Relaxing after intense effort not only provides an opportunity to rejuvenate, but also to metabolize and embed learning. It's also during rest that the right hemisphere becomes more dominant, which can lead to creative breakthroughs.
  6. Ritualize practice. Will and discipline are wildly overrated. As the researcher Roy Baumeisterhas found, none of us have very much of it. The best way to insure you'll take on difficult tasks is to build rituals — specific, inviolable times at which you do them, so that over time you do them without having to squander energy thinking about them.