Android Resources

Web Hosting
There are many more items which you use to build a good Android application. Apart from coding for the application, you take care of various other resources like static content that your code uses, such as bitmaps, colors, layout definitions, user interface strings, animation instructions, and more. These resources are always maintained separately in various sub-directories under res/ directory of the project.
This tutorial will explain you how you can organize your application resources, specify alternative resources and access them in your applications.

Organize Resources

You should place each type of resource in a specific subdirectory of your project's res/ directory. For example, here's the file hierarchy for a simple project:
MyProject/
    src/  
        MyActivity.java  
    res/
        drawable/  
            icon.png  
        layout/  
            activity_main.xml
            info.xml
        values/  
            strings.xml 
The res/ directory contains all the resources in various subdirectories. Here we have an image resource, two layout resources, and a string resource file. Following table gives a detail about the resource directories supported inside project res/ directory.
DirectoryResource Type
anim/XML files that define property animations. They are saved in res/anim/ folder and accessed from the R.anim class.
color/XML files that define a state list of colors. They are saved in res/color/ and accessed from the R.color class.
drawable/Image files like .png, .jpg, .gif or XML files that are compiled into bitmaps, state lists, shapes, animation drawables. They are saved in res/drawable/ and accessed from the R.drawable class.
layout/XML files that define a user interface layout. They are saved in res/layout/ and accessed from the R.layout class.
menu/XML files that define application menus, such as an Options Menu, Context Menu, or Sub Menu. They are saved in res/menu/ and accessed from theR.menu class.
raw/Arbitrary files to save in their raw form. You need to callResources.openRawResource() with the resource ID, which is R.raw.filename to open such raw files.
values/XML files that contain simple values, such as strings, integers, and colors. For example, here are some filename conventions for resources you can create in this directory:
  • arrays.xml for resource arrays, and accessed from the R.array class.
  • integers.xml for resource integers, and accessed from the R.integerclass.
  • bools.xml for resource boolean, and accessed from the R.bool class.
  • colors.xml for color values, and accessed from the R.color class.
  • dimens.xml for dimension values, and accessed from the R.dimen class.
  • strings.xml for string values, and accessed from the R.string class.
  • styles.xml for styles, and accessed from the R.style class.
xml/Arbitrary XML files that can be read at runtime by calling Resources.getXML(). You can save various configuration files here which will be used at run time.

Alternative Resources

Your application should provide alternative resources to support specific device configurations. For example, you should include alternative drawable resources ( ie.images ) for different screen resolution and alternative string resources for different languages. At runtime, Android detects the current device configuration and loads the appropriate resources for your application.
To specify configuration-specific alternatives for a set of resources, follow the following steps:
  • Create a new directory in res/ named in the form <resources_name>-<config_qualifier>. Hereresources_name will be any of the resources mentioned in the above table, like layout, drawable etc. The qualifier will specify an individual configuration for which these resources are to be used. You can check official documentation for a complete list of qualifiers for different type of resources.
  • Save the respective alternative resources in this new directory. The resource files must be named exactly the same as the default resource files as shown in the below example, but these files will have content specific to the alternative. For example though image file name will be same but for high resolution screen, its resolution will be high.
Below is an example which specifies images for a default screen and alternative images for high resolution screen.
MyProject/
    src/  
        MyActivity.java  
    res/
        drawable/  
            icon.png
            background.png
        drawable-hdpi/  
            icon.png
            background.png  
        layout/  
            activity_main.xml
            info.xml
        values/  
            strings.xml 
Below is another example which specifies layout for a default language and alternative layout for arabic language.
MyProject/
    src/  
        MyActivity.java  
    res/
        drawable/  
            icon.png
            background.png
        drawable-hdpi/  
            icon.png
            background.png  
        layout/  
            activity_main.xml
            info.xml
        layout-ar/
            main.xml
        values/  
            strings.xml 

Accessing Resources

During your application development you will need to access defined resources either in your code, or in your layout XML files. Following section explains how to access your resources in both the scenarios:

ACCESSING RESOURCES IN CODE

When your Android application is compiled, a R class gets generated, which contains resource IDs for all the resources available in your res/ directory. You can use R class to access that resource using sub-directory and resource name or directly resource ID.

EXAMPLE:

To access res/drawable/myimage.png and set an ImageView you will use following code:
ImageView imageView = (ImageView) findViewById(R.id.myimageview);
imageView.setImageResource(R.drawable.myimage);
Here first line of the code make use of R.id.myimageview to get ImageView defined with idmyimageview in a Layout file. Second line of code makes use of R.drawable.myimage to get an image with name myimage available in drawable sub-directory under /res.

EXAMPLE:

Consider next example where res/values/strings.xml has following definition:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string  name="hello">Hello, World!</string>
</resources>
Now you can set the text on a TextView object with ID msg using a resource ID as follows:
TextView msgTextView = (TextView) findViewById(R.id.msg);
msgTextView.setText(R.string.hello);

EXAMPLE:

Consider a layout res/layout/activity_main.xml with the following definition:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="fill_parent" 
   android:layout_height="fill_parent" 
   android:orientation="vertical" >
<TextView android:id="@+id/text"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="Hello, I am a TextView" />
<Button android:id="@+id/button"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="Hello, I am a Button" />
</LinearLayout>
This application code will load this layout for an Activity, in the onCreate() method as follows:
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_activity);
}

ACCESSING RESOURCES IN XML

Consider the following resource XML res/values/strings.xml file that includes a color resource and a string resource:
<?xml version="1.0" encoding="utf-8"?>
<resources>
   <color name="opaque_red">#f00</color>
   <string name="hello">Hello!</string>
</resources>
Now you can use these resources in the following layout file to set the text color and text string as follows:
<?xml version="1.0" encoding="utf-8"?>
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:textColor="@color/opaque_red"
    android:text="@string/hello" />
Now if you will go through previous chapter once again where I have explained Hello World! example, and I'm sure you will have better understanding on all the concepts explained in this chapter. So I highly recommend to check previous chapter for working example and check how I have used various resources at very basic level.

Web Hosting

Content Providers

Web Hosting
A content provider component supplies data from one application to others on request. Such requests are handled by the methods of the ContentResolver class. A content provider can use different ways to store its data and the data can be stored in a database, in files, or even over a network.
Each Android applications runs in its own process with its own permissions which keeps an application data hidden from another application. But sometimes it is required to share data across applications. This is where content providers become very useful.
Content providers let you centralize content in one place and have many different applications access it as needed. A content provider behaves very much like a database where you can query it, edit its content, as well as add or delete content usingg insert(), update(), delete(), and query() methods. In most cases this data is stored in an SQlite database.
A content provider is implemented as a subclass of ContentProvider class and must implement a standard set of APIs that enable other applications to perform transactions.
public class MyContentProvider extends  ContentProvider {

}

Content URIs

To query a content provider, you specify the query string in the form of a URI which has following format:
<prefix>://<authority>/<data_type>/<id>
Here is the detaial of various parts of the URI:
PartDescription
prefixThis is always set to content://
authorityThis specifies the name of the content provider, for example contactsbrowser etc. For third-party content providers, this could be the fully qualified name, such ascom.tutorialspoint.statusprovider
data_typeThis indicates the type of data that this particular provider provides. For example, if you are getting all the contacts from the Contacts content provider, then the data path would be people and URI would look like this content://contacts/people
idThis specifies the specific record requested. For example, if you are looking for contact number 5 in the Contacts content provider then URI would look like thiscontent://contacts/people/5.

Create Content Provider

This involves number of simple steps to create your own content provider.
  • First of all you need to create a Content Provider class that extends the ContentProviderbaseclass.
  • Second, you need to define your content provider URI address which will be used to access the content.
  • Next you will need to create your own database to keep the content. Usually, Android uses SQLite database and framework needs to override onCreate() method which will use SQLite Open Helper method to create or open the provider's databse. When your application is launched, theonCreate() handler of each of its Content Providers is called on the main application thread.
  • Next you will have to implement Content Provider queries to perform different database specific operations.
  • Finally register your Content Provider in your acitivity file using <provider> tag.
Here is the list of methods which you need to override in Content Provider class to have your Content Provider working:
  • onCreate() This method is called when the provider is started.
  • query() This method receives a request from a client. The result is returned as a Cursor object.
  • insert()This method inserts a new record into the content provider.
  • delete() This method deletes an existing record from the content provider.
  • update() This method updates an existing record from the content provider.
  • getType() This method returns the MIME type of the data at the given URI.

Example

This example will explain you how to create your own ContentProvider. So let's follow the following steps to similar to what we followed while creating Hello World Example:
StepDescription
1You will use Eclipse IDE to create an Android application and name it as MyContentProviderunder a package com.example.mycontentprovider, with blank Activity.
2Modify main activity file MainActivity.java to add two new methods onClickAddName() andonClickRetrieveStudents().
3Create a new java file called StudentsProvider.java under the packagecom.example.mycontentprovider to define your actual provider and associated methods.
4Register your content provider in your AndroidManifest.xml file using <provider.../> tag
5Modify the detault content of res/layout/activity_main.xml file to include a small GUI to add sudents records.
6Define required constants in res/values/strings.xml file
7Run the application to launch Android emulator and verify the result of the changes done in the aplication.
Following is the content of the modified main activity filesrc/com.example.mycontentprovider/MainActivity.java. This file can include each of the fundamental lifecycle methods. We have added two new methods onClickAddName() andonClickRetrieveStudents() to handle user interaction with the application.
package com.example.mycontentprovider;

import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.ContentValues;
import android.content.CursorLoader;
import android.database.Cursor;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {

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

   @Override
   public boolean onCreateOptionsMenu(Menu menu) {
      getMenuInflater().inflate(R.menu.main, menu);
      return true;
   }

   public void onClickAddName(View view) {
      // Add a new student record
      ContentValues values = new ContentValues();

      values.put(StudentsProvider.NAME, 
      ((EditText)findViewById(R.id.txtName)).getText().toString());
      
      values.put(StudentsProvider.GRADE, 
      ((EditText)findViewById(R.id.txtGrade)).getText().toString());

      Uri uri = getContentResolver().insert(
      StudentsProvider.CONTENT_URI, values);
      
      Toast.makeText(getBaseContext(), 
      uri.toString(), Toast.LENGTH_LONG).show();
   }

   public void onClickRetrieveStudents(View view) {
      // Retrieve student records
      String URL = "content://com.example.provider.College/students";
      Uri students = Uri.parse(URL);
      Cursor c = managedQuery(students, null, null, null, "name");
      if (c.moveToFirst()) {
         do{
            Toast.makeText(this, 
            c.getString(c.getColumnIndex(StudentsProvider._ID)) + 
            ", " +  c.getString(c.getColumnIndex( StudentsProvider.NAME)) + 
            ", " + c.getString(c.getColumnIndex( StudentsProvider.GRADE)), 
            Toast.LENGTH_SHORT).show();
         } while (c.moveToNext());
      }
   }
}
Create new file StudentsProvider.java under com.example.mycontentprovider package and following is the content of src/com.example.mycontentprovider/StudentsProvider.java:
package com.example.mycontentprovider;

import java.util.HashMap;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;

public class StudentsProvider extends ContentProvider {

   static final String PROVIDER_NAME = "com.example.provider.College";
   static final String URL = "content://" + PROVIDER_NAME + "/students";
   static final Uri CONTENT_URI = Uri.parse(URL);

   static final String _ID = "_id";
   static final String NAME = "name";
   static final String GRADE = "grade";

   private static HashMap<String, String> STUDENTS_PROJECTION_MAP;

   static final int STUDENTS = 1;
   static final int STUDENT_ID = 2;

   static final UriMatcher uriMatcher;
   static{
      uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
      uriMatcher.addURI(PROVIDER_NAME, "students", STUDENTS);
      uriMatcher.addURI(PROVIDER_NAME, "students/#", STUDENT_ID);
   }

   /**
    * Database specific constant declarations
    */
   private SQLiteDatabase db;
   static final String DATABASE_NAME = "College";
   static final String STUDENTS_TABLE_NAME = "students";
   static final int DATABASE_VERSION = 1;
   static final String CREATE_DB_TABLE = 
      " CREATE TABLE " + STUDENTS_TABLE_NAME +
      " (_id INTEGER PRIMARY KEY AUTOINCREMENT, " + 
      " name TEXT NOT NULL, " +
      " grade TEXT NOT NULL);";

   /**
    * Helper class that actually creates and manages 
    * the provider's underlying data repository.
    */
   private static class DatabaseHelper extends SQLiteOpenHelper {
       DatabaseHelper(Context context){
          super(context, DATABASE_NAME, null, DATABASE_VERSION);
       }

       @Override
       public void onCreate(SQLiteDatabase db)
       {
          db.execSQL(CREATE_DB_TABLE);
       }
       
       @Override
       public void onUpgrade(SQLiteDatabase db, int oldVersion, 
                             int newVersion) {
          db.execSQL("DROP TABLE IF EXISTS " +  STUDENTS_TABLE_NAME);
          onCreate(db);
       }
   }

   @Override
   public boolean onCreate() {
      Context context = getContext();
      DatabaseHelper dbHelper = new DatabaseHelper(context);
      /**
       * Create a write able database which will trigger its 
       * creation if it doesn't already exist.
       */
      db = dbHelper.getWritableDatabase();
      return (db == null)? false:true;
   }

   @Override
   public Uri insert(Uri uri, ContentValues values) {
      /**
       * Add a new student record
       */
      long rowID = db.insert(	STUDENTS_TABLE_NAME, "", values);
      /** 
       * If record is added successfully
       */
      if (rowID > 0)
      {
         Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
         getContext().getContentResolver().notifyChange(_uri, null);
         return _uri;
      }
      throw new SQLException("Failed to add a record into " + uri);
   }

   @Override
   public Cursor query(Uri uri, String[] projection, String selection,	
                       String[] selectionArgs, String sortOrder) {
      
      SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
      qb.setTables(STUDENTS_TABLE_NAME);
      
      switch (uriMatcher.match(uri)) {
      case STUDENTS:
         qb.setProjectionMap(STUDENTS_PROJECTION_MAP);
         break;
      case STUDENT_ID:
         qb.appendWhere( _ID + "=" + uri.getPathSegments().get(1));
         break;
      default:
         throw new IllegalArgumentException("Unknown URI " + uri);
      }
      if (sortOrder == null || sortOrder == ""){
         /** 
          * By default sort on student names
          */
         sortOrder = NAME;
      }
      Cursor c = qb.query(db,	projection,	selection, selectionArgs, 
                          null, null, sortOrder);
      /** 
       * register to watch a content URI for changes
       */
      c.setNotificationUri(getContext().getContentResolver(), uri);

      return c;
   }

   @Override
   public int delete(Uri uri, String selection, String[] selectionArgs) {
      int count = 0;

      switch (uriMatcher.match(uri)){
      case STUDENTS:
         count = db.delete(STUDENTS_TABLE_NAME, selection, selectionArgs);
         break;
      case STUDENT_ID:
         String id = uri.getPathSegments().get(1);
         count = db.delete( STUDENTS_TABLE_NAME, _ID +  " = " + id + 
                (!TextUtils.isEmpty(selection) ? " AND (" + 
                selection + ')' : ""), selectionArgs);
         break;
      default: 
         throw new IllegalArgumentException("Unknown URI " + uri);
      }
      
      getContext().getContentResolver().notifyChange(uri, null);
      return count;
   }

   @Override
   public int update(Uri uri, ContentValues values, String selection, 
                     String[] selectionArgs) {
      int count = 0;
      
      switch (uriMatcher.match(uri)){
      case STUDENTS:
         count = db.update(STUDENTS_TABLE_NAME, values, 
                 selection, selectionArgs);
         break;
      case STUDENT_ID:
         count = db.update(STUDENTS_TABLE_NAME, values, _ID + 
                 " = " + uri.getPathSegments().get(1) + 
                 (!TextUtils.isEmpty(selection) ? " AND (" +
                 selection + ')' : ""), selectionArgs);
         break;
      default: 
         throw new IllegalArgumentException("Unknown URI " + uri );
      }
      getContext().getContentResolver().notifyChange(uri, null);
      return count;
   }

   @Override
   public String getType(Uri uri) {
      switch (uriMatcher.match(uri)){
      /**
       * Get all student records 
       */
      case STUDENTS:
         return "vnd.android.cursor.dir/vnd.example.students";
      /** 
       * Get a particular student
       */
      case STUDENT_ID:
         return "vnd.android.cursor.item/vnd.example.students";
      default:
         throw new IllegalArgumentException("Unsupported URI: " + uri);
      }
   }
}
Following will the modified content of AndroidManifest.xml file. Here we have added <provider.../> tag to include our content provider:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.mycontentprovider"
    android:versionCode="1"
    android:versionName="1.0" >

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

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.mycontentprovider.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <provider android:name="StudentsProvider" 
           android:authorities="com.example.provider.College">
        </provider>
    </application>

</manifest>
Following will be the content of res/layout/activity_main.xml file to include a button to broadcast your custom intent:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    <TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Name" />
    <EditText
    android:id="@+id/txtName"
    android:layout_height="wrap_content"
    android:layout_width="fill_parent" />
    <TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Grade" />
    <EditText
    android:id="@+id/txtGrade"
    android:layout_height="wrap_content"
    android:layout_width="fill_parent" />
    <Button
    android:text="Add Name"
    android:id="@+id/btnAdd"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:onClick="onClickAddName" />
    <Button
    android:text="Retrieve Students"
    android:id="@+id/btnRetrieve"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:onClick="onClickRetrieveStudents" />
</LinearLayout>
Make sure you have following content of res/values/strings.xml file:
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">MyContentProvider</string>
    <string name="action_settings">Settings</string>
    <string name="hello_world">Hello world!</string>

</resources>;
Let's try to run our modified MyContentProvider application we just created. I assume you had created your AVD while doing environment setup. To run the app from Eclipse, open one of your project's activity files and click Run Eclipse Run Icon icon from the toolbar. Eclipse installs the app on your AVD and starts it and if everything is fine with your setup and application, it will display following Emulator window, be patience because it may take sometime based on your computer speed:
Android Content Provider Demo
Now let's enter student Name and Grade and finally click on Add Name button, this will add student record in the database and will flash a message at the bottom showing ContentProvider URI along with record number added in the database. This operation makes use of our insert() method. Let's repeat this process to add few more students in the database of our content provider.
Add Records using ContentProvider
Once you are done with adding records in the database, now its time to ask ContentProvider to give us those records back, so let's click Retrieve Students button which will fetch and display all the records one by one which is as per our the implementation of our query() method.
Fetch Records using ContentProvider
You can write activities against update and delete operations by providing callback functions inMainActivity.java file and then modify user interface to have buttons for update and deleted operations in the same way as we have done for add and read operations.
This way you can use existing Content Provider like Address Book or you can use Content Provider concept in developing nice database oriented applications where you can perform all sort of database operations like read, write, update and delete as explained above in the example.

Web Hosting