Create your Treasure Map on Android

Treasure Map on Android


Refer to the post "Embed Google Map in WebView", you can create your own Treasure Map easily, by copy the link URL from http://maps.google.com/ with Treasure Mode selected, and replace it to mapPath in the MainActivity code.

Get the URL from Google Maps with Threasure Mode selected


Remark: I don't know will it back to normal tomorrow:)


FYI:



How to get Traffic come from International Space Station

If you have install Google Analytics, check your Real-Time report of Locations within today, you have 100% traffic from International Space Station!

100% Traffic from International Space Station

Happy April Fools Day:)

YouTube's ready to select a winner...

Another April Fools Joke?


Google Nose!? Is it a April Fools Joke?

Is it a April Fools Joke? http://www.google.com/nose/




Introducing Google Nose

We're excited to announce our newest addition to Search: Google Nose. What do wet dogs smell like? Google Nose! How about victory? Google Nose! Try searching on Google for "wet dog" and explore other smells that people sniffed for, or visit google.com/nose to learn more. Happy smelling!

ADK Example: Control Arduino Due LED from Android device



Example to show how to implement with ADK, to control LED on Arduino Due from Android device.

Refer to my another blog for Arduino:

Draw Polyline from touched location to MyLocation

In this exercise, Google Maps Android API v2 is implemented with MyLocation enabled. When user LongClick on the Map, a marker will be drawn, and a Polyline from touched location to MyLocation will be drawn if MyLocation is available.

Polyline from touched location to MyLocation


To get MyLocation programmatically, you have to enable my-location layer by calling setMyLocationEnabled(true). Then call getMyLocation(), it returns the currently displayed user location, or null if there is no location data available. Please noted that MyLocation may be need long time to available.

MainActivity.java
package com.example.androidmapex;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;

import android.location.Location;
import android.os.Bundle;
import android.widget.Toast;
import android.app.Activity;
import android.app.FragmentManager;

public class MainActivity extends Activity{

private GoogleMap myMap;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

FragmentManager myFragmentManager = getFragmentManager();
MapFragment myMapFragment1 =
(MapFragment)myFragmentManager.findFragmentById(R.id.map1);

myMap = myMapFragment1.getMap();
myMap.setMyLocationEnabled(true);
myMap.setOnMapLongClickListener(myOnMapLongClickListener);

}

OnMapLongClickListener myOnMapLongClickListener =
new OnMapLongClickListener(){

@Override
public void onMapLongClick(LatLng point) {
myMap.addMarker(new MarkerOptions()
.position(point)
.title(point.toString()));

Location myLocation = myMap.getMyLocation();
if(myLocation == null){
Toast.makeText(getApplicationContext(),
"My location not available",
Toast.LENGTH_LONG).show();
}else{
PolylineOptions polylineOptions = new PolylineOptions();
polylineOptions.add(point);
polylineOptions.add(
new LatLng(myLocation.getLatitude(), myLocation.getLongitude()));
myMap.addPolyline(polylineOptions);
}
}

};

}


layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >

<fragment
android:id="@+id/map1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5px"
class="com.google.android.gms.maps.MapFragment"/>

</LinearLayout>


download filesDownload the files.



The series:
A simple example using Google Maps Android API v2, step by step.

Embed Google Map in WebView

Embed Google Map in WebView


To load your custom Map, open it in PC, copy the Short URL in share option. It will be used later.

copy the Short URL in share option


MainActivity.java, load mapPath with the Short URL in share option.
package com.example.androidwebmap;

import android.os.Bundle;
import android.app.Activity;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class MainActivity extends Activity {

WebView myWebView;

String mapPath = "https://maps.google.com/?ll=37.0625,-95.677068&spn=29.301969,56.513672&t=h&z=4";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myWebView = (WebView)findViewById(R.id.mapview);
myWebView.getSettings().setJavaScriptEnabled(true);
myWebView.setWebViewClient(new WebViewClient());

myWebView.loadUrl(mapPath);
}

}


Modify layout to add a MapView.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context=".MainActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
<WebView
android:id="@+id/mapview"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</LinearLayout>

Notes:
- Permission of "android.permission.INTERNET" is need.
- Not all function can work on WebView! such as user cannot touch the marker on map.

download filesDownload the files.

Retain instance of MapFragment

In the last exercise of "Dual MapFragment", if the device orientation changed, the display area and zoom level will be kept, but the added marker will disappear.

In this exercise, we are going to modify map2 from last post, to retain instance after orientation changed.

Retain instance of MapFragment


To keep the markers after orientation changed, create custom MapFragment (RetainMapFragment), override onActivityCreated() call setRetainInstance(true).
package com.example.androidmapex;

import android.os.Bundle;

import com.google.android.gms.maps.MapFragment;

public class RetainMapFragment extends MapFragment {

@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
}

}


Modify layout file to use "com.example.androidmapex.RetainMapFragment" class on map2.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >

<fragment
android:id="@+id/map1"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="2"
android:layout_margin="5px"
class="com.google.android.gms.maps.MapFragment"/>
<fragment
android:id="@+id/map2"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="3"
android:layout_margin="5px"
class="com.example.androidmapex.RetainMapFragment"/>

</LinearLayout>


Modify MainActivity to use define myMapFragment2 as RetainMapFragment.
package com.example.androidmapex;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

import android.os.Bundle;
import android.app.Activity;
import android.app.FragmentManager;

public class MainActivity extends Activity{

private GoogleMap myMap1, myMap2;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

FragmentManager myFragmentManager = getFragmentManager();
MapFragment myMapFragment1 =
(MapFragment)myFragmentManager.findFragmentById(R.id.map1);
RetainMapFragment myMapFragment2 =
(RetainMapFragment)myFragmentManager.findFragmentById(R.id.map2);
myMap1 = myMapFragment1.getMap();
myMap2 = myMapFragment2.getMap();

myMap1.setOnMapLongClickListener(my1_OnMapLongClickListener);
myMap2.setOnMapLongClickListener(my2_OnMapLongClickListener);
}

OnMapLongClickListener my1_OnMapLongClickListener =
new OnMapLongClickListener(){

@Override
public void onMapLongClick(LatLng point) {
myMap1.addMarker(new MarkerOptions()
.position(point)
.title(point.toString()));
}

};

OnMapLongClickListener my2_OnMapLongClickListener =
new OnMapLongClickListener(){

@Override
public void onMapLongClick(LatLng point) {

BitmapDescriptor bitmapDescriptor =
BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE);

myMap2.addMarker(new MarkerOptions()
.position(point)
.icon(bitmapDescriptor)
.title(point.toString()));
}

};

}


Related:
- Retain data using Fragment API setRetainInstance()


download filesDownload the files.



The series:
A simple example using Google Maps Android API v2, step by step.

Dual MapFragment of Google Maps Android API v2

This exercise demonstrate how to embed two MapFragment in a Activity.

Dual MapFragment


Modify layout file to have two fragment of "com.google.android.gms.maps.MapFragment".
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >

<fragment
android:id="@+id/map1"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="2"
android:layout_margin="5px"
class="com.google.android.gms.maps.MapFragment"/>
<fragment
android:id="@+id/map2"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="3"
android:layout_margin="5px"
class="com.google.android.gms.maps.MapFragment"/>

</LinearLayout>


Modify MainActivity.java to handle the Maps separately.
package com.example.androidmapex;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

import android.os.Bundle;
import android.app.Activity;
import android.app.FragmentManager;

public class MainActivity extends Activity{

private GoogleMap myMap1, myMap2;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

FragmentManager myFragmentManager = getFragmentManager();
MapFragment myMapFragment1 =
(MapFragment)myFragmentManager.findFragmentById(R.id.map1);
MapFragment myMapFragment2 =
(MapFragment)myFragmentManager.findFragmentById(R.id.map2);
myMap1 = myMapFragment1.getMap();
myMap2 = myMapFragment2.getMap();

myMap1.setOnMapLongClickListener(my1_OnMapLongClickListener);
myMap2.setOnMapLongClickListener(my2_OnMapLongClickListener);
}

OnMapLongClickListener my1_OnMapLongClickListener =
new OnMapLongClickListener(){

@Override
public void onMapLongClick(LatLng point) {
myMap1.addMarker(new MarkerOptions()
.position(point)
.title(point.toString()));
}

};

OnMapLongClickListener my2_OnMapLongClickListener =
new OnMapLongClickListener(){

@Override
public void onMapLongClick(LatLng point) {

BitmapDescriptor bitmapDescriptor =
BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE);

myMap2.addMarker(new MarkerOptions()
.position(point)
.icon(bitmapDescriptor)
.title(point.toString()));
}

};

}


download filesDownload the files.



The series:
A simple example using Google Maps Android API v2, step by step.

Google Maps Engine Lite (Beta) launched

Google is launching Google Maps Engine Lite (Beta) helping you create advanced custom maps to share with collaborators and also publish to the web. You can visualize and map more data, import locations from a spreadsheet, use layers to visualize different types of content, or simply draw and add places, lines, and shapes.

Visit: Log-in your google account and browse Google Maps Engine Lite

Google Maps Engine Lite
Google Maps Engine Lite
Google Maps Engine Lite


- Learn more about Maps Engine Lite (Beta)

Save $350 on Apple 15.4" MacBook Pro quad-core Intel Core i7 2.7GHz

Apple 15.4" MacBook Pro quad-core Intel Core i7 2.7GHz,16GB RAM, 768GB SSD, 1GB GDDR5 NVIDIA,Retina display, OS X Mountain Lion - Ships from our warehouse by March 29th (Z0ML4LL/A) only $2,849.99.
List Price:$3,199.00
You Save:$350 (11%)

Your Final Price:$2,849.99
Ships from our warehouse by March 29th

Stop IntentService

I have numbers of post about IntentService before; include Perform background processing with IntentService, Share IntentService among Fragments, Send data from IntentService to Activity, via additional Broadcast and Generate Notification in IntentService. This post is a good exercise to understand the life-cycle of IntentService.

To stop a IntentService, call the method stopService (Intent service). It request that a given application service be stopped. If the service is not running, nothing happens. Otherwise it is stopped. Note that calls to startService() are not counted -- this stops the service no matter how many times it was started. So imple? not at all.

In this exercise, buttons are added to start and stop IntentService. It can be noted that:
  • If you click the "Start IntentService" multi times, only one request will be processed at a time, All requests are handled on a single worker thread. The extra requests will be place in a queue.
  • If you click the "Start IntentService" multi times, only ONE call of stopService() is need to stop all request.
  • If you remove the checking on the boolean stopped, in onHandleIntent() of MyIntentService.java; call stopService() will call onDestroy() of MyIntentService, but the code in onHandleIntent() will keep running until finish by itself. Because onHandleIntent() is running on another worker thread.

Stop IntentService


Layout file
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context=".MainActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
<Button
android:id="@+id/start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start IntentService" />
<Button
android:id="@+id/stop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Stop IntentService" />
<TextView
android:id="@+id/result"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleHorizontal"
android:max="10"
android:progress="0"/>

</LinearLayout>


MainActivity.java
package com.example.androidintentservice;

import android.os.Bundle;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

public class MainActivity extends Activity {

Button buttonStart, buttonStop;
TextView textResult;
ProgressBar progressBar;

private MyBroadcastReceiver myBroadcastReceiver;
private MyBroadcastReceiver_Update myBroadcastReceiver_Update;

Intent intentMyIntentService;
int numberOfIntentService;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textResult = (TextView)findViewById(R.id.result);
progressBar = (ProgressBar)findViewById(R.id.progressbar);
buttonStart = (Button)findViewById(R.id.start);
buttonStop = (Button)findViewById(R.id.stop);

buttonStart.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View arg0) {
numberOfIntentService++;

//prepare String passing to intentMyIntentService
String msgToIntentService = "Android-er: " + numberOfIntentService;

//Start MyIntentService
intentMyIntentService = new Intent(MainActivity.this, MyIntentService.class);
intentMyIntentService.putExtra(MyIntentService.EXTRA_KEY_IN, msgToIntentService);
startService(intentMyIntentService);
}});

buttonStop.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View arg0) {
if(intentMyIntentService != null){
stopService(intentMyIntentService);
intentMyIntentService = null;
}
}});

numberOfIntentService = 0;

myBroadcastReceiver = new MyBroadcastReceiver();
myBroadcastReceiver_Update = new MyBroadcastReceiver_Update();

//register BroadcastReceiver
IntentFilter intentFilter = new IntentFilter(MyIntentService.ACTION_MyIntentService);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(myBroadcastReceiver, intentFilter);

IntentFilter intentFilter_update = new IntentFilter(MyIntentService.ACTION_MyUpdate);
intentFilter_update.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(myBroadcastReceiver_Update, intentFilter_update);
}

@Override
protected void onDestroy() {
super.onDestroy();
//un-register BroadcastReceiver
unregisterReceiver(myBroadcastReceiver);
unregisterReceiver(myBroadcastReceiver_Update);
}

public class MyBroadcastReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
String result = intent.getStringExtra(MyIntentService.EXTRA_KEY_OUT);
textResult.setText(result);
}
}

public class MyBroadcastReceiver_Update extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
int update = intent.getIntExtra(MyIntentService.EXTRA_KEY_UPDATE, 0);
progressBar.setProgress(update);
}
}

}


MyIntentService.java
package com.example.androidintentservice;

import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.widget.Toast;

public class MyIntentService extends IntentService {

private static final int MY_NOTIFICATION_ID=1;
private static final int MY_DESTORY_NOTIFICATION_ID=2;
NotificationManager notificationManager;
Notification myNotification;

public static final String ACTION_MyIntentService = "com.example.androidintentservice.RESPONSE";
public static final String ACTION_MyUpdate = "com.example.androidintentservice.UPDATE";
public static final String EXTRA_KEY_IN = "EXTRA_IN";
public static final String EXTRA_KEY_OUT = "EXTRA_OUT";
public static final String EXTRA_KEY_UPDATE = "EXTRA_UPDATE";
String msgFromActivity;
String extraOut;

boolean success;
boolean stopped;

public MyIntentService() {
super("com.example.androidintentservice.MyIntentService");
success = false;
stopped = false;
}

@Override
protected void onHandleIntent(Intent intent) {

//get input
msgFromActivity = intent.getStringExtra(EXTRA_KEY_IN);
extraOut = "Hello: " + msgFromActivity;

//total 10 sec
for(int i = 0; i <=10; i++){

try {
Thread.sleep(1000); //every 1 sec
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

//--- Try to comment it ---//
if(stopped){
break;
}

//send update
Intent intentUpdate = new Intent();
intentUpdate.setAction(ACTION_MyUpdate);
intentUpdate.addCategory(Intent.CATEGORY_DEFAULT);
intentUpdate.putExtra(EXTRA_KEY_UPDATE, i);
sendBroadcast(intentUpdate);

PendingIntent pendingIntent_doNothing = PendingIntent.getActivity(
getApplicationContext(),
0,
new Intent(), //empty Intent do nothing
Intent.FLAG_ACTIVITY_NEW_TASK);

//generate notification
String notificationText = String.valueOf((int)(100 * i / 10)) + " %";
myNotification = new NotificationCompat.Builder(getApplicationContext())
.setContentTitle("Progress")
.setContentText(notificationText)
.setTicker("Notification!")
.setWhen(System.currentTimeMillis())
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pendingIntent_doNothing)
.build();

notificationManager.notify(MY_NOTIFICATION_ID, myNotification);
}

success = true;
myNotification = new NotificationCompat.Builder(getApplicationContext())
.setContentTitle("Success Finished")
.setContentText("Successful Finished")
.setTicker("Successful Finished")
.setWhen(System.currentTimeMillis())
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
.build();
notificationManager.notify(MY_NOTIFICATION_ID, myNotification);

//return result
Intent intentResponse = new Intent();
intentResponse.setAction(ACTION_MyIntentService);
intentResponse.addCategory(Intent.CATEGORY_DEFAULT);
intentResponse.putExtra(EXTRA_KEY_OUT, extraOut);
sendBroadcast(intentResponse);
}

@Override
public void onCreate() {
super.onCreate();
notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
}

@Override
public void onDestroy() {
Notification onDestroyNotification;
String notice;

stopped = true;

if(success){
notice = "onDestroy with success";
onDestroyNotification = new NotificationCompat.Builder(getApplicationContext())
.setContentTitle(notice)
.setContentText(msgFromActivity)
.setTicker(notice)
.setWhen(System.currentTimeMillis())
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
.build();
}else{
notice = "onDestroy WITHOUT success!";
onDestroyNotification = new NotificationCompat.Builder(getApplicationContext())
.setContentTitle(notice)
.setContentText(msgFromActivity)
.setTicker(notice)
.setWhen(System.currentTimeMillis())
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
.build();
}
notificationManager.notify(MY_DESTORY_NOTIFICATION_ID, onDestroyNotification);
Toast.makeText(getApplicationContext(), notice, Toast.LENGTH_LONG).show();

super.onDestroy();
}

}


Need to include <service> of "MyIntentService" in AndroidManifest.xml, refer to the post "Send data from IntentService to Activity, via additional Broadcast".


download filesDownload the files.

Create a PendingIntent for Notification to do nothing

Refer to the last post "error of using NotificationCompat.Builder, IllegalArgumentException: contentIntent required", if you want no action perform when user click on the Notification, you can insert a PendingIntent with a dummy Intent.

Example:


PendingIntent pendingIntent = PendingIntent.getActivity(
MainActivity.this,
0,
new Intent(), //Dummy Intent do nothing
Intent.FLAG_ACTIVITY_NEW_TASK);

myNotification = new NotificationCompat.Builder(context)
.setContentTitle("Exercise of Notification!")
.setContentText("http://arteluzevida.blogspot.com/")
.setTicker("Notification!")
.setWhen(System.currentTimeMillis())
.setContentIntent(pendingIntent)
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
.build();



Get inside your favorite movies with Google Play

“What’s his name again? Wasn’t he the guy in that movie with the battle of the bands?” Now, while you're watching a movie on Google Play, you can find out that it’s Jack Black (of course!), who was born in Hermosa Beach and is 43 years old. And with one click you can search the web and learn the fun fact that his parents are both rocket scientists.

We’ve added info cards to the Google Play Movies & TV app so you can easily learn more about the actors, related films and even what song is playing in many of your favorite movies. When you’re watching a film on your tablet, simply press pause and cards will pop up with information about actors on screen. You can tap on an actor’s face to learn more about him, like his age, place of birth, his character in the movie, and his recent work, or scroll through the info cards to learn more about the movie or soundtrack. When you resume the movie, the cards will disappear.


This new feature is offered for hundreds of movies in Google Play and we’re adding more every day. If you’re in the U.S. and have a tablet running Android 4.0 (Ice Cream Sandwich) and higher, download the latest version of the Google Play Movies & TV app to check it out. We hope to bring info cards to more movies in more countries and devices soon.

Posted by Ben Serridge, Product Manager for Google Play

The World’s Languages in Your Pocket (No Internet Required)

Have you ever found yourself in a foreign country, wishing you knew how to say "I'm lost!" or "I'm allergic to peanuts”? The Internet and services like Google Translate can help—but what if you don't have a connection? 

Today we're launching offline language packages for Google Translate on Android (2.3 and above) with support for fifty languages, from French and Spanish to Chinese and Arabic. 

You can select [Offline Languages] in the app menu to see all the offline language packages available for download. To enable offline translation between any two languages, you just need to select them in the offline languages menu. Once the packages are downloaded, you're good to go.

 

While the offline models are less comprehensive than their online equivalents, they are perfect for translating in a pinch when you are traveling abroad with poor reception or without mobile data access.
  

So go out and explore another language or another culture without worrying about Internet access. There’s a whole world offline out there.

Posted by Minqi Jiang, Associate Product Manager

error of using NotificationCompat.Builder, IllegalArgumentException: contentIntent required

Refer to the exercise "Example of using NotificationCompat.Builder"; if the statement of new NotificationCompat.Builder...build() modified to remove .setContentIntent(pendingIntent), error of java.lang.IllegalArgumentException: contentIntent required MAY be thrown.

It will have no error in compile time, and no error when run on HTC One X (Android 4.1.1) and HTC Fly (Android 3.2.1). But error when run on Nexus One (Android 2.3.6), with error:

03-27 21:33:40.631: D/AndroidRuntime(24248): Shutting down VM
03-27 21:33:40.631: W/dalvikvm(24248): threadid=1: thread exiting with uncaught exception (group=0x40015560)
03-27 21:33:40.651: E/AndroidRuntime(24248): FATAL EXCEPTION: main
03-27 21:33:40.651: E/AndroidRuntime(24248): java.lang.IllegalArgumentException: contentIntent required: pkg=com.example.androidnotificationbuilder id=1 notification=Notification(vibrate=null,sound=default,defaults=0x1,flags=0x10)
03-27 21:33:40.651: E/AndroidRuntime(24248): at android.os.Parcel.readException(Parcel.java:1326)
03-27 21:33:40.651: E/AndroidRuntime(24248): at android.os.Parcel.readException(Parcel.java:1276)
03-27 21:33:40.651: E/AndroidRuntime(24248): at android.app.INotificationManager$Stub$Proxy.enqueueNotificationWithTag(INotificationManager.java:274)
03-27 21:33:40.651: E/AndroidRuntime(24248): at android.app.NotificationManager.notify(NotificationManager.java:111)
03-27 21:33:40.651: E/AndroidRuntime(24248): at android.app.NotificationManager.notify(NotificationManager.java:91)
03-27 21:33:40.651: E/AndroidRuntime(24248): at com.example.androidnotificationbuilder.MainActivity$1.onClick(MainActivity.java:52)
03-27 21:33:40.651: E/AndroidRuntime(24248): at android.view.View.performClick(View.java:2485)
03-27 21:33:40.651: E/AndroidRuntime(24248): at android.view.View$PerformClick.run(View.java:9080)
03-27 21:33:40.651: E/AndroidRuntime(24248): at android.os.Handler.handleCallback(Handler.java:587)
03-27 21:33:40.651: E/AndroidRuntime(24248): at android.os.Handler.dispatchMessage(Handler.java:92)
03-27 21:33:40.651: E/AndroidRuntime(24248): at android.os.Looper.loop(Looper.java:130)
03-27 21:33:40.651: E/AndroidRuntime(24248): at android.app.ActivityThread.main(ActivityThread.java:3683)
03-27 21:33:40.651: E/AndroidRuntime(24248): at java.lang.reflect.Method.invokeNative(Native Method)
03-27 21:33:40.651: E/AndroidRuntime(24248): at java.lang.reflect.Method.invoke(Method.java:507)
03-27 21:33:40.651: E/AndroidRuntime(24248): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
03-27 21:33:40.651: E/AndroidRuntime(24248): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
03-27 21:33:40.651: E/AndroidRuntime(24248): at dalvik.system.NativeStart.main(Native Method)

IllegalArgumentException: contentIntent required on Nexus One

Run on HTC One X

Run on HTC Flyer


If you want no action perform when user click on the Notification, you can create a PendingIntent with empty Intent for Notification to do nothing.


Generate Notification in IntentService

Last exercise demonstrate how to "Send data from IntentService to Activity, via additional Broadcast". We can also generate Notification in IntentService, to inform user about the progress.

Generate Notification in IntentService

Generate Notification in IntentService


Modify MyIntentService.java from last exercise.
package com.example.androidintentservice;

import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;

public class MyIntentService extends IntentService {

private static final int MY_NOTIFICATION_ID=1;
NotificationManager notificationManager;
Notification myNotification;

public static final String ACTION_MyIntentService = "com.example.androidintentservice.RESPONSE";
public static final String ACTION_MyUpdate = "com.example.androidintentservice.UPDATE";
public static final String EXTRA_KEY_IN = "EXTRA_IN";
public static final String EXTRA_KEY_OUT = "EXTRA_OUT";
public static final String EXTRA_KEY_UPDATE = "EXTRA_UPDATE";
String msgFromActivity;
String extraOut;

public MyIntentService() {
super("com.example.androidintentservice.MyIntentService");
}

@Override
protected void onHandleIntent(Intent intent) {

//get input
msgFromActivity = intent.getStringExtra(EXTRA_KEY_IN);
extraOut = "Hello: " + msgFromActivity;

for(int i = 0; i <=10; i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

//send update
Intent intentUpdate = new Intent();
intentUpdate.setAction(ACTION_MyUpdate);
intentUpdate.addCategory(Intent.CATEGORY_DEFAULT);
intentUpdate.putExtra(EXTRA_KEY_UPDATE, i);
sendBroadcast(intentUpdate);

//generate notification
String notificationText = String.valueOf((int)(100 * i / 10)) + " %";
myNotification = new NotificationCompat.Builder(getApplicationContext())
.setContentTitle("Progress")
.setContentText(notificationText)
.setTicker("Notification!")
.setWhen(System.currentTimeMillis())
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
.build();

notificationManager.notify(MY_NOTIFICATION_ID, myNotification);
}

//return result
Intent intentResponse = new Intent();
intentResponse.setAction(ACTION_MyIntentService);
intentResponse.addCategory(Intent.CATEGORY_DEFAULT);
intentResponse.putExtra(EXTRA_KEY_OUT, extraOut);
sendBroadcast(intentResponse);
}

@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
}

}


download filesDownload the files.

Related:
- error of using NotificationCompat.Builder, IllegalArgumentException: contentIntent required

Next:
- Stop IntentService, and know about the life-cycle of IntentService.

Send data from IntentService to Activity, via additional Broadcast

The former post demonstrate "Perform background processing with IntentService". It's a standard approach for IntentService, inform the Activity end of job by sending a Broadcast with Extra of respond.

But how can main Activity know the status of IntentService before end of job? It's another exercise to send additional Broadcast with another IntentFilter while running, to update main Activity, from IntentService. Such that we can implement additional BroadcastReceiver in main Activity to receive the additional Broadcast , and then update a ProgressBar.

Send data from IntentService to Activity, via additional Broadcast


MyIntentService.java
package com.example.androidintentservice;

import android.app.IntentService;
import android.content.Intent;

public class MyIntentService extends IntentService {

public static final String ACTION_MyIntentService = "com.example.androidintentservice.RESPONSE";
public static final String ACTION_MyUpdate = "com.example.androidintentservice.UPDATE";
public static final String EXTRA_KEY_IN = "EXTRA_IN";
public static final String EXTRA_KEY_OUT = "EXTRA_OUT";
public static final String EXTRA_KEY_UPDATE = "EXTRA_UPDATE";
String msgFromActivity;
String extraOut;

public MyIntentService() {
super("com.example.androidintentservice.MyIntentService");
}

@Override
protected void onHandleIntent(Intent intent) {

//get input
msgFromActivity = intent.getStringExtra(EXTRA_KEY_IN);
extraOut = "Hello: " + msgFromActivity;

for(int i = 0; i <=10; i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

//send update
Intent intentUpdate = new Intent();
intentUpdate.setAction(ACTION_MyUpdate);
intentUpdate.addCategory(Intent.CATEGORY_DEFAULT);
intentUpdate.putExtra(EXTRA_KEY_UPDATE, i);
sendBroadcast(intentUpdate);
}

//return result
Intent intentResponse = new Intent();
intentResponse.setAction(ACTION_MyIntentService);
intentResponse.addCategory(Intent.CATEGORY_DEFAULT);
intentResponse.putExtra(EXTRA_KEY_OUT, extraOut);
sendBroadcast(intentResponse);
}

}


MainActivity.java
package com.example.androidintentservice;

import android.os.Bundle;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.widget.ProgressBar;
import android.widget.TextView;

public class MainActivity extends Activity {

TextView textResult;
ProgressBar progressBar;

private MyBroadcastReceiver myBroadcastReceiver;
private MyBroadcastReceiver_Update myBroadcastReceiver_Update;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textResult = (TextView)findViewById(R.id.result);
progressBar = (ProgressBar)findViewById(R.id.progressbar);

//prepare MyParcelable passing to intentMyIntentService
String msgToIntentService = "Android-er";

//Start MyIntentService
Intent intentMyIntentService = new Intent(this, MyIntentService.class);
intentMyIntentService.putExtra(MyIntentService.EXTRA_KEY_IN, msgToIntentService);
startService(intentMyIntentService);

myBroadcastReceiver = new MyBroadcastReceiver();
myBroadcastReceiver_Update = new MyBroadcastReceiver_Update();

//register BroadcastReceiver
IntentFilter intentFilter = new IntentFilter(MyIntentService.ACTION_MyIntentService);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(myBroadcastReceiver, intentFilter);

IntentFilter intentFilter_update = new IntentFilter(MyIntentService.ACTION_MyUpdate);
intentFilter_update.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(myBroadcastReceiver_Update, intentFilter_update);
}

@Override
protected void onDestroy() {
super.onDestroy();
//un-register BroadcastReceiver
unregisterReceiver(myBroadcastReceiver);
unregisterReceiver(myBroadcastReceiver_Update);
}

public class MyBroadcastReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
String result = intent.getStringExtra(MyIntentService.EXTRA_KEY_OUT);
textResult.setText(result);
}
}

public class MyBroadcastReceiver_Update extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
int update = intent.getIntExtra(MyIntentService.EXTRA_KEY_UPDATE, 0);
progressBar.setProgress(update);
}
}

}


activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context=".MainActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
<TextView
android:id="@+id/result"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleHorizontal"
android:max="10"
android:progress="0"/>

</LinearLayout>


Need to include <service> of "MyIntentService" in AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.androidintentservice"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.androidintentservice.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>
<service android:name="com.example.androidintentservice.MyIntentService"></service>
</application>

</manifest>



download filesDownload the files.

Related:
- Generate Notification in IntentService


Example of using NotificationCompat.Builder

Last exercise demonstrate "Notification.Builder". If your app supports versions of Android as old as API level 4, you can instead use NotificationCompat.Builder, available in the Android Support library.

Example of using NotificationCompat.Builder


Simple replace Notification.Builder with NotificationCompat.Builder, and import android.support.v4.app.NotificationCompat.



myNotification = new NotificationCompat.Builder(context)
.setContentTitle("Exercise of Notification!")
.setContentText("http://arteluzevida.blogspot.com/")
.setTicker("Notification!")
.setWhen(System.currentTimeMillis())
.setContentIntent(pendingIntent)
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
.build();



Related:
- error of using NotificationCompat.Builder, IllegalArgumentException: contentIntent required


Example of using Notification.Builder

android.app.Notification.Builder is a builder class for Notification objects. Provides a convenient way to set the various fields of a Notification and generate content views using the platform's notification layout template. If your app supports versions of Android as old as API level 4, you can instead use NotificationCompat.Builder, available in the Android Support library.

Example of using Notification.Builder

Example of using Notification.Builder


package com.example.androidnotificationbuilder;

import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

private static final int MY_NOTIFICATION_ID=1;
NotificationManager notificationManager;
Notification myNotification;
private final String myBlog = "http://arteluzevida.blogspot.com/";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button buttonSend = (Button)findViewById(R.id.send);
buttonSend.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View arg0) {
Context context = getApplicationContext();
Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(myBlog));
PendingIntent pendingIntent = PendingIntent.getActivity(
MainActivity.this,
0,
myIntent,
Intent.FLAG_ACTIVITY_NEW_TASK);

myNotification = new Notification.Builder(context)
.setContentTitle("Exercise of Notification!")
.setContentText("http://arteluzevida.blogspot.com/")
.setTicker("Notification!")
.setWhen(System.currentTimeMillis())
.setContentIntent(pendingIntent)
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher)
.build();

notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(MY_NOTIFICATION_ID, myNotification);

}});

}

}



<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context=".MainActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
<Button
android:id="@+id/send"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Send a Notification" />

</LinearLayout>


Note: minSdkVersion have to be set ="16".

Share IntentService among Fragments

The post "Perform background processing with IntentService" demonstrate how to use IntentService in Activity. In this exercise, we are going to modify the exercise of "Yahoo Weather" to demonstrate how to share one IntentService by three Fragments. Here one IntentService means one common IntentService class, not one common IntentService object.




Create MyIntentService.java, it's our common IntentService class, to load Yahoo Weather in background service.
package com.example.androidyahooweatherdom;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import android.app.IntentService;
import android.content.Intent;

public class MyIntentService extends IntentService {

String ExtraAction;
String woeid;
MyWeather weatherResult;


public static final String EXTRA_KEY_ACTION = "EXTRA_ACTION";
public static final String EXTRA_KEY_WOEID = "EXTRA_WOEID";
public static final String EXTRA_KEY_WEATHER = "EXTRA_WEATHER";

class MyWeather{
String description;
String city;
String region;
String country;

String windChill;
String windDirection;
String windSpeed;

String sunrise;
String sunset;

String conditiontext;
String conditiondate;

String numberOfForecast;
String forecast;

public String toString(){

return "\n- " + description + " -\n\n"
+ "city: " + city + "\n"
+ "region: " + region + "\n"
+ "country: " + country + "\n\n"

+ "Wind\n"
+ "chill: " + windChill + "\n"
+ "direction: " + windDirection + "\n"
+ "speed: " + windSpeed + "\n\n"

+ "Sunrise: " + sunrise + "\n"
+ "Sunset: " + sunset + "\n\n"

+ "Condition: " + conditiontext + "\n"
+ conditiondate +"\n"

+ "\n"
+ "number of forecast: " + numberOfForecast + "\n"
+ forecast;

}
}

public MyIntentService() {
super("com.example.androidintentservice.MyIntentService");
}

@Override
protected void onHandleIntent(Intent intent) {

//get input
ExtraAction = intent.getStringExtra(EXTRA_KEY_ACTION);
woeid = intent.getStringExtra(EXTRA_KEY_WOEID);

loadYahooWeather();

//dummy delay
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

//return result
Intent intentResponse = new Intent();
intentResponse.setAction(ExtraAction);
intentResponse.addCategory(Intent.CATEGORY_DEFAULT);
intentResponse.putExtra(EXTRA_KEY_WEATHER, weatherResult.toString());
sendBroadcast(intentResponse);
}

protected void loadYahooWeather(){

String weatherString = QueryYahooWeather();
Document weatherDoc = convertStringToDocument(weatherString);
weatherResult = parseWeather(weatherDoc);
}

private MyWeather parseWeather(Document srcDoc){

MyWeather myWeather = new MyWeather();

//<description>Yahoo! Weather for New York, NY</description>
myWeather.description = srcDoc.getElementsByTagName("description")
.item(0)
.getTextContent();

//<yweather:location.../>
Node locationNode = srcDoc.getElementsByTagName("yweather:location").item(0);
myWeather.city = locationNode.getAttributes()
.getNamedItem("city")
.getNodeValue()
.toString();
myWeather.region = locationNode.getAttributes()
.getNamedItem("region")
.getNodeValue()
.toString();
myWeather.country = locationNode.getAttributes()
.getNamedItem("country")
.getNodeValue()
.toString();

//<yweather:wind.../>
Node windNode = srcDoc.getElementsByTagName("yweather:wind").item(0);
myWeather.windChill = windNode.getAttributes()
.getNamedItem("chill")
.getNodeValue()
.toString();
myWeather.windDirection = windNode.getAttributes()
.getNamedItem("direction")
.getNodeValue()
.toString();
myWeather.windSpeed = windNode.getAttributes()
.getNamedItem("speed")
.getNodeValue()
.toString();

//<yweather:astronomy.../>
Node astronomyNode = srcDoc.getElementsByTagName("yweather:astronomy").item(0);
myWeather.sunrise = astronomyNode.getAttributes()
.getNamedItem("sunrise")
.getNodeValue()
.toString();
myWeather.sunset = astronomyNode.getAttributes()
.getNamedItem("sunset")
.getNodeValue()
.toString();

//<yweather:condition.../>
Node conditionNode = srcDoc.getElementsByTagName("yweather:condition").item(0);
myWeather.conditiontext = conditionNode.getAttributes()
.getNamedItem("text")
.getNodeValue()
.toString();
myWeather.conditiondate = conditionNode.getAttributes()
.getNamedItem("date")
.getNodeValue()
.toString();

//Added to get elements of <yweather:forecast.../>
NodeList forecastList = srcDoc.getElementsByTagName("yweather:forecast");

myWeather.forecast = "";
if(forecastList.getLength() > 0){
myWeather.numberOfForecast = String.valueOf(forecastList.getLength());
for(int i = 0; i < forecastList.getLength(); i++){
Node forecastNode = forecastList.item(i);
myWeather.forecast +=
forecastNode
.getAttributes()
.getNamedItem("date")
.getNodeValue()
.toString() + " " +
forecastNode
.getAttributes()
.getNamedItem("text")
.getNodeValue()
.toString() +
" High: " + forecastNode
.getAttributes()
.getNamedItem("high")
.getNodeValue()
.toString() +
" Low: " + forecastNode
.getAttributes()
.getNamedItem("low")
.getNodeValue()
.toString() + "\n";
}
}else{
myWeather.numberOfForecast = "No forecast";
}

return myWeather;
}

private Document convertStringToDocument(String src){

Document dest = null;
DocumentBuilderFactory dbFactory =
DocumentBuilderFactory.newInstance();
DocumentBuilder parser;

try {
parser = dbFactory.newDocumentBuilder();
dest = parser.parse(new ByteArrayInputStream(src.getBytes()));
} catch (ParserConfigurationException e1) {
e1.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

return dest;
}

private String QueryYahooWeather(){

String qResult = "";
String queryString = "http://weather.yahooapis.com/forecastrss?w=" + woeid;

HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(queryString);

try {
HttpEntity httpEntity = httpClient.execute(httpGet).getEntity();

if (httpEntity != null){
InputStream inputStream = httpEntity.getContent();
Reader in = new InputStreamReader(inputStream);
BufferedReader bufferedreader = new BufferedReader(in);
StringBuilder stringBuilder = new StringBuilder();

String stringReadLine = null;

while ((stringReadLine = bufferedreader.readLine()) != null) {
stringBuilder.append(stringReadLine + "\n");
}

qResult = stringBuilder.toString();
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

return qResult;
}

}


Modify AndroidManifest.xml to add <service> of "MyIntentService", and add permission of "android.permission.INTERNET".
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.androidyahooweatherdom"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.INTERNET"/>

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.androidyahooweatherdom.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>
<service android:name="MyIntentService"></service>
</application>

</manifest>


res/layout/fragmentlayout.xml, layout of our fragments.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >

<TextView
android:id="@+id/status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold" />
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:id="@+id/weather"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</ScrollView>

</LinearLayout>


Creater a abstract class MyAbsYWeatherFragment extends Fragment, all our fragments will extend it. All it's sub-class have to override init_country() method to provide the target city of Yahoo Weather, a string of ACTION_RESPONSE for IntentFilter, and boolean retainInst to determine is it need retain instance. It will start our IntentService and handle all job for our fragments.
package com.example.androidyahooweatherdom;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public abstract class MyAbsYWeatherFragment extends Fragment {

TextView weather, status;
String stringWeatherResult;

private MyBroadcastReceiver myBroadcastReceiver;

String woeid;
String ACTION_RESPONSE;
boolean retainInst;
abstract void init_country();

public MyAbsYWeatherFragment(){
super();
init_country();
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View myFragmentView = inflater.inflate(R.layout.fragmentlayout, container, false);
weather = (TextView)myFragmentView.findViewById(R.id.weather);
status = (TextView)myFragmentView.findViewById(R.id.status);

myBroadcastReceiver = new MyBroadcastReceiver();

//register BroadcastReceiver
IntentFilter intentFilter = new IntentFilter(ACTION_RESPONSE);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
getActivity().registerReceiver(myBroadcastReceiver, intentFilter);

return myFragmentView;
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);

if(stringWeatherResult != null){
weather.setText(stringWeatherResult);
status.setText("Reloaded previous weatherResult");
}else{
status.setText("loadYahooWeather()");
startServiceToLoadYahooWeather();
}

setRetainInstance(retainInst);
}

@Override
public void onDestroyView() {
// TODO Auto-generated method stub
super.onDestroyView();
//un-register BroadcastReceiver
getActivity().unregisterReceiver(myBroadcastReceiver);
}

protected void startServiceToLoadYahooWeather(){
Intent intentMyIntentService = new Intent(getActivity().getApplicationContext(), MyIntentService.class);
intentMyIntentService.putExtra(MyIntentService.EXTRA_KEY_ACTION, ACTION_RESPONSE);
intentMyIntentService.putExtra(MyIntentService.EXTRA_KEY_WOEID, woeid);
getActivity().startService(intentMyIntentService);
}

public class MyBroadcastReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
stringWeatherResult = intent.getStringExtra(MyIntentService.EXTRA_KEY_WEATHER);

FragmentActivity parentActivity = getActivity();
if(parentActivity != null){
parentActivity.runOnUiThread(new Runnable(){

@Override
public void run() {
weather.setText(stringWeatherResult);
status.setText("Finished");
}});
}
}

}

}


Create three sub-class of MyAbsYWeatherFragment, Fragment1, Fragment2 and Fragment3. They are the actual fragments in the layout.

Fragment1.java
package com.example.androidyahooweatherdom;

public class Fragment1 extends MyAbsYWeatherFragment {

@Override
void init_country() {
//New York
woeid = "2459115";
ACTION_RESPONSE = "androidyahooweatherdom.RESPONSE.NewYork";
retainInst = false;
}

}


Fragment2.java
package com.example.androidyahooweatherdom;

public class Fragment2 extends MyAbsYWeatherFragment {

@Override
void init_country() {
//New York
woeid = "2459115";
ACTION_RESPONSE = "androidyahooweatherdom.RESPONSE.NewYork";
retainInst = true;
}

}


Fragment3.java
package com.example.androidyahooweatherdom;

public class Fragment3 extends MyAbsYWeatherFragment {

@Override
void init_country() {
//London
woeid = "23416974";
ACTION_RESPONSE = "androidyahooweatherdom.RESPONSE.London";
retainInst = true;
}

}


Modify /res/layout/activity_main.xml and /res/layout-land/activity_main.xml to add the three fragments in normal and landscape orientation.

/res/layout/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >

<fragment
class="com.example.androidyahooweatherdom.Fragment1"
android:id="@+id/fragment1"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1" />
<fragment
class="com.example.androidyahooweatherdom.Fragment2"
android:id="@+id/fragment2"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1" />
<fragment
class="com.example.androidyahooweatherdom.Fragment3"
android:id="@+id/fragment3"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1" />

</LinearLayout>


/res/layout-land/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context=".MainActivity" >

<fragment
class="com.example.androidyahooweatherdom.Fragment1"
android:id="@+id/fragment1"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
class="com.example.androidyahooweatherdom.Fragment2"
android:id="@+id/fragment2"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
class="com.example.androidyahooweatherdom.Fragment3"
android:id="@+id/fragment3"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1" />

</LinearLayout>


MainActivity
package com.example.androidyahooweatherdom;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

}


- Fragment1 call setRetainInstance(flase), so everytime orientation changed, it will re-start and reload new weather.

- Fragment3 call setRetainInstance(true), so it will not reload weather.

- Fragment2 call setRetainInstance(true), it will not reload weather too. But it have the same Action string for IntentFilter of Fragment1, so when IntentService of Fragment1 finished, BroadcastReceiver of Fragment2 will be called also.



download filesDownload the files.

If you have RuntimeException: Unable to instantiate activity ComponentInfo, please read HERE.