Android Twitter and C2DM Explained

Preview:

DESCRIPTION

Presentation given on TechGig for Android Twitter Reference App and C2DM.

Citation preview

Android Twitter Appand C2DM

By Rohit GhatolArchitect @ QuickOffice

Speaker Introduction

Speaker Introduction

• Rohit Ghatol• Architect @ QuickOffice• Proj Mgr @ Synerzip• Founder TechNext • Author “Beginning PhoneGap”

@Apress• Technical Speaker and Corporate

Trainer

Why Twitter App and C2DM in the same talk?

Poll Vs Push

Twitter App Demo

http://code.google.com/p/DroidTwitt

Twitter Requirements

OAuth AuthenticationFetch Tweets

Send TweetsBackground Sync

Revisit Android Building Blocks in Short

Activity LifeCycle

onCreate()

onDestroy()

onStart()

onStop()

onResume()

onPause()

CompleteLifeCycle

VisibleLifeCycle

Foreground VisibleLifeCycle

Calling Service

Activity

Service

void onStartCommand(Intent intent,…){}

Activity

Service

startService(intent)

bindService(intent)

foo();bar();

void foo(){}int bar(){}

Broadcast ReceiversApp 1

Android OS

App 2

Your App

Custom Event 1

Custom Event 2

Battery Low

RoamingCall

Network Change

Interested in any of these Events.

Overall Architecture

OAuth Authentication

First Time Launch

Asking Service To Fetch Tweets

Triggering Service every n minutes

Phone Boot Event

Battery Low Event

OAuth

Short Introduction to OAuth

http://fotofast.com

username

password

Login Cancel

http://fotofast.comWelcome Rohit

Picasa Flickr

Print Photo

http://fotofast.com

username

password

Login Cancel

Welcome Rohit

Enter Credentials for Picasa

If I go to a restaurant, do I give my ATM Card and Pin to the waiter to pay my bills?

Then Why should I give my Picasa Credentials to fotofast.com?

Take 2, Action

http://fotofast.com

username

password

Login Cancel

http://fotofast.comWelcome Rohit

Picasa Flickr

Print Photo

http://picasa.com

username

password

Login Cancel

Welcome to Picasa

Look Ma its Picasa itself asking for password!

http://picasa.com

Choose Album to Share with FotoFast

1 2

4 5

3

6

http://fotofast.comWelcome Rohit

Picasa Albums

Choose photo to Print Photo

Look Ma! I am able to print picasa photos from FotoFast

Twitter OAuth in Android

About OAuth

• The way OAuth works for Web is Browser redirects between the 2 sites

• Browser first shows FotoFast, which redirects you to Picasa to authenticate and approve sharing data with FotoFast

• On proper Authentication and Approval, Browser takes you back to FotoFast.com

How does the same work for Android Applications?

Steps of Twitter OAuth

Step 1 : Register with Twitter for OAuth Token

Step 2: Register your Activity to handle url “DroidTwit://twitt”

<activity android:name=".OAuthLogin" android:label="@string/app_name”android:launchMode="singleTask"><intent-filter>

<action android:name="android.intent.action.VIEW"></action><category

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

android:name="android.intent.category.BROWSABLE"></category><data android:scheme="DroidTwit" android:host="twitt"></data>

</intent-filter></activity>

Step 3: Create a OAuth URL and ask browser to show it

OAuth URL also contains CallBack URL you URL can redirect back to

your activity

public static final String CALLBACK_URL ="DroidTwitt://twitt";

public void buttonClick(){OAuthSignpostClient client = new OAuthSignpostClient

(TWITTER_KEY, TWITTER_SECRET,CALLBACK_URL);

final URI twitterUrl = client.authorizeUrl();

//Start BrowserstartActivity(new Intent(Intent.ACTION_VIEW,

Uri.parse(twitterUrl.toString())));}

Step 4: Handle Response from Browser after OAuth

@Overrideprotected void onNewIntent(final Intent intent) {

super.onNewIntent(intent);final Uri uri = intent.getData();if ((uri != null) && uri.toString().startsWith(CALLBACK_URL)) {verifier = uri.getQueryParameter("oauth_verifier");

client.setAuthorizationCode(verifier);

accessTokenAndSecret = client.getAccessToken();

Log.e("NewIntent", "Access token: " + accessTokenAndSecret[0]);Log.e("NewIntent", "Token secret: " + accessTokenAndSecret[1]);}

}

Fetching From Twitter API

Guidelines

• Make the fetch module SynchronousList<Tweets> getLatestTweets();

• Either call directly or through Services

• Use AsyncTask on UI to avoid ANR

Need for List Adapter

Entry 1

1

2

3

4

List<Tweet>

Tweets

Something else

Data that can varyList View functionality is same

Layout can vary

Adapter Layer

public class MyAdapter extends BaseAdapter{

public int getCount(){ }

public Object getItem(int position){ }

public long getItemId(int position){ }

public View getView(int position,Convert view,..){ }}

List View Explained

Entry 1

Entry 2

Entry 3

Entry 4

1

2

3

4

1 Plank No.

Plank

List View Explained

Entry 1

Entry 2

Entry 3

Entry 4

1

2

3

4

1 Plank No.

Scroll

Entry 51

Plank 1 Reused when it pops out the top while scrolling

Thumbnails in ListView

Common Problem and How to solve it

Why is thumbnail is such a problem in ListView?

Entry 1

Entry 2

Entry 3

Entry 4

Thread 1

Thread 2

Threads to fetch image asynchronously

1

2

3

4

Thread 3

Thread 4

Why is thumbnail is such a problem in ListView?

Entry 5

Entry 6

Entry 7

Entry 8

Thread 1 Now Thread 1 returns image one and sets it on Plank 1, but Plank 1 should show image from Entry 5.

Wrong images are shown for Entry 5, till Thread 5 returns.

And for each scroll, again images are fetched

1

2

3

4

Thread 5

Solution

Entry 1

Entry 2

Entry 3

Entry 4

1

2

3

4Blocking Queue

Thread 1

Local Image Cache

Thread reads from Blocking Queue, checks the Image Cache, if Image not there, fetches it dumps in Image Cache and gives it to the Image View (If position is valid)

While Scrolling, we add entries in Blocking Queue

Class Diagram<<ImageLoader>>

void queue(String url, ImageView imageView);

<<ImageCache>>

void cache(String url, Bitmap bitmap);

BitMap get(String url);

PhotoToLoad

private String url;private ImageView imageView;

Uses BlockingQueue<PhotoToLoad> and has a thread which keeps running on the

BlockingQueue, to either fetch image from cache or from internet

Using Services

Lets see Code Demos for this

Complete Detailed Video of this is available on http://code.google.com/p/droidtwit/wiki/AndroidServiceTutorial

Using Alarm Manager

Lets see Code Demo

Listener Battery Event

Lets see Code Demo

C2DM

What is C2DM?

Cloud To Device Messaging

What is C2DM?

• Messages send from Server to Device via Google

• Small Messages only meant to tell the client, that server has new information

• Optimized and uses same channel as Gmail, Calendar and other google apps

Why C2DM?

Drawbacks of DroidTwitt

Battery Drain

• Application launching every 5/10/15 mins

• Not sure if we will get new information, blindly trying

• Way to optimize is using AlarmManager.setInExactRepeating(), but that only helps so much

Data Usage

• Polling means more use of Data

• If Server is not optimized, then it always sends a chunk of stale data, more bandwidth

• Ways to optimize is ask server to send data on top of what the client already has (say use the timestamp), but that only helps so much

Lag

• Notification using Polling will always have a Lag

• It ends up being a balancing act between Saving Battery and Freshness of the data

Traffic (Server Side)

• Every device, Every n minutes bombarding the Server

• Businesses won’t mind traffic as long it is adding real value. But is this real value?

Load (Server Side)

• Hitting Database blindly when getting traffic

• Servers need to optimized to know whether the polling requests will repeat very n minutes and they only hit database if they have change.

C2DM Players

Google Cloud

ApplicationServer

But Remember a User can have more than one Device!

C2DM Flow of Events

Google Cloud

ApplicationServer

Step 1: App Server goes Authentication with Google C2DM Cloud and gets a security token

Security TokenAppServer123

Google Cloud

ApplicationServer

Step 2: Device 1 Registers itself to Google C2DM Cloud, gets an Registration Id

Security TokenAppServer123

Google Cloud

ApplicationServer

Step 3: Device 1 tells App Server that its C2DM Reg Id is XYZ

Device Info Reg IdRohit’s Phone XYZ

XYZ

Security Token

AppServer123

Google Cloud

ApplicationServer

Device Info Reg IdRohit’s Phone XYZ

Step 4: App Server sends message to C2DM intended for Device with Reg Id XYZ

Security TokenAppServer123

Google Cloud

ApplicationServer

Device Info Reg IdRohit’s Phone XYZ

Step 6: Google C2DM Cloud Service sends Intent to Device with Reg id XYZ

Security TokenAppServer123

Google Cloud

ApplicationServer

Device Info Reg IdRohit’s Phone XYZ

Step 7: Device fetches data from App Server to process

Security TokenAppServer123

C2DM Demo

Lets see the Demo

Steps for C2DM

Step 1: Create AVD for Emulator

• Create a AVD With Google API 8 (not Android API 8)

Step 1: Create AVD for Emulator

• Go to Settings and Add a Google Account

Step 2: Server Side Authentication//Create URL for Google Authentication for C2DMStringBuilder builder = new StringBuilder();//Has to be C2DM Registered Email Addressbuilder.append("Email=").append(email);builder.append("&Passwd=").append(password);builder.append("&accountType=GOOGLE");builder.append("&source=MyLittleExample");builder.append("&service=ac2dm");

byte[] data = builder.toString().getBytes();

Server Side Code

One Time

Step 2: Server Side Authentication// Setup the Http PostURL url = new URL("https://www.google.com/accounts/ClientLogin");HttpURLConnection con = (HttpURLConnection) url.openConnection();……con.setRequestMethod("POST");con.setRequestProperty("Content-Type”, "application/x-www-form-urlencoded");

con.setRequestProperty("Content-Length", Integer.toString(data.length));

Server Side Code

One Time

Step 2: Server Side Authentication// Issue the HTTP POST requestOutputStream output = con.getOutputStream();output.write(data);output.close();

//Read Auth Token ResponseBufferedReader reader = new BufferedReader(new InputStreamReader(

con.getInputStream()));String line = null, auth_key = null;while ((line = reader.readLine()) != null) {

if (line.startsWith("Auth=")) {auth_key = line.substring(5);

}}

Server Side Code

One Time

Step 3: Android Manifest<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.sparklytix.factoreal" android:versionCode="1"android:versionName="1.0"><uses-sdk android:minSdkVersion="8" /><permission

android:name="com.sparklytix.factoreal.permission.C2D_MESSAGE"android:protectionLevel="signature" />

<uses-permissionandroid:name="com.sparklytix.factoreal.permission.C2D_MESSAGE" />

<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

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

Android Code

Step 3: Android Manifest<receiver android:name=".receivers.C2DMBroadCastReceiver”

android:permission="com.google.android.c2dm.permission.SEND"> <!-- Receive the actual message -->

<intent-filter><action

android:name="com.google.android.c2dm.intent.RECEIVE" /><category android:name="com.sparklytix.factoreal" />

</intent-filter><!-- Receive the registration id --><intent-filter>

<action android:name="com.google.android.c2dm.intent.REGISTRATION" />

<category android:name="com.sparklytix.factoreal" /></intent-filter>

</receiver>

Android Code

Step 4: Send C2DM Device Reg Reqpublic void register(Context context, String sender) {

Intent intent = new Intent("com.google.android.c2dm.intent.REGISTER"); //app is this application itself

intent.putExtra("app",PendingIntent.getBroadcast(context, 0, new

Intent(), 0)); //sender is typical gmail account on the device

intent.putExtra("sender”,sender);context.startService(intent);

}

Android Code

Step 5: Get Device Reg Idpublic void onReceive(Context context, Intent intent) { if(“com.google.android.c2dm.intent.REGISTRATION”.equals(intent.getAction()){

final String registrationId = intent.getStringExtra("registration_id");String error = intent.getStringExtra("error");if (intent.getStringExtra("error") != null) {

// Registration failed, should try again later.} else if (intent.getStringExtra("unregistered") != null) {

//Remove Reg Id from Our Application Server} else if (registrationId != null) {

//Send Reg Id to Our Application Server}

}}

Android Code

Broadcast Receiver

Step 6: Send Message from ServerStringBuilder postDataBuilder = new StringBuilder();postDataBuilder.append(“registration_id”).append("=”).append

(registrationId);postDataBuilder.append("&").append(“collapse_key”).append("=")

.append("0");postDataBuilder.append("&").append("data.payload").append("=")

.append(URLEncoder.encode(message, UTF8));byte[] postData = postDataBuilder.toString().getBytes(UTF8);

Server Side Code

Step 6: Send Message from ServerURL url = new URL("https://android.clients.google.com/c2dm/send");HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();....conn.setRequestMethod("POST");conn.setRequestProperty("Content-Type“,"application/x-www-form-urlencoded;charset=UTF-8");conn.setRequestProperty("Content-Length",Integer.toString(postData.length));conn.setRequestProperty("Authorization", "GoogleLogin auth="

+ auth_token);OutputStream out = conn.getOutputStream();out.write(postData);out.close();

Server Side Code

Step 7: Receive Message at Android

public void onReceive(Context context, Intent intent) { if(“com.google.android.c2dm.intent.RECEIVE”.equals(intent.getAction()){

String payload = intent.getStringExtra("payload");//Create a Notification and fire it

}}

Android Code

Broadcast Receiver

Conclusion

• C2DM is an excellent way to– Reduce Data Usage and Save Battery on the

Device– And also reduce load and network contingency on

the Server– Also when device goes offline, Our App Server

does not have to queue, Google C2DM queues itself

• Definitely a better option than Polling

Code Projects referred

• Droid Twitt Project - http://code.google.com/p/droidtwitt

• Android Code Examples – http://code.google.com/p/droidtwitt

• Listing Thumbnails in ListView - http://code.google.com/p/feedreader/

• C2DM Server and Android App - http://code.google.com/p/android-c2dm-reference-impl/

Q & A

Recommended