Wednesday, December 28, 2011

Android Tutorial: Preferences

Tutorial: Preferences

11.1. Using preferences

We will continue using the example project "de.vogella.android.social".

Create an Android XML resource "preferences.xml" of type "PreferenceScreen".

How to create a XML file for storing preference value definitions

Open the file via right-mouse click and Open-withAndroid XML Resource Editor. Press Add, add a "PreferenceCategory" and add two preferences "EditTextPreferences" to this category : "User" and "Password".

Adding a category to the preference XML file

Adding the field "user" to the preference XML file

Adding the field "password" to the preference XML file

You can also maintain other properties to EditTextField, e.g. the inputMethod. Add for example the following attribute to the XML definition of your password to make the input quoted with *.

      android:inputType="textPassword"     

Create the class "MyPreferencesActivity" with extends PreferenceActivity. This activity will load the "preference.xml" and will allow to maintain the values.

     package de.vogella.android.socialapp;  import android.os.Bundle; import android.preference.PreferenceActivity;  public class MyPreferencesActivity extends PreferenceActivity {  @Override  public void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);      addPreferencesFromResource(R.xml.preferences);  } }     

To make this class available as an activity for Android you need to register it in your "AndroidManifest.xml" file. Select "AndroidManifest.xml" and the tab "Application". Scroll to the botton of the view and add your new activity via the "Add" button.

How to define a new activity in the AndroidManifest.xml

To make use of our new preference activity and the preference values we adjust the "OverviewActivity". The first button will show the current values of the preferences via a Toast and the second button will revert the maintained user name to demonstrate how you could change the preferences via code.

     package de.vogella.android.socialapp;  import android.app.Activity; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.os.Bundle; import android.preference.PreferenceManager; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast;  public class OverviewActivity extends Activity {  SharedPreferences preferences;  @Override  public void onCreate(Bundle savedInstanceState) {   super.onCreate(savedInstanceState);   setContentView(R.layout.main);   Button button = (Button) findViewById(R.id.Button01);   // Initialize preferences   preferences = PreferenceManager.getDefaultSharedPreferences(this);    button.setOnClickListener(new OnClickListener() {    public void onClick(View v) {     String username = preferences.getString("username", "n/a");     String password = preferences.getString("password", "n/a");     showPrefs(username, password);    }   });    Button buttonChangePreferences = (Button) findViewById(R.id.Button02);   buttonChangePreferences.setOnClickListener(new OnClickListener() {    public void onClick(View v) {          updatePreferenceValue();    }   });  }    private void showPrefs(String username, String password){   Toast.makeText(     OverviewActivity.this,     "Input: " + username + " and password: "       + password, Toast.LENGTH_LONG).show();   }    private void updatePreferenceValue(){   Editor edit = preferences.edit();   String username = preferences.getString("username", "n/a");   // We will just revert the current user name and save again   StringBuffer buffer = new StringBuffer();   for (int i = username.length() - 1; i >= 0; i--) {    buffer.append(username.charAt(i));   }   edit.putString("username", buffer.toString());   edit.commit();   // A toast is a view containing a quick little message for the   // user. We give a little feedback   Toast.makeText(OverviewActivity.this,     "Reverted string sequence of user name.",     Toast.LENGTH_LONG).show();  }     

To open the new preference activity we will use the method onOptionsItemSelected(). Even though we currently have only one option in our menu we use a switch to be ready for several new menu entries. To see the current values of the preferences we define a button and use the class "PreferenceManager" to get the sharedPreferences.

     @Override public boolean onCreateOptionsMenu(Menu menu) {  MenuInflater inflater = getMenuInflater();  inflater.inflate(R.menu.mainmenu, menu);  return true; }  // This method is called once the menu is selected @Override public boolean onOptionsItemSelected(MenuItem item) {  switch (item.getItemId()) {  // We have only one menu option  case R.id.preferences:   // Launch Preference activity   Intent i = new Intent(OverviewActivity.this, MyPreferencesActivity.class);   startActivity(i);   // Some feedback to the user   Toast.makeText(OverviewActivity.this, "Enter your user credentials.",    Toast.LENGTH_LONG).show();   break;   }  return true; }    

11.2. Run

Run your application. Press the "menu" hardware button and then select your menu item "Preferences". You should be able to enter your user settings then press the back hardware button to return to your main activity. The saved values should be displayed in a small message windows (Toast) if you press your first button. If you press the second button the username should be reversed.

The running application showing the maintenance dialog for the field "user" in the preference activity

12. Dialogs via the AlertDialog

We have already used a "Toast" which is a small message window which does not take the focus. In this chapter we will use the class "AlertDialog". AlertDialog is used to open a dialog from our activity. This modal dialog gets the focus until the user closes it.

An instance of this class can be created by the builder pattern, e.g. you can chain your method calls.

You should always open a dialog from the class onCreateDialog(int) as the Android system manages the dialog in this case for you. This method is automatically called by Android if you call showDialog(int).

Create a new Android project "de.vogella.android.alertdialog" with the activity "ShowMyDialog". Maintain the following layout for "main.xml".

             

Change the code of your activity to the following.

    package de.vogella.android.alertdialog;  import android.app.Activity; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.app.Dialog; import android.content.DialogInterface; import android.os.Bundle; import android.view.View; import android.widget.Toast;  public class ShowMyDialog extends Activity {  /** Called when the activity is first created. */  @Override  public void onCreate(Bundle savedInstanceState) {   super.onCreate(savedInstanceState);   setContentView(R.layout.main);   }   public void openMyDialog(View view) {   showDialog(10);  }   @Override  protected Dialog onCreateDialog(int id) {         switch (id) {         case 10:             // Create out AlterDialog             Builder builder = new AlertDialog.Builder(this);             builder.setMessage("This will end the activity");             builder.setCancelable(true);             builder.setPositiveButton("I agree", new DialogInterface.OnClickListener() {                 public void onClick(DialogInterface dialog, int which) {                     ShowMyDialog.this.finish();                 }             });             builder.setNegativeButton("No, no", new DialogInterface.OnClickListener() {                 public void onClick(DialogInterface dialog, int which) {                     Toast.makeText(getApplicationContext(),"Activity will continue",Toast.LENGTH_LONG).show();                 }             });             AlertDialog dialog = builder.create();             dialog.show();         }         return super.onCreateDialog(id);     }  }   

If you run your application and click your button you should see your dialog.

Showing the running application with the dialog open

More on dialogs can be found on Android Dialogs standard documentation.

Starting an deployed Android application

After you ran your application on the virtual device you can start it again on the device. If you press the Home button you can also select your application.

How to select your application from the Android home menu

Selecting the application from the application choicer

8. Menus and Action Bar

8.1. Definition of menu entries

Android provides two possible ways to display global actions which the user can select. The first one is the usage of the Action Bar in the application. The Action Bar is a window feature at the top of the activity that may display the activity title, navigation modes, and other interactive items.

The second option is that the app can open a menu which show additional actions via a popup menu. Typical you define your menu entries in a way that they are added to the action bar if sufficient space is available in the action bar and if not that remaining menu items are displayed in the popup menu.

The option menu and the action bar of your activity is filled by the method onCreateOptionsMenu() of your activity.

The ActionBar also shows an icon of your application. You can also add an action to this icon. If you select this icon the onOptionsItemSelected() method will be called with the value android.R.id.home. The recommendation is to return to the main Activity in your program.

     // If home icon is clicked return to main Activity case android.R.id.home: 
Intent intent = new Intent(this, OverviewActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent); break;

In this method you can create the menu programmatically or you can use a pre-defined XML resources which you inflate via the class "MenuInflator". Each activity has already an instance of the class available and this instance can get accessed via the method getMenuInflator().

onCreateContextMenu() is only called once. If you want to influence the menu later you have to use the method onPrepareOptionsMenu().

8.2. Action bar tabs

It is also possible to add tabs to an action bar.

8.3. Context menus

You can also assign a context menu to an UI widget (view). A context menu is activated if the user "long presses" the view.

A context menu for a view is registered via the method registerForContextMenu(view). The method onCreateContextMenu() is called every time a context menu is activated as the context menu is discarded after its usage. The Android platform may also add options to your view, e.g. "EditText" provides context options to select text, etc.

9. Tutorial: Menus and Action Bar

9.1. Project

This chapter will demonstrate how to create and evaluate a option menu which is displayed in the action bar if sufficient space is available. This example will be extended in the chapter about preferences.

Create a project "de.vogella.android.socialapp" with the activity "OverviewActivity". Change the UI in the file "/res/layout/main.xml" to the following:

                        

9.2. Add a menu XML resource

Select your project, right click on it and select NewOtherAndroidAndroid XML File to create a new XML resource.

Select the option "Menu", enter as File "mainmenu.xml" and press the button "Finish".

Creating a new XML resource for the menu

This will create a new file "mainmenu.xml" in the folder "res/menu" of your project. Android provides an nice editor to edit this file, unfortunately this editor is not always automatically used. To use this editor right-click on your menu file and select Open withAndroid Menu Editor.

Switch if necessary to the "Layout" tab of the editor. Press Add and select "Item". Maintain the following value. This defines the entries in your menu. We will also define that the menu entry is displayed in the action bar if there is sufficient space available.

How to maintain the menu entries in an menu xml file

Change your Activity class "OverviewActivity" to the following. The OnCreateOptionsMenu method is used to create the menu. The behavior in "onOptionsItemSelected" is currently hard-coded to show a Toast and will soon call the preference settings. In case you want to disable or hide menu items you can use the method "onPrepareOptionsMenu" which is called every time the menu is called.

     package de.vogella.android.socialapp;  import android.app.Activity; import android.os.Bundle;
import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.widget.Toast;
public class OverviewActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState)
{ super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override public boolean onCreateOptionsMenu(Menu menu)
{ MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.mainmenu, menu); return true; }
@Override public boolean onOptionsItemSelected(MenuItem item) { Toast.makeText(this, "Just a test", Toast.LENGTH_SHORT).show(); return true; } }

Run your application. As there is enough space in the action bar your item will be displayed there. If there would be more items you could press "Menu" on the emulator to see them. If you select the menu item you should see a small info message.

Screenshot of the running application with the menu open

The two "Preference" buttons are not yet active. We will use them in the next chapter.

10. Preferences

Android supports the usage of Preferences to allow you to save data for your application. Preferences are stored as key values. The definition of Preferences can also be done via an XML resource.

Android provides the class "PreferenceActivity" which extends the class Activity. PreferenceActivity supports the simple handling of preferences. This activity can load a preference definition resources via the method addPreferencesFromResource().

To communicate between different components Android uses Intents. Typically the PreferenceActivity is started from another activity via an Intent.

In your application you can access the preference manager via the following:

    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);   

Values can get access via the key of the preference setting.

    String username = preferences.getString("username", "n/a");   

To create or change preferences you have to call the edit() methods. Once you have changed the value you have to call commit() to apply your changes.

     Editor edit = preferences.edit(); edit.putString("username", "new_value_for_user"); edit.commit();    

Your first Android project

6. Your first Android project

6.1. Create Project

This app is also available on the Android Marketplace. Search for "vogella" for find this example.

Select FileNewOtherAndroidAndroid Project and create the Android project "de.vogella.android.temperature". Enter the following.

New Android Project Wizard

New Android Project Wizard - Android Target

New Android Project Wizard - Package Definition

Press "Finish". This should create the following directory structure.

Android Project Structure

While "res" contains structured values which are known to the Android platform the directory "assets" can be used to store any kind of data. In Java you can access this data via the AssetsManager and the method getAssets().

6.2. Two faces of things

The Android SDK allows to define certain artifacts, e.g. strings and UI's, in two ways, via a rich editor and directly via XML. The following description tries to use the rich UI but for validation lists also the XML. You can switch between both things by clicking on the tab on the lower part of the screen. For example in the Package Explorer select "res/layout/main.xml".

ADT Resource Editor

6.3. Create attributes

Android allows you to create attributes for resources, e.g. for strings and / or colors. These attributes can be used in your UI definition via XML or in your Java source code.

Select the file "res/values/string.xml" and press "Add". Select "Color" and enter "myColor" as the name and "#3399CC" as the value.

Adding Android Attributes

Details for a String

Add also the following "String" attributes. String attributes allow to translate the application at a later point.

Table 1. String Attributes

NameValue
celsiusto Celsius
fahrenheitto Fahrenheit
calcCalculate


Switch to the XML representation and validate the values.

        Hello World, Convert!  Temperature Converter  #3399CC  myClickHandler  to Celsius  to Fahrenheit  Calculate     

6.4. Add UI Elements

Select "res/layout/main.xml" and open the Android editor via a double-click. This editor allows you to create the UI via drag and drop or via the XML source code. You can switch between both representations via the tabs at the bottom of the editor. For changing the position and grouping elements you can use the outline view.

The following shows a screenshot of the Palette view from which you can drag and drop new UI elements into your layout. Please note that the "Palette" view changes frequently so your view might be a bit different.

Palette for the Android Layout Editor

Right-click on the text object “Hello World, Hello!” in the layout. Select Delete on the popup menu to remove the text object. Then, from the “Palette” view, select Text Fields and locate “Plain Text”. Drag this onto the layout to create a text input field. All object types in the section "Text Fields” derive from the class "EditText", they just specify via an additional attribute which text type can be used.

Now select the Palette section “Form Widgets” and drag a “RadioGroup” object onto the layout. The number of radio buttons added to the radio button group depends on your version of Eclipse. Make sure there are two radio buttons by deleting or adding radio buttons to the group.

From the Palette section Form Widgets, drag a Button object onto the layout.

The result should look like the following.

Current layout of main.xml

Switch to "main.xml" and verify that your XML looks like the following.

                          

6.5. Edit UI properties

If you select a UI element you can change its properties via the properties view. Most of the properties can be changed via the right mouse menu. You can also edit properties of fields directy in XML. Typically you change properties directly in the XML file as this is much faster. But the right mouse functionality is nice if you are searching for a certain property.

Open your file "main.xml" We will delete the initial text for the EditText field in XML. Switch to the XML tab called "main.xml" and delete the android:text="EditText" property from the EditText part. Switch back to the "Graphical Layout" tab and check that the text is removed.

Use the right mouse click on the first radio button to assign the "celsius" string attribute to its "text" property. Assign the and "fahrenheit" string attribute to the second radio button.

Change the text property of the radio button

Selection of the right text from the pre-defined string values

From now on I assume you are able to use the properties menu on the UI elements. You can either edit the XML file or modify the properties via right mouse click.

Set the property "Checked" to true for the first RadioButton. Assign "calc" to the text property of your button and assign "myClickHandler" to the "onClick" property. Set the "Input type" property to "numberSigned" and "numberDecimal" on your EditText.

All your other UI controls are contained in a LinearLayout. We want to assign a background color to this LinearLayout. Right-click on an empty space in Graphical Layout mode, then select Other PropertiesAll by NameBackground. Select “Color” and then “myColor” in the list.

New look of the layout after the changes

Switch to the "main.xml" tab and verify that the XML is correctly maintained.

                          

6.6. Code your application

During the generation of your new Android project you specified that an Activity called CovertActivity should get created. The project wizard also created the correspondig Java classs.

Change your code in ConvertActivity.java to the following. Note that the myClickHandler will be called based on the OnClick property of your button.

     package de.vogella.android.temperature;  import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.EditText; import android.widget.RadioButton; import android.widget.Toast;  public class ConvertActivity extends Activity {  private EditText text;   @Override  public void onCreate(Bundle savedInstanceState) {   super.onCreate(savedInstanceState);   setContentView(R.layout.main);   text = (EditText) findViewById(R.id.editText1);   }   // This method is called at button click because we assigned the name to the  // "On Click property" of the button  public void myClickHandler(View view) {   switch (view.getId()) {   case R.id.button1:    RadioButton celsiusButton = (RadioButton) findViewById(R.id.radio0);    RadioButton fahrenheitButton = (RadioButton) findViewById(R.id.radio1);    if (text.getText().length() == 0) {     Toast.makeText(this, "Please enter a valid number",       Toast.LENGTH_LONG).show();     return;    }     float inputValue = Float.parseFloat(text.getText().toString());    if (celsiusButton.isChecked()) {     text.setText(String       .valueOf(convertFahrenheitToCelsius(inputValue)));     celsiusButton.setChecked(false);     fahrenheitButton.setChecked(true);    } else {     text.setText(String       .valueOf(convertCelsiusToFahrenheit(inputValue)));     fahrenheitButton.setChecked(false);     celsiusButton.setChecked(true);    }    break;   }  }   // Converts to celsius  private float convertFahrenheitToCelsius(float fahrenheit) {   return ((fahrenheit - 32) * 5 / 9);  }   // Converts to fahrenheit  private float convertCelsiusToFahrenheit(float celsius) {   return ((celsius * 9) / 5) + 32;  } }    

6.7. Start Project

To start the Android Application, select your project, right click on it, Run-As-> Android Application Be patient, the emulator starts up very slow. You should get the following result.

The running application in the emulator

Type in a number, select your conversion and press the button. The result should be displayed and the other option should get selected.

Error handling and typical problems at Android

5. Error handling and typical problems

Things are not always working as they should. This section gives an overview over typical problems and how to solve them.

5.1. Clean Project

Several users report that get the following errors:

  1. Project ... is missing required source folder: 'gen'

  2. The project could not be built until build path errors are resolved.

  3. Unable to open class file R.java.

To solve any of these errors, go to the project menu and select Project -> Clean.

5.2. Problems with Android Debug Bridge (adb)

The communication with the emulator or your Android device might have problems. This communication is handle by the Android Debug Bridge (adb).

Eclipse allows to reset the adb in case this causes problems. Select therefore the DDMS perspective via WindowOpen PerspectiveOtherDDMS

To restart the adb, select the "Reset adb" in the Device View.

5.3. LogCat

The LogCat view shows you the log message of your Android device and help you analyzing problems. For example Java exceptions in your program would be shown here. To open this view, select "Window -> Show View -> Other -> Android -> LogCat" from the menu.

5.4. Emulator does not start

If your emulator does not start, make sure that the androd-sdk version is in a path without any spaces in the path name.

5.5. Error message for @override

The @override annotation was introduced in Java 1.6. If you receive an error message for @override change the Java compiler level to Java 1.6 via right-mouse click on the project -> Properties -> Java Compiler -> Compiler compliance level and set it to "1.6".

5.6. Missing Imports

Java requires that the classes which are not part of the standard Java Language are either fully qualified or declared via imports. In your editor use the click mouse click, select "Source-> Organize Imports" if you see error message with "XX cannot be resolved to a variable".

5.7. Eclipse Tips

To work more efficient with Eclipse, select Window -> Preferences -> Java -> Editor -> Save Actions and select that the source code should be formated and that the imports should be organized at every save

Installation Eclipse and automatic Android SDK

3. Installation

The following assume that you have already Eclipse installed. For details please see Eclipse Tutorial .

3.1. Eclipse and automatic Android SDK

Use the Eclipse update manager to install all available components for the Android Development Tools (ADT) from the URL https://dl-ssl.google.com/android/eclipse/. If you are not familiar with the Eclipse update manager the usage is described in Eclipse update manager.

After the new Android development components are installed you will be prompted to install the Android SDK. You can do follow the following wizard or go to the next section to learn how to do it manually.

Wizard to install Android SDK - Part 1

Wizard to install Android SDK - Part 2

Wizard to install Android SDK - Part 3

3.2. Manually install Android SDK

The previous step downloads the Android SDK automatically for you. You can also download the Android SDK manuallz from the Android homepage under Android SDK download. The download contains a zip file which you can extract to any place in your file system, e.g. I placed it under "c:\android-sdk-windows". Avoid using spaces in the path name otherwise you may experience problems later.

You also have to define the location of the Android SDK in the Eclipse Preferences. In Eclipse open the Preferences dialog via WindowsPreferences. Select Android and enter the installation path of the Android SDK.

Setting up the Android SDK in the Eclipse Preferences

3.3. Install a specific Android version

The Android SDK Manager allows you to install specific versions of Android. Select WindowAndroid SDK Manager from the Eclipse menu.

Starting ADV Manager

The dialog allows you to install new package and also allow you to delete them. Select "Available packages" and open the "Third Party Add-ons". Select the Google API 14 (Android 4.0) version of the SDK and press "Install".

Install Android API

Press the "Install" button and confirm the license for all package. After the installation restart Eclipse.

3.4. Android Source Code

The following step is optional.

During Android development it is very useful to have the Android source code available as Android uses a lot of defaults.

Haris Peco maintains plugins which provides access to the Android Source code code. Use the Eclipse update manager to install the Android Source plugin from the following update site: "http://adt-addons.googlecode.com/svn/trunk/source/com.android.ide.eclipse.source.update".

More details can be found on the project website.

4. Using the Emulator

4.1. Create an Android Emulator Device

The Android tools include an emulator. This emulator behaves like a real Android device in most cases and allows you to test your application without having a real device. You can emulate one or several devices with different configurations. Each configuration is defined via an "Android Virtual Device" (AVD).

To define an AVD open the "AVD Manager" via WindowsAVD Manager and press "New".

Create a new AVD

Enter the following.

Settings for a new AVD

We can also select the box "Enabled" for Snapshots. This will make the second start of the virtual device much faster.

At the end press the button "Create AVD".This will create the device and display it under the "Virtual devices". To test if your setup is correct, select your device and press "Start".

After (a long time) your device should be started.

4.2. Emulator Shortcuts

Obviously you can use the emulator via the keyboard on the right side of the emulator. But there are also some nice shortcuts which are useful.

Alt+Enter maximizes the emulator. Nice for demos.

Ctrl+F11 changes the orientation of the emulator.

F8 turns network on / off.

4.3. Performance

Try to use a smaller resolution for your emulator as for example HVGA. The emulator gets slower the more pixels its needs to render as it is using software rendering.

Also if you have sufficient memory on your computer, add at least 1 GB of memory to your emulator. This is the value "Device ram size" during the creation of the AVD.

Also set the flag "Enabled" for Snapshots. This will save the state of the emulator and let it start much faster.

Android Application Architecture

2. Android Application Architecture

2.1. AndroidManifest.xml

An Android application is described in the file AndroidManifest.xml. This file must declare all Activities, Services, BroadcastReceivers and ContentProvider of the application. It must also contain the required permissions for the application. For example if the application requires network access it must be specified here. AndroidManifest.xml can be thought as the deployment descriptor for an Android application.

                                                                                                           

The package attribute defines the base package for the following Java elements. It also must be unique as the Android Marketplace only allows application for a specific package once. Therefore a good habit is to use your reverse domain name as a package to avoid collisions with other developers.

android:versionName and android:versionCode specify the version of your application. versionName is what the user sees and can be any string. versionCode must be an integer and the Android Market uses this to determine if you provided a newer version to trigger the update on devices which have your application installed. You typically start with "1" and increase this value by one if you roll-out a new version of your application.

The tag defines an Activity, in this example pointing to the class "de.vogella.android.temperature.Convert". An intent filter is registered for this class which defines that this Activity is started once the application starts (action android:name="android.intent.action.MAIN" ). The category definition category android:name="android.intent.category.LAUNCHER" defines that this application is added to the application directory on the Android device. The @string/app_name value refer to resource files which contain the actual values. This makes it easy to provide different resources, e.g. strings, colors, icons, for different devices and makes it easy to translate applications.

The "uses-sdk" part of the "AndroidManifest.xml" defines the minimal SDK version your application is valid for. This will prevent your application being installed on devices with older SDK versions.

2.2. R.java, Resources and Assets

The directory gen in an Android project contains generated values. R.java is a generated class which contains references to resources of the res folder in the project. These resources are defined in the res directory and can be values, menus, layouts, icons or pictures or animations. For example a resource can be an image or an XML file which defines strings.

If you create a new resource, the corresponding reference is automatically created in R.java. The references are static int values, the Android system provides methods to access the corresponding resource. For example to access a String with the reference id R.string.yourString use the method getString(R.string.yourString));. R.java is automatically maintained by the Eclipse development environment, manual changes are not necessary.

While the directory res contains structured values which are known to the Android platform the directory assets can be used to store any kind of data. In Java you can access this data via the AssetsManager and the method getAssets().

2.3. Reference to resources in XML files

In your XML files, e.g. your layout files you can refer to other resources via the @ sign. For example if you want to refer to a color you defined as resources you can refer to it via @color/your_id or if you have defined a "hello" string as resource you can access it via @string/hello .

2.4. Activities and Layouts

The user interface for Activities is defined via layouts. At runtime, layouts are instances of android.view.ViewGroups . The layout defines the UI elements, their properties and their arrangement.

UI elements are based on the class android.view.View . ViewGroup is a subclass of the class View and a layout can contain UI components ( Views ) or other layouts ( ViewGroups ). You should not nestle ViewGroups too deeply as this has a negative impact on performance.

A layout can be defined via Java code or via XML. You typically uses Java code to generate the layout if you don't know the content until runtime; for example if your layout depends on content which you read from the Internet.

XML based layouts are defined via a resource file in the folder /res/layout . This file specifies the ViewGroups , Views , their relationship and their attributes for a specific layout. If a UI element needs to be accessed via Java code you have to give the UI element an unique id via the android:id attribute. To assign a new id to an UI element use @+id/yourvalue . By conversion this will create and assign a new id yourvalue to the corresponding UI element. In your Java code you can later access these UI elements via the method findViewById(R.id.yourvalue) .

Defining layouts via XML is usually the preferred way as this separates the programming logic from the layout definition. It also allows the definition of different layouts for different devices. You can also mix both approaches.

2.5. Activities and Lifecycle

The operating system controls the life cycle of your application. At any time the Android system may stop or destroy your application, e.g. because of an incoming call. The Android system defines a life cycle for activities via pre-defined methods. The most important methods are:

  • onSaveInstanceState() - called if the activity is stopped. Used to save data so that the activity can restore its states if re-started

  • onPause() - always called if the Activity ends, can be used to release resource or save data

  • onResume() - called if the Activity is re-started, can be used to initialize fields

The activity will also be restarted if a so called "configuration change" happens. A configuration change for example happens if the user changes the orientation of the device (vertical or horizontal). The activity is in this case restarted to enable the Android platform to load different resources for these configuration, e.g. layouts for vertical or horizontal mode. In the emulator you can simulate the change of the orientation via CNTR+F11.

You can avoid a restart of your application for certain configuration changes via the configChanges attribute on your activity definition in your AndroidManifest.xml. The following activity will not be restarted in case of orientation changes or position of the physical keyboard (hidden / visible).

          

2.6. Context

The class android.content.Context provides the connections to the Android system. It is the interface to global information about the application environment. Context also provides access to Android Services, e.g. theLocation Service. As Activities and Services extend the class Context you can directly access the context via this.

The Calendar Provider at Android

The Calendar Provider is a repository for a user's calendar events. The Calendar Provider API allows you to perform query, insert, update, and delete operations on calendars, events, attendees, reminders, and so on.

The Calender Provider API can be used by applications and sync adapters. The rules vary depending on what type of program is making the calls. This document focuses primarily on using the Calendar Provider API as an application. For a discussion of how sync adapters are different, see Sync Adapters.

Normally, to read or write calendar data, an application's manifest must include the proper permissions, described in User Permissions. To make performing common operations easier, the Calendar Provider offers a set of intents, as described in Calendar Intents. These intents take users to the Calendar application to insert, view, and edit events. The user interacts with the Calendar application and then returns to the original application. Thus your application doesn't need to request permissions, nor does it need to provide a user interface to view or create events.

Basics

Content providers store data and make it accessible to applications. The content providers offered by the Android platform (including the Calendar Provider) typically expose data as a set of tables based on a relational database model, where each row is a record and each column is data of a particular type and meaning. Through the Calendar Provider API, applications and sync adapters can get read/write access to the database tables that hold a user's calendar data.

Every content provider exposes a public URI (wrapped as a Uri object) that uniquely identifies its data set. A content provider that controls multiple data sets (multiple tables) exposes a separate URI for each one. All URIs for providers begin with the string "content://". This identifies the data as being controlled by a content provider. The Calendar Provider defines constants for the URIs for each of its classes (tables). These URIs have the format .CONTENT_URI. For example, Events.CONTENT_URI.

Figure 1 shows a graphical representation of the Calendar Provider data model. It shows the main tables and the fields that link them to each other.

Calendar Provider Data Model

Figure 1. Calendar Provider data model.

A user can have multiple calendars, and different calendars can be associated with different types of accounts (Google Calendar, Exchange, and so on).

The CalendarContract defines the data model of calendar and event related information. This data is stored in a number of tables, listed below.

Table (Class) Description

CalendarContract.Calendars

This table holds the calendar-specific information. Each row in this table contains the details for a single calendar, such as the name, color, sync information, and so on.
CalendarContract.Events This table holds the event-specific information. Each row in this table has the information for a single event—for example, event title, location, start time, end time, and so on. The event can occur one-time or can recur multiple times. Attendees, reminders, and extended properties are stored in separate tables. They each have an EVENT_ID that references the _ID in the Events table.
CalendarContract.Instances This table holds the start and end time for each occurrence of an event. Each row in this table represents a single event occurrence. For one-time events there is a 1:1 mapping of instances to events. For recurring events, multiple rows are automatically generated that correspond to multiple occurrences of that event.
CalendarContract.Attendees This table holds the event attendee (guest) information. Each row represents a single guest of an event. It specifies the type of guest and the guest's attendance response for the event.
CalendarContract.Reminders This table holds the alert/notification data. Each row represents a single alert for an event. An event can have multiple reminders. The maximum number of reminders per event is specified in MAX_REMINDERS, which is set by the sync adapter that owns the given calendar. Reminders are specified in minutes before the event and have a method that determines how the user will be alerted.

The Calendar Provider API is designed to be flexible and powerful. At the same time, it's important to provide a good end user experience and protect the integrity of the calendar and its data. To this end, here are some things to keep in mind when using the API:

  • Inserting, updating, and viewing calendar events. To directly insert, modify, and read events from the Calendar Provider, you need the appropriate permissions. However, if you're not building a full-fledged calendar application or sync adapter, requesting these permissions isn't necessary. You can instead use intents supported by Android's Calendar application to hand off read and write operations to that application. When you use the intents, your application sends users to the Calendar application to perform the desired operation in a pre-filled form. After they're done, they're returned to your application. By designing your application to perform common operations through the Calendar, you provide users with a consistent, robust user interface. This is the recommended approach. For more information, see Calendar Intents.
  • Sync adapters. A sync adapter synchronizes the calendar data on a user's device with another server or data source. In the CalendarContract.Calendars and CalendarContract.Events tables, there are columns that are reserved for the sync adapters to use. The provider and applications should not modify them. In fact, they are not visible unless they are accessed as a sync adapter. For more information about sync adapters, see Sync Adapters.

User Permissions

To read calendar data, an application must include the READ_CALENDAR permission in its manifest file. It must include the WRITE_CALENDAR permission to delete, insert or update calendar data:

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

xmlns:android="http://schemas.android.com/apk/res/android"...>
android:minSdkVersion="14" />
android:name="android.permission.READ_CALENDAR" />
android:name="android.permission.WRITE_CALENDAR" />
...

Calendars Table

The CalendarContract.Calendars table contains details for individual calendars. The following Calendars columns are writable by both an application and a sync adapter. For a full list of supported fields, see the CalendarContract.Calendars reference.

Constant Description
NAME The name of the calendar.
CALENDAR_DISPLAY_NAME The name of this calendar that is displayed to the user.
VISIBLE A boolean indicating whether the calendar is selected to be displayed. A value of 0 indicates that events associated with this calendar should not be shown. A value of 1 indicates that events associated with this calendar should be shown. This value affects the generation of rows in the CalendarContract.Instances table.
SYNC_EVENTS A boolean indicating whether the calendar should be synced and have its events stored on the device. A value of 0 says do not sync this calendar or store its events on the device. A value of 1 says sync events for this calendar and store its events on the device.

Querying a calendar

Here is an example that shows how to get all the calendars for a particular user. For simplicity's sake, in this example the query operation is shown in the user interface thread ("main thread"). In practice, this should be done in an asynchronous thread instead of on the main thread. For more discussion, see Loaders. If you are not just reading data but modifying it, see AsyncQueryHandler.

  // Projection array. Creating indices for this array instead of doing

// dynamic lookups improves performance.
public static final String[] EVENT_PROJECTION = new String[] {
Calendars._ID, // 0
Calendars.ACCOUNT_NAME, // 1
Calendars.CALENDAR_DISPLAY_NAME // 2
};

// The indices for the projection array above.
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;

In the next part of the example, you construct your query. The selection specifies the criteria for the query. In this example the query is looking for all calendars that have the ACCOUNT_NAME "sampleuser@google.com" and the ACCOUNT_TYPE "com.google". The query returns a Cursor object that you can use to traverse the result set returned by the database query. For more discussion of using queries in content providers, see Content Providers.

// Run query

Cursor cur = null;
ContentResolver cr = getContentResolver();
Uri uri = Calendars.CONTENT_URI;
String selection = "((" + Calendars.ACCOUNT_NAME + " = ?) AND ("
+ Calendars.ACCOUNT_TYPE + " = ?))";
String[] selectionArgs = new String[] {"sampleuser@gmail.com", "com.google"};
// Submit the query and get a Cursor object back.
cur
= cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);

This next section uses the cursor to step through the result set. It uses the constants that were set up at the beginning of the example to return the values for each field.

// Use the cursor to step through the returned records

while (cur.moveToNext()) {
long calID = 0;
String displayName = null;
String accountName = null;

// Get the field values
calID
= cur.getLong(PROJECTION_ID_INDEX);
displayName
= cur.getString(PROJECTION_DISPLAY_NAME_INDEX);
accountName
= cur.getString(PROJECTION_ACCOUNT_NAME_INDEX);

// Do something with the values...

...
}

Modifying a calendar

To perform an update of an calendar, you can provide the _ID of the calendar either as an appended ID to the Uri (withAppendedId()) or as the first selection item. The selection should start with "_id=?", and the first selectionArg should be the _ID of the calendar. You can also do updates by encoding the ID in the URI. This example changes a calendar's display name using the (withAppendedId()) approach:

private static final String DEBUG_TAG = "MyActivity";

...
long calID = 2;
ContentValues values = new ContentValues();
// The new display name for the calendar
values
.put(Calendars.CALENDAR_DISPLAY_NAME, "Trevor's Calendar");
Uri updateUri = ContentUris.withAppendedId(Calendars.CONTENT_URI, calID);
int rows = getContentResolver().update(updateUri, values, null, null);
Log.i(DEBUG_TAG, "Rows updated: " + rows);

Inserting a calendar

Calendars are designed to be primarily managed by a sync adapter, so you should only insert new calendars as a sync adapter. For the most part, applications can only make superficial changes to calendars, such as changing the display name. If an application needs to create a local calendar, it can do this by performing the calendar insertion as a sync adapter, using an ACCOUNT_TYPE of ACCOUNT_TYPE_LOCAL. ACCOUNT_TYPE_LOCAL is a special account type for calendars that are not associated with a device account. Calendars of this type are not synced to a server. For a discussion of sync adapters, see Sync Adapters.

Events Table

The CalendarContract.Events table contains details for individual events. To add, update, or delete events, an application must include the WRITE_CALENDAR permission in its manifest file.

The following Events columns are writable by both an application and a sync adapter. For a full list of supported fields, see the CalendarContract.Events reference.

Constant Description
CALENDAR_ID The _ID of the calendar the event belongs to.
ORGANIZER Email of the organizer (owner) of the event.
TITLE The title of the event.
EVENT_LOCATION Where the event takes place.
DESCRIPTION The description of the event.
DTSTART The time the event starts in UTC milliseconds since the epoch.
DTEND The time the event ends in UTC milliseconds since the epoch.
EVENT_TIMEZONE The time zone for the event.
EVENT_END_TIMEZONE The time zone for the end time of the event.
DURATION The duration of the event in RFC5545 format. For example, a value of "PT1H" states that the event should last one hour, and a value of "P2W" indicates a duration of 2 weeks.
ALL_DAY A value of 1 indicates this event occupies the entire day, as defined by the local time zone. A value of 0 indicates it is a regular event that may start and end at any time during a day.
RRULE The recurrence rule for the event format. For example, "FREQ=WEEKLY;COUNT=10;WKST=SU". You can find more examples here.
RDATE The recurrence dates for the event. You typically use RDATE in conjunction with RRULE to define an aggregate set of repeating occurrences. For more discussion, see the RFC5545 spec.
AVAILABILITY If this event counts as busy time or is free time that can be scheduled over.
GUESTS_CAN_MODIFY Whether guests can modify the event.
GUESTS_CAN_INVITE_OTHERS Whether guests can invite other guests.
GUESTS_CAN_SEE_GUESTS Whether guests can see the list of attendees.

Adding Events

When your application inserts a new event, we recommend that you use an INSERT Intent, as described in Using an intent to insert an event. However, if you need to, you can insert events directly. This section describes how to do this.

Here are the rules for inserting a new event:

  • You must include CALENDAR_ID and DTSTART.
  • You must include an EVENT_TIMEZONE. To get a list of the system's installed time zone IDs, use getAvailableIDs(). Note that this rule does not apply if you're inserting an event through the INSERT Intent, described in Using an intent to insert an event—in that scenario, a default time zone is supplied.
  • For non-recurring events, you must include DTEND.
  • For recurring events, you must include a DURATION in addition to RRULE or RDATE. Note that this rule does not apply if you're inserting an event through the INSERT Intent, described in Using an intent to insert an event—in that scenario, you can use an RRULE in conjunction with DTSTART and DTEND, and the Calendar application converts it to a duration automatically.

Here is an example of inserting an event. This is being performed in the UI thread for simplicity. In practice, inserts and updates should be done in an asynchronous thread to move the action into a background thread. For more information, see AsyncQueryHandler.

long calID = 3;

long startMillis = 0;
long endMillis = 0;
Calendar beginTime = Calendar.getInstance();
beginTime
.set(2012, 9, 14, 7, 30);
startMillis
= beginTime.getTimeInMillis();
Calendar endTime = Calendar.getInstance();
endTime
.set(2012, 9, 14, 8, 45);
endMillis
= endTime.getTimeInMillis();
...

ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values
.put(Events.DTSTART, startMillis);
values
.put(Events.DTEND, endMillis);
values
.put(Events.TITLE, "Jazzercise");
values
.put(Events.DESCRIPTION, "Group workout");
values
.put(Events.CALENDAR_ID, calID);
values
.put(Events.EVENT_TIMEZONE, "America/Los_Angeles");
Uri uri = cr.insert(Events.CONTENT_URI, values);

// get the event ID that is the last element in the Uri
long eventID = Long.parseLong(uri.getLastPathSegment());
//
// ... do something with event ID
//
//

Note: See how this example captures the event ID after the event is created. This is the easiest way to get an event ID. You often need the event ID to perform other calendar operations—for example, to add attendees or reminders to an event.

Updating Events

When your application wants to allow the user to edit an event, we recommend that you use an EDIT Intent, as described in Using an intent to edit an event. However, if you need to, you can edit events directly. To perform an update of an Event, you can provide the _ID of the event either as an appended ID to the Uri (withAppendedId()) or as the first selection item. The selection should start with "_id=?", and the first selectionArg should be the _ID of the event. You can also do updates using a selection with no ID. Here is an example of updating an event. It changes the title of the event using the withAppendedId() approach:

private static final String DEBUG_TAG = "MyActivity";

...
long eventID = 188;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
Uri updateUri = null;
// The new title for the event
values
.put(Events.TITLE, "Kickboxing");
myUri
= ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
int rows = getContentResolver().update(updateUri, values, null, null);
Log.i(DEBUG_TAG, "Rows updated: " + rows);

Deleting Events

You can delete an event either by its _ID as an appended ID on the URI, or by using standard selection. If you use an appended ID, you can't also do a selection. There are two versions of delete: as an application and as a sync adapter. An application delete sets the deleted column to 1. This flag that tells the sync adapter that the row was deleted and that this deletion should be propagated to the server. A sync adapter delete removes the event from the database along with all its associated data. Here is an example of application deleting an event through its _ID:

private static final String DEBUG_TAG = "MyActivity";

...
long eventID = 201;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
Uri deleteUri = null;
deleteUri
= ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
int rows = getContentResolver().delete(deleteUri, null, null);
Log.i(DEBUG_TAG, "Rows deleted: " + rows);

Attendees Table

Each row of the CalendarContract.Attendees table represents a single attendee or guest of an event. Calling query() returns a list of attendees for the event with the given EVENT_ID. This EVENT_ID must match the _ID of a particular event.

The following table lists the writable fields. When inserting a new attendee, you must include all of them except ATTENDEE_NAME.

Constant Description
EVENT_ID The ID of the event.
ATTENDEE_NAME The name of the attendee.
ATTENDEE_EMAIL The email address of the attendee.
ATTENDEE_RELATIONSHIP

The relationship of the attendee to the event. One of:

  • RELATIONSHIP_ATTENDEE
  • RELATIONSHIP_NONE
  • RELATIONSHIP_ORGANIZER
  • RELATIONSHIP_PERFORMER
  • RELATIONSHIP_SPEAKER
ATTENDEE_TYPE

The type of attendee. One of:

  • TYPE_REQUIRED
  • TYPE_OPTIONAL
ATTENDEE_STATUS

The attendance status of the attendee. One of:

  • ATTENDEE_STATUS_ACCEPTED
  • ATTENDEE_STATUS_DECLINED
  • ATTENDEE_STATUS_INVITED
  • ATTENDEE_STATUS_NONE
  • ATTENDEE_STATUS_TENTATIVE

Adding Attendees

Here is an example that adds a single attendee to an event. Note that the EVENT_ID is required:

long eventID = 202;

...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values
.put(Attendees.ATTENDEE_NAME, "Trevor");
values
.put(Attendees.ATTENDEE_EMAIL, "trevor@example.com");
values
.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
values
.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_OPTIONAL);
values
.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED);
values
.put(Attendees.EVENT_ID, eventID);
Uri uri = cr.insert(Attendees.CONTENT_URI, values);

Reminders Table

Each row of the CalendarContract.Reminders table represents a single reminder for an event. Calling query() returns a list of reminders for the event with the given EVENT_ID.

The following table lists the writable fields for reminders. All of them must be included when inserting a new reminder. Note that sync adapters specify the types of reminders they support in the CalendarContract.Calendars table. See ALLOWED_REMINDERS for details.

Constant Description
EVENT_ID The ID of the event.
MINUTES The minutes prior to the event that the reminder should fire.
METHOD

The alarm method, as set on the server. One of:

  • METHOD_ALERT
  • METHOD_DEFAULT
  • METHOD_EMAIL
  • METHOD_SMS

Adding Reminders

This example adds a reminder to an event. The reminder fires 15 minutes before the event.

long eventID = 221;

...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values
.put(Reminders.MINUTES, 15);
values
.put(Reminders.EVENT_ID, eventID);
values
.put(Reminders.METHOD, Reminders.METHOD_ALERT);
Uri uri = cr.insert(Reminders.CONTENT_URI, values);

Instances Table

The CalendarContract.Instances table holds the start and end time for occurrences of an event. Each row in this table represents a single event occurrence. The instances table is not writable and only provides a way to query event occurrences.

The following table lists some of the fields you can query on for an instance. Note that time zone is defined by KEY_TIMEZONE_TYPE and KEY_TIMEZONE_INSTANCES.

Constant Description
BEGIN The beginning time of the instance, in UTC milliseconds.
END The ending time of the instance, in UTC milliseconds.
END_DAY The Julian end day of the instance, relative to the Calendar's time zone.
END_MINUTE The end minute of the instance measured from midnight in the the Calendar's time zone.
EVENT_ID The _ID of the event for this instance.
START_DAY The Julian start day of the instance, relative to the Calendar's time zone.
START_MINUTE The start minute of the instance measured from midnight, relative to the Calendar's time zone.

Querying the Instances table

To query the Instances table, you need to specify a range time for the query in the URI. In this example, CalendarContract.Instances gets access to the TITLE field through its implementation of the CalendarContract.EventsColumns interface. In other words, TITLE is returned through a database view, not through querying the raw CalendarContract.Instances table.

private static final String DEBUG_TAG = "MyActivity";

public static final String[] INSTANCE_PROJECTION = new String[] {
Instances.EVENT_ID, // 0
Instances.BEGIN, // 1
Instances.TITLE // 2
};

// The indices for the projection array above.
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_BEGIN_INDEX = 1;
private static final int PROJECTION_TITLE_INDEX = 2;
...

// Specify the date range you want to search for recurring
// event instances
Calendar beginTime = Calendar.getInstance();
beginTime
.set(2011, 9, 23, 8, 0);
long startMillis = beginTime.getTimeInMillis();
Calendar endTime = Calendar.getInstance();
endTime
.set(2011, 10, 24, 8, 0);
long endMillis = endTime.getTimeInMillis();

Cursor cur = null;
ContentResolver cr = getContentResolver();

// The ID of the recurring event whose instances you are searching
// for in the Instances table
String selection = Instances.EVENT_ID + " = ?";
String[] selectionArgs = new String[] {"207"};

// Construct the query with the desired date range.
Uri.Builder builder = Instances.CONTENT_URI.buildUpon();
ContentUris.appendId(builder, startMillis);
ContentUris.appendId(builder, endMillis);

// Submit the query
cur
= cr.query(builder.build(),
INSTANCE_PROJECTION
,
selection
,
selectionArgs
,
null);

while (cur.moveToNext()) {
String title = null;
long eventID = 0;
long beginVal = 0;

// Get the field values
eventID
= cur.getLong(PROJECTION_ID_INDEX);
beginVal
= cur.getLong(PROJECTION_BEGIN_INDEX);
title
= cur.getString(PROJECTION_TITLE_INDEX);

// Do something with the values.
Log.i(DEBUG_TAG, "Event: " + title);
Calendar calendar = Calendar.getInstance();
calendar
.setTimeInMillis(beginVal);
DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
Log.i(DEBUG_TAG, "Date: " + formatter.format(calendar.getTime()));
}
}

Calendar Intents

Your application doesn't need permissions to read and write calendar data. It can instead use intents supported by Android's Calendar application to hand off read and write operations to that application. The following table lists the intents supported by the Calendar Provider:

Action URI Description Extras

VIEW

content://com.android.calendar/time/

You can also refer to the URI with CalendarContract.CONTENT_URI. For an example of using this intent, see Using intents to view calendar data.
Open calendar to the time specified by . None.

VIEW

content://com.android.calendar/events/

You can also refer to the URI with Events.CONTENT_URI. For an example of using this intent, see Using intents to view calendar data.
View the event specified by . CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

content://com.android.calendar/events/

You can also refer to the URI with Events.CONTENT_URI. For an example of using this intent, see Using an intent to edit an event.
Edit the event specified by . CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

INSERT

content://com.android.calendar/events

You can also refer to the URI with Events.CONTENT_URI. For an example of using this intent, see Using an intent to insert an event.
Create an event. Any of the extras listed in the table below.

The following table lists the intent extras supported by the Calendar Provider:

Intent Extra Description
Events.TITLE Name for the event.
CalendarContract.EXTRA_EVENT_BEGIN_TIME Event begin time in milliseconds from the epoch.
CalendarContract.EXTRA_EVENT_END_TIME Event end time in milliseconds from the epoch.
CalendarContract.EXTRA_EVENT_ALL_DAY A boolean that indicates that an event is all day. Value can be true or false.
Events.EVENT_LOCATION Location of the event.
Events.DESCRIPTION Event description.
Intent.EXTRA_EMAIL Email addresses of those to invite as a comma-separated list.
Events.RRULE The recurrence rule for the event.
Events.ACCESS_LEVEL Whether the event is private or public.
Events.AVAILABILITY If this event counts as busy time or is free time that can be scheduled over.

The following sections describe how to use these intents.

Using an intent to insert an event

Using the INSERT Intent lets your application hand off the event insertion task to the Calendar itself. With this approach, your application doesn't even need to have the WRITE_CALENDAR permission included in its manifest file.

When users run an application that uses this approach, the application sends them to the Calendar to finish adding the event. The INSERT Intent uses extra fields to pre-populate a form with the details of the event in the Calendar. Users can then cancel the event, edit the form as needed, or save the event to their calendars.

Here is a code snippet that schedules an event on January 19, 2012, that runs from 7:30 a.m. to 8:30 a.m. Note the following about this code snippet:

  • It specifies Events.CONTENT_URI as the Uri.
  • It uses the CalendarContract.EXTRA_EVENT_BEGIN_TIME and CalendarContract.EXTRA_EVENT_END_TIME extra fields to pre-populate the form with the time of the event. The values for these times must be in UTC milliseconds from the epoch.
  • It uses the Intent.EXTRA_EMAIL extra field to provide a comma-separated list of invitees, specified by email address.
Calendar beginTime = Calendar.getInstance();

beginTime
.set(2012, 0, 19, 7, 30);
Calendar endTime = Calendar.getInstance();
endTime
.set(2012, 0, 19, 8, 30);
Intent intent = new Intent(Intent.ACTION_INSERT)
.setData(Events.CONTENT_URI)
.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis())
.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis())
.putExtra(Events.TITLE, "Yoga")
.putExtra(Events.DESCRIPTION, "Group class")
.putExtra(Events.EVENT_LOCATION, "The gym")
.putExtra(Events.AVAILABILITY, Events.AVAILABILITY_BUSY)
.putExtra(Intent.EXTRA_EMAIL, "rowan@example.com,trevor@example.com");
startActivity
(intent);

Using an intent to edit an event

You can update an event directly, as described in Updating events. But using the EDIT Intent allows an application that doesn't have permission to hand off event editing to the Calendar application. When users finish editing their event in Calendar, they're returned to the original application.

Here is an example of an intent that sets a new title for a specified event and lets users edit the event in the Calendar.

long eventID = 208;

Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
Intent intent = new Intent(Intent.ACTION_EDIT)
.setData(uri)
.putExtra(Events.TITLE, "My New Title");
startActivity
(intent);

Using intents to view calendar data

Calender Provider offers two different ways to use the VIEW Intent:

  • To open the Calendar to a particular date.
  • To view an event.

Here is an example that shows how to open the Calendar to a particular date:

// A date-time specified in milliseconds since the epoch.

long startMillis;
...
Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
builder
.appendPath("time");
ContentUris.appendId(builder, startMillis);
Intent intent = new Intent(Intent.ACTION_VIEW)
.setData(builder.build());
startActivity
(intent);

Here is an example that shows how to open an event for viewing:

long eventID = 208;

...
Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
Intent intent = new Intent(Intent.ACTION_VIEW)
.setData(uri);
startActivity
(intent);

Sync Adapters

There are only minor differences in how an application and a sync adapter access the Calendar Provider:

  • A sync adapter needs to specify that it's a sync adapter by setting CALLER_IS_SYNCADAPTER to true.
  • A sync adapter needs to provide an ACCOUNT_NAME and an ACCOUNT_TYPE as query parameters in the URI.
  • A sync adapter has write access to more columns than an application or widget. For example, an application can only modify a few characteristics of a calendar, such as its name, display name, visibility setting, and whether the calendar is synced. By comparison, a sync adapter can access not only those columns, but many others, such as calendar color, time zone, access level, location, and so on. However, a sync adapter is restricted to the ACCOUNT_NAME and ACCOUNT_TYPE it specified.

Here is a helper method you can use to return a URI for use with a sync adapter:

 static Uri asSyncAdapter(Uri uri, String account, String accountType) {

return uri.buildUpon()
.appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true")
.appendQueryParameter(Calendars.ACCOUNT_NAME, account)
.appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
}

My Headlines

RF Optimization 2G,3G,4G and Wimax

Subscribe Now: google

Add to Google Reader or Homepage