I have a SparseArray<myObject> and want to store it in bundle in onSaveInstanceState method in my activity and restore it in oncreate. I found putSparseParcelableArray method for put SparseArray in bundle and did this in onSaveInstanceState method:
bundle.putSparseParcelableArray("mySparseArray", mySparseArray);
But eclips shows this error:
The method putSparseParcelableArray(String, SparseArray<? extends Parcelable>) in the type Bundle is not applicable for the arguments (String, SparseArray<myObject>)
And the quick fix is casting argument mySparsArray to SparseArray<? extends Parcelable>, but if I do so and get it in onCreate method:
mySparseArray = (SparseArray<myObject>) savedInstanceState.getSparseParcelableArray("mySparseArray");
It gets this error:
Cannot cast from SparseArray<Parcelable> to SparseArray<myObject>
If this way is wrong, what is the solution for put mySparseArray in bundle?
Any help would be much appreciated.
You can extend SparsArray to imlement a Serializable as use it:
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import android.util.SparseArray;
/**
* #author Asaf Pinhassi www.mobiledev.co.il
* #param <E>
*
*/
public class SerializableSparseArray<E> extends SparseArray<E> implements Serializable{
private static final long serialVersionUID = 824056059663678000L;
public SerializableSparseArray(int capacity){
super(capacity);
}
public SerializableSparseArray(){
super();
}
/**
* This method is private but it is called using reflection by java
* serialization mechanism. It overwrites the default object serialization.
*
* <br/><br/><b>IMPORTANT</b>
* The access modifier for this method MUST be set to <b>private</b> otherwise {#link java.io.StreamCorruptedException}
* will be thrown.
*
* #param oos
* the stream the data is stored into
* #throws IOException
* an exception that might occur during data storing
*/
private void writeObject(ObjectOutputStream oos) throws IOException {
Object[] data = new Object[size()];
for (int i=data.length-1;i>=0;i--){
Object[] pair = {keyAt(i),valueAt(i)};
data[i] = pair;
}
oos.writeObject(data);
}
/**
* This method is private but it is called using reflection by java
* serialization mechanism. It overwrites the default object serialization.
*
* <br/><br/><b>IMPORTANT</b>
* The access modifier for this method MUST be set to <b>private</b> otherwise {#link java.io.StreamCorruptedException}
* will be thrown.
*
* #param oos
* the stream the data is read from
* #throws IOException
* an exception that might occur during data reading
* #throws ClassNotFoundException
* this exception will be raised when a class is read that is
* not known to the current ClassLoader
*/
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
Object[] data = (Object[]) ois.readObject();
for (int i=data.length-1;i>=0;i--){
Object[] pair = (Object[]) data[i];
this.append((Integer)pair[0],(E)pair[1]);
}
return;
}
}
Your class should implement Parcelable and should have a static final member variable called CREATOR of type Parcelable.Creator<myObject>.
Related
When creating a live event from my android application, I can't seem to add any tags for the event. The youtube api uses the BroadcastSnippet class to set all basic info for a new live event. The class BroadcastSnippet doesn't contain a tags field. However, when I create a live event from my web browser I can add tags to the new live event. Does anyone know how I can add tags to a live event using the youtube api programmatically from my android phone?
live broadcast snippet class:
public final class LiveBroadcastSnippet extends com.google.api.client.json.GenericJson {
/**
* The date and time that the broadcast actually ended. This information is only available once
* the broadcast's state is complete. The value is specified in ISO 8601 (YYYY-MM-DDThh:mm:ss.sZ)
* format.
* The value may be {#code null}.
*/
#com.google.api.client.util.Key
private com.google.api.client.util.DateTime actualEndTime;
/**
* The date and time that the broadcast actually started. This information is only available once
* the broadcast's state is live. The value is specified in ISO 8601 (YYYY-MM-DDThh:mm:ss.sZ)
* format.
* The value may be {#code null}.
*/
#com.google.api.client.util.Key
private com.google.api.client.util.DateTime actualStartTime;
/**
* The ID that YouTube uses to uniquely identify the channel that is publishing the broadcast.
* The value may be {#code null}.
*/
#com.google.api.client.util.Key
private java.lang.String channelId;
/**
* The broadcast's description. As with the title, you can set this field by modifying the
* broadcast resource or by setting the description field of the corresponding video resource.
* The value may be {#code null}.
*/
#com.google.api.client.util.Key
private java.lang.String description;
/**
* The date and time that the broadcast was added to YouTube's live broadcast schedule. The value
* is specified in ISO 8601 (YYYY-MM-DDThh:mm:ss.sZ) format.
* The value may be {#code null}.
*/
#com.google.api.client.util.Key
private com.google.api.client.util.DateTime publishedAt;
/**
* The date and time that the broadcast is scheduled to end. The value is specified in ISO 8601
* (YYYY-MM-DDThh:mm:ss.sZ) format.
* The value may be {#code null}.
*/
#com.google.api.client.util.Key
private com.google.api.client.util.DateTime scheduledEndTime;
/**
* The date and time that the broadcast is scheduled to start. The value is specified in ISO 8601
* (YYYY-MM-DDThh:mm:ss.sZ) format.
* The value may be {#code null}.
*/
#com.google.api.client.util.Key
private com.google.api.client.util.DateTime scheduledStartTime;
/**
* A map of thumbnail images associated with the broadcast. For each nested object in this object,
* the key is the name of the thumbnail image, and the value is an object that contains other
* information about the thumbnail.
* The value may be {#code null}.
*/
#com.google.api.client.util.Key
private ThumbnailDetails thumbnails;
/**
* The broadcast's title. Note that the broadcast represents exactly one YouTube video. You can
* set this field by modifying the broadcast resource or by setting the title field of the
* corresponding video resource.
* The value may be {#code null}.
*/
#com.google.api.client.util.Key
private java.lang.String title;
/**
* The date and time that the broadcast actually ended. This information is only available once
* the broadcast's state is complete. The value is specified in ISO 8601 (YYYY-MM-DDThh:mm:ss.sZ)
* format.
* #return value or {#code null} for none
*/
public com.google.api.client.util.DateTime getActualEndTime() {
return actualEndTime;
}
/**
* The date and time that the broadcast actually ended. This information is only available once
* the broadcast's state is complete. The value is specified in ISO 8601 (YYYY-MM-DDThh:mm:ss.sZ)
* format.
* #param actualEndTime actualEndTime or {#code null} for none
*/
public LiveBroadcastSnippet setActualEndTime(com.google.api.client.util.DateTime actualEndTime) {
this.actualEndTime = actualEndTime;
return this;
}
/**
* The date and time that the broadcast actually started. This information is only available once
* the broadcast's state is live. The value is specified in ISO 8601 (YYYY-MM-DDThh:mm:ss.sZ)
* format.
* #return value or {#code null} for none
*/
public com.google.api.client.util.DateTime getActualStartTime() {
return actualStartTime;
}
/**
* The date and time that the broadcast actually started. This information is only available once
* the broadcast's state is live. The value is specified in ISO 8601 (YYYY-MM-DDThh:mm:ss.sZ)
* format.
* #param actualStartTime actualStartTime or {#code null} for none
*/
public LiveBroadcastSnippet setActualStartTime(com.google.api.client.util.DateTime actualStartTime) {
this.actualStartTime = actualStartTime;
return this;
}
/**
* The ID that YouTube uses to uniquely identify the channel that is publishing the broadcast.
* #return value or {#code null} for none
*/
public java.lang.String getChannelId() {
return channelId;
}
/**
* The ID that YouTube uses to uniquely identify the channel that is publishing the broadcast.
* #param channelId channelId or {#code null} for none
*/
public LiveBroadcastSnippet setChannelId(java.lang.String channelId) {
this.channelId = channelId;
return this;
}
/**
* The broadcast's description. As with the title, you can set this field by modifying the
* broadcast resource or by setting the description field of the corresponding video resource.
* #return value or {#code null} for none
*/
public java.lang.String getDescription() {
return description;
}
/**
* The broadcast's description. As with the title, you can set this field by modifying the
* broadcast resource or by setting the description field of the corresponding video resource.
* #param description description or {#code null} for none
*/
public LiveBroadcastSnippet setDescription(java.lang.String description) {
this.description = description;
return this;
}
/**
* The date and time that the broadcast was added to YouTube's live broadcast schedule. The value
* is specified in ISO 8601 (YYYY-MM-DDThh:mm:ss.sZ) format.
* #return value or {#code null} for none
*/
public com.google.api.client.util.DateTime getPublishedAt() {
return publishedAt;
}
/**
* The date and time that the broadcast was added to YouTube's live broadcast schedule. The value
* is specified in ISO 8601 (YYYY-MM-DDThh:mm:ss.sZ) format.
* #param publishedAt publishedAt or {#code null} for none
*/
public LiveBroadcastSnippet setPublishedAt(com.google.api.client.util.DateTime publishedAt) {
this.publishedAt = publishedAt;
return this;
}
/**
* The date and time that the broadcast is scheduled to end. The value is specified in ISO 8601
* (YYYY-MM-DDThh:mm:ss.sZ) format.
* #return value or {#code null} for none
*/
public com.google.api.client.util.DateTime getScheduledEndTime() {
return scheduledEndTime;
}
/**
* The date and time that the broadcast is scheduled to end. The value is specified in ISO 8601
* (YYYY-MM-DDThh:mm:ss.sZ) format.
* #param scheduledEndTime scheduledEndTime or {#code null} for none
*/
public LiveBroadcastSnippet setScheduledEndTime(com.google.api.client.util.DateTime scheduledEndTime) {
this.scheduledEndTime = scheduledEndTime;
return this;
}
/**
* The date and time that the broadcast is scheduled to start. The value is specified in ISO 8601
* (YYYY-MM-DDThh:mm:ss.sZ) format.
* #return value or {#code null} for none
*/
public com.google.api.client.util.DateTime getScheduledStartTime() {
return scheduledStartTime;
}
/**
* The date and time that the broadcast is scheduled to start. The value is specified in ISO 8601
* (YYYY-MM-DDThh:mm:ss.sZ) format.
* #param scheduledStartTime scheduledStartTime or {#code null} for none
*/
public LiveBroadcastSnippet setScheduledStartTime(com.google.api.client.util.DateTime scheduledStartTime) {
this.scheduledStartTime = scheduledStartTime;
return this;
}
/**
* A map of thumbnail images associated with the broadcast. For each nested object in this object,
* the key is the name of the thumbnail image, and the value is an object that contains other
* information about the thumbnail.
* #return value or {#code null} for none
*/
public ThumbnailDetails getThumbnails() {
return thumbnails;
}
/**
* A map of thumbnail images associated with the broadcast. For each nested object in this object,
* the key is the name of the thumbnail image, and the value is an object that contains other
* information about the thumbnail.
* #param thumbnails thumbnails or {#code null} for none
*/
public LiveBroadcastSnippet setThumbnails(ThumbnailDetails thumbnails) {
this.thumbnails = thumbnails;
return this;
}
/**
* The broadcast's title. Note that the broadcast represents exactly one YouTube video. You can
* set this field by modifying the broadcast resource or by setting the title field of the
* corresponding video resource.
* #return value or {#code null} for none
*/
public java.lang.String getTitle() {
return title;
}
/**
* The broadcast's title. Note that the broadcast represents exactly one YouTube video. You can
* set this field by modifying the broadcast resource or by setting the title field of the
* corresponding video resource.
* #param title title or {#code null} for none
*/
public LiveBroadcastSnippet setTitle(java.lang.String title) {
this.title = title;
return this;
}
#Override
public LiveBroadcastSnippet set(String fieldName, Object value) {
return (LiveBroadcastSnippet) super.set(fieldName, value);
}
#Override
public LiveBroadcastSnippet clone() {
return (LiveBroadcastSnippet) super.clone();
}
}
JSONArray.getString(1); where ["name",null] is the json array, throws a JSONArray[1] not a string exception only when proguard is enabled.
Am I missing something in my proguard rules?
Did you try these methods:
/**
* Returns the value at {#code index} if it exists, coercing it if
* necessary. Returns the empty string if no such value exists.
*/
public String optString(int index)
or
/**
* Returns the value at {#code index} if it exists, coercing it if
* necessary. Returns {#code fallback} if no such value exists.
*/
public String optString(int index, String fallback)
Seem like does not return null for String
when we want to get value of a key we should do something like this.
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
String syncConnPref = sharedPref.getString(SettingsActivity.KEY_PREF_SYNC_CONN, "defVal");
in getString document wrote.
/**
* Retrieve a set of String values from the preferences.
*
* <p>Note that you <em>must not</em> modify the set instance returned
* by this call. The consistency of the stored data is not guaranteed
* if you do, nor is your ability to modify the instance at all.
*
* #param key The name of the preference to retrieve.
* **#param defValues Values to return if this preference does not** exist.
*
* #return Returns the preference values if they exist, or defValues.
* Throws ClassCastException if there is a preference with this name
* that is not a Set.
*
* #throws ClassCastException
*/
now i have a question why default value argument must exist!?
Because the preference may not have been saved yet. It's easier to deal with this than to write:
String value;
if (sharedPreferences.contains(PREFS_KEY)) {
value = sharedPreferences.getString(PREFS_KEY);
} else {
value = "defaultValue";
}
The answer is in the documentation. What if you call
String syncConnPref = sharedPref.getString(SettingsActivity.KEY_PREF_SYNC_CONN, "defVal");
before saving any value? It will have to return null, which in turn can create problems like NullPointerException. So, default value is used as a precaution.
I have this problem of recreating an array of a custom data type after it was written to a parcel.
playerwear is declared as such:
private Wearable playerWear[] = new Wearable[5];
This is the lines that write it to a parcel in the parent class.
It is an array of type Wearable with 5 elements
parcel.writeArray(playerWear);
This is the line that reads it from the Parcel (temp is an empty data element of the parent and has all elements filled in the same fashion as bellow. All others are working)
temp.playerWear = (Wearable[])source.readArray(Wearable.class.getClassLoader());
Here is the code of the Parcel Interface that are defined in the Wearable datatype:
#Override
public int describeContents()
{
return 0;
}
/**
* This is the main method of the Parcelable Interface. This packs everything
* up into the parcel that will be used in the next activity
* #param parcel the parcel that will be passed to the next activity
* #param i used to keep track of the size...I think
*/
#Override
public void writeToParcel(Parcel parcel, int i)
{
parcel.writeString(slotName);
parcel.writeParcelable(heldItem, i);
if (holdingItem)
parcel.writeString("True");
else
parcel.writeString("False");
}
/**
* This is the method that takes the passed parcel and rebuilds it so that it can be used
* #param source the passed parcel
* #return the reconstructed data type
*/
public static Wearable recreateParcel(Parcel source)
{
Wearable temp = new Wearable();
temp.slotName = source.readString();
temp.heldItem = (Item) source.readParcelable(Item.class.getClassLoader());
String bool = source.readString();
if(bool.equals("True"))
temp.holdingItem = true;
else
temp.holdingItem = false;
return temp;
}
/**
* Not truly sure what this is but I think this is similar to the classLoader
*/
public static final Parcelable.Creator CREATOR = new Parcelable.Creator()
{
/**
* Calls the method that I created to recreate the Data type
* #param in the passed parcel
* #return s the return of the recreateParcel method
*/
public Wearable createFromParcel(Parcel in)
{
return recreateParcel(in);
}
/**
* Called if the custom data type is in an array
* #param size the size of the array. Used to figure out how many elements are needed
* #return an Array of the Data type
*/
public Wearable[] newArray(int size)
{
return new Wearable[size];
}
};
Lastly this is the error that it is giving me,
Caused by: java.lang.ClassCastException: java.lang.Object[] cannot be cast to com.rtbd.storytime.Wearable[]
Please post if I forgot any vital information that is needed to solve this problem.
Thanks!
After doing a lot more research I found the answer at:
Read & writing arrays of Parcelable objects
You don't want to write as a parcelableArray or as a Array you want to write it as a TypedArray and then call the CREATOR of the data type in the createTypedArray method.
I am able to listen to SMS that arrives but after searching I can't find a way to listen for emails that arrive in GMAIL. I will take any way possible on any level of the OS. Thanks.
See GmailContract of DashClock Widget for a working example.
Copy/pasting the source code below for reference just in case if the URL becomes unavailable.
/*
* Copyright 2012 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.apps.dashclock.gmail;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.net.Uri;
import android.text.TextUtils;
/**
* <p>Contract for use with the Gmail content provider.</p>
*
* <p>Developers can use this content provider to display label information to the user.
* <br/>
* The label information includes:
* <ul>
* <li>Label name</li>
* <li>Total number of conversations</li>
* <li>Number of unread conversations</li>
* <li>Label text color</li>
* <li>Label background color</li>
* </ul></p>
*
* <p>This content provider is available in Gmail version 2.3.6 or newer for Froyo/Gingerbread
* and version 4.0.5 and newer for Honeycomb and Ice Cream Sandwich</p>
* <p>An application can query the
* <a href="http://developer.android.com/reference/android/content/ContentResolver.html">
* Content Resolver</a> directly
* (or use a <a href="http://developer.android.com/guide/topics/fundamentals/loaders.html"
* target="_blank">Loader</a>)
* to obtain a Cursor with information for all labels on an account</p>
* <code>Cursor labelsCursor = getContentResolver().query(GmailContract.Labels.getLabelsUri(
* selectedAccount), null, null, null, null);</code>
*/
public final class GmailContract {
private GmailContract() {}
/**
* Permission required to access this {#link android.content.ContentProvider}
*/
public static final String PERMISSION =
"com.google.android.gm.permission.READ_CONTENT_PROVIDER";
/**
* Authority for the Gmail content provider.
*/
public static final String AUTHORITY = "com.google.android.gm";
static final String LABELS_PARAM = "/labels";
static final String LABEL_PARAM = "/label/";
static final String BASE_URI_STRING = "content://" + AUTHORITY;
static final String PACKAGE = "com.google.android.gm";
/**
* Check if the installed Gmail app supports querying for label information.
*
* #param c an application Context
* #return true if it's safe to make label API queries
*/
public static boolean canReadLabels(Context c) {
boolean supported = false;
try {
final PackageInfo info = c.getPackageManager().getPackageInfo(PACKAGE,
PackageManager.GET_PROVIDERS | PackageManager.GET_PERMISSIONS);
boolean allowRead = false;
if (info.permissions != null) {
for (int i = 0, len = info.permissions.length; i < len; i++) {
final PermissionInfo perm = info.permissions[i];
if (PERMISSION.equals(perm.name)
&& perm.protectionLevel < PermissionInfo.PROTECTION_SIGNATURE) {
allowRead = true;
break;
}
}
}
if (allowRead && info.providers != null) {
for (int i = 0, len = info.providers.length; i < len; i++) {
final ProviderInfo provider = info.providers[i];
if (AUTHORITY.equals(provider.authority) &&
TextUtils.equals(PERMISSION, provider.readPermission)) {
supported = true;
}
}
}
} catch (NameNotFoundException e) {
// Gmail app not found
}
return supported;
}
/**
* Table containing label information.
*/
public static final class Labels {
/**
* Label canonical names for default Gmail system labels.
*/
public static final class LabelCanonicalNames {
/**
* Canonical name for the Inbox label
*/
public static final String CANONICAL_NAME_INBOX = "^i";
/**
* Canonical name for the Priority Inbox label
*/
public static final String CANONICAL_NAME_PRIORITY_INBOX = "^iim";
/**
* Canonical name for the Starred label
*/
public static final String CANONICAL_NAME_STARRED = "^t";
/**
* Canonical name for the Sent label
*/
public static final String CANONICAL_NAME_SENT = "^f";
/**
* Canonical name for the Drafts label
*/
public static final String CANONICAL_NAME_DRAFTS = "^r";
/**
* Canonical name for the All Mail label
*/
public static final String CANONICAL_NAME_ALL_MAIL = "^all";
/**
* Canonical name for the Spam label
*/
public static final String CANONICAL_NAME_SPAM = "^s";
/**
* Canonical name for the Trash label
*/
public static final String CANONICAL_NAME_TRASH = "^k";
private LabelCanonicalNames() {}
}
/**
* The MIME-type of uri providing a directory of
* label items.
*/
public static final String CONTENT_TYPE =
"vnd.android.cursor.dir/vnd.com.google.android.gm.label";
/**
* The MIME-type of a label item.
*/
public static final String CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/vnd.com.google.android.gm.label";
/**
* This string value is the canonical name of a label. Canonical names are not localized and
* are not user-facing.
*
* <p>Type: TEXT</p>
*/
public static final String CANONICAL_NAME = "canonicalName";
/**
* This string value is the user-visible name of a label. Names of system labels
* (Inbox, Sent, Drafts...) are localized.
*
* <p>Type: TEXT</p>
*/
public static final String NAME = "name";
/**
* This integer value is the number of conversations in this label.
*
* <p>Type: INTEGER</p>
*/
public static final String NUM_CONVERSATIONS = "numConversations";
/**
* This integer value is the number of unread conversations in this label.
*
* <p>Type: INTEGER</p>
*/
public static final String NUM_UNREAD_CONVERSATIONS = "numUnreadConversations";
/**
* This integer value is the label's foreground text color in 32-bit 0xAARRGGBB format.
*
* <p>Type: INTEGER</p>
*/
public static final String TEXT_COLOR = "text_color";
/**
* This integer value is the label's background color in 32-bit 0xAARRGGBB format.
*
* <p>Type: INTEGER</p>
*/
public static final String BACKGROUND_COLOR = "background_color";
/**
* This string column value is the uri that can be used in subsequent calls to
* {#link android.content.ContentProvider#query()} to query for information on the single
* label represented by this row.
*
* <p>Type: TEXT</p>
*/
public static final String URI = "labelUri";
/**
* Returns a URI that, when queried, will return the list of labels for an
* account.
* <p>
* To use the Labels API, an app must first find the email address of a
* valid Gmail account to query for label information. The <a href=
* "http://developer.android.com/reference/android/accounts/AccountManager.html"
* target="_blank">AccountManager</a> can return this information (<a
* href="https://developers.google.com/gmail/android">example</a>).
* </p>
*
* #param account Name of a valid Google account.
* #return The URL that can be queried to retrieve the the label list.
*/
public static Uri getLabelsUri(String account) {
return Uri.parse(BASE_URI_STRING + "/" + account + LABELS_PARAM);
}
private Labels() {}
}
}
Hope this helps.
Getting direct access the actual message is not possible to my knowledge. Best way I know is to use the Java MAIL for Android library (http://code.google.com/p/javamail-android/) and use the IMAP protocol to grab the message you are looking for.
But your actual question was about getting events from gmail. To get them, you can use a ContentObserver. First, declare you own subclass of ContentObserver:
class GmailContentObserver extends ContentObserver {
public GmailContentObserver(Handler handler) {
super(handler);
}
#Override
public void onChange(boolean selfChange) {
onChange(selfChange, null);
}
#Override
public void onChange(boolean selfChange, Uri uri) {
// This is where you will get your change notifications
// for each received e-mails. This method will be called multiple times per
// received e-mail. At least 3, but it depends on the way you register
// your observer and on your gmail account set up (number of labels).
// You will also get calls for other events. Remember that this method is
// called for any change on the local gmail repository. Receiving an e-mail
// is only one of many actions that will change this repository.
Toast.makeText(MainActivity.this,"gmail event",Toast.LENGTH_LONG).show();
}
}
Then register your ContentObserver:
GmailContentObserver gco = new GmailContentObserver(new Handler());
ContentResolver cr = getContentResolver();
cr.registerContentObserver(Uri.parse("content://gmail-ls"),true,gco);
That's it!
Note that you should also unregister your observer when you don't need it anymore or when your application/service shuts down.
Note also the new Hanlder() when instantiating your observer. This will determine on which thread the onChange methods will be called. The thread on which the Handler object was created is the one on which onChange will be called (in my sample, you can't see this, but it is the UI thread, the one calling onCreate in my Activity).
When registering your observer, there are two parameters: the URI you want to observe and whether you want to observer the descendents. In the above sample code, you will get a notification for the URI content://gmail-ls but also for example for content://gmail-ls/foo#gmail.com, foo#gmail.com being your gmail account. And there are many more descendent URIs (especially labels). To get fewer events, you can register your observer for content://gmail-ls/foo#gmail.com and set the second parameter to false.
Given this mechanism, in theory if you wanted to observe changes on a given gmail Label, you could query for the Label id (see gmail api) and register the corresponding URI content://gmail-ls/foo#gmail.com/label/80, 80 being the label id. But for some reason this does not work.
Also check the gmail api already mentioned for code to determine if the installed gmail app has a content provider (depends on the version of gmail).
If you want to get the content of the email, I would recommend using the Gmail REST API is very good (but you need to have user authorise within your app).
var url = 'https://www.googleapis.com/gmail/v1/users/me/messages?q=in%3Ainbox&'
We have used this in our app Speaking Email (shameless plug) which is a cordova app using javascript and the gmail API works very well for us.