Android App

Table of Contents

set up development environment

Download the Android SDK.

Install the ADT plugin for Eclipse (if you’ll use the Eclipse IDE).

Download the latest SDK tools and platforms using the SDK Manager.

http://developer.android.com/training/basics/firstapp/index.html the ADT Bundle includes everything you need to begin developing apps:

  • Eclipse + ADT plugin
  • Android SDK Tools
  • Android Platform-tools
  • The latest Android platform
  • The latest Android system image for the emulator

add PATH

Add the platform-tools/ as well as the tools/ directory to your PATH environment variable.

a few directories and files in the Android project

AndroidManifest.xml

The manifest file describes the fundamental characteristics of the app and defines each of its components.

src/

Directory for your app's main source files.

  • drawable-hdpi/

    Directory for drawable objects (such as bitmaps) that are designed for high-density (hdpi) screens. Other drawable directories contain assets designed for other screen densities.

  • layout/

    Directory for files that define your app's user interface.

  • values/

    Directory for other various XML files that contain a collection of resources, such as string and color definitions.

Managing Projects

http://developer.android.com/tools/projects/index.html There are three types of projects, and they all share the same general structure but differ in function:

  • Android Projects
  • Test Projects
  • Library Projects These projects contain shareable Android source code and resources that you can reference in Android projects. This is useful when you have common code that you want to reuse. Library projects cannot be installed onto a device, however, they are pulled into the .apk file at build time.

Android Projects

The following directories and files comprise an Android project:

  • src/ Contains your stub Activity file, which is stored at src/your/package/namespace/ActivityName.java. All other source code files (such as .java or .aidl files) go here as well.
  • bin Output directory of the build. This is where you can find the final .apk file and other compiled resources.
  • jni Contains native code sources developed using the Android NDK. For more information, see the Android NDK documentation.
  • gen/ Contains the Java files generated by ADT, such as your R.java file and interfaces created from AIDL files.
  • assets/ This is empty. You can use it to store raw asset files. Files that you save here are compiled into an .apk file as-is, and the original filename is preserved. You can navigate this directory in the same way as a typical file system using URIs and read files as a stream of bytes using the the AssetManager. For example, this is a good location for textures and game data.
  • res/ Contains application resources, such as drawable files, layout files, and string values. See Application Resources for more information.
    • anim/ For XML files that are compiled into animation objects. See the Animation resource type.
    • color/ For XML files that describe colors. See the Color Values resource type.
    • drawable/ For bitmap files (PNG, JPEG, or GIF), 9-Patch image files, and XML files that describe Drawable shapes or a Drawable objects that contain multiple states (normal, pressed, or focused). See the Drawable resource type.
    • layout/ XML files that are compiled into screen layouts (or part of a screen). See the Layout resource type.
    • menu/ For XML files that define application menus. See the Menus resource type.
    • raw/ For arbitrary raw asset files. Saving asset files here instead of in the assets/ directory only differs in the way that you access them. These files are processed by aapt and must be referenced from the application using a resource identifier in the R class. For example, this is a good place for media, such as MP3 or Ogg files.
    • values/ For XML files that are compiled into many kinds of resource. Unlike other resources in the res/ directory, resources written to XML files in this folder are not referenced by the file name. Instead, the XML element type controls how the resources is defined within them are placed into the R class.
    • xml/ For miscellaneous XML files that configure application components. For example, an XML file that defines a PreferenceScreen, AppWidgetProviderInfo, or Searchability Metadata. See Application Resources for more information about configuring these application components.
  • libs/ Contains private libraries.
  • AndroidManifest.xml The control file that describes the nature of the application and each of its components. For instance, it describes: certain qualities about the activities, services, intent receivers, and content providers; what permissions are requested; what external libraries are needed; what device features are required, what API Levels are supported or required; and others. See the AndroidManifest.xml documentation for more information
  • project.properties This file contains project settings, such as the build target. This file is integral to the project, so maintain it in a source revision control system. To edit project properties in Eclipse, right-click the project folder and select Properties.
  • local.properties Customizable computer-specific properties for the build system. If you use Ant to build the project, this contains the path to the SDK installation. Because the content of the file is specific to the local installation of the SDK, the local.properties should not be maintained in a source revision control system. If you use Eclipse, this file is not used.
  • ant.properties Customizable properties for the build system. You can edit this file to override default build settings used by Ant and also provide the location of your keystore and key alias so that the build tools can sign your application when building in release mode. This file is integral to the project, so maintain it in a source revision control system. If you use Eclipse, this file is not used.
  • build.xml The Ant build file for your project. This is only applicable for projects that you build with Ant.

Library Projects

However, a library project differs from an standard Android application project in that you cannot compile it directly to its own .apk and run it on an Android device. Similarly, you cannot export the library project to a self-contained JAR file, as you would do for a true library. Instead, you must compile the library indirectly, by referencing the library in the dependent application and building that application

Test Projects

Test projects contain Android applications that you write using the Testing and Instrumentation framework. The framework is an extension of the JUnit test framework and adds access to Android system objects. The file structure of a test project is the same as an Android project.

Managing the Activity Lifecycle

Recreating an Activity

Caution: Your activity will be destroyed and recreated each time the user rotates the screen. When the screen changes orientation, the system destroys and recreates the foreground activity because the screen configuration has changed and your activity might need to load alternative resources (such as the layout).

To save additional data about the activity state, you must override the onSaveInstanceState() callback method. The system calls this method when the user is leaving your activity and passes it the Bundle object that will be saved in the event that your activity is destroyed unexpectedly. If the system must recreate the activity instance later, it passes the same Bundle object to both the onRestoreInstanceState() and onCreate() methods.

Instead of restoring the state during onCreate() you may choose to implement onRestoreInstanceState(), which the system calls after the onStart() method. The system calls onRestoreInstanceState() only if there is a saved state to restore, so you do not need to check whether the Bundle is null

Supporting Different Devices

Supporting Different Languages

  • Create Locale Directories and String Files

    To add support for more languages, create additional values directories inside res/ that include a hyphen and the ISO country code at the end of the directory name. For example, values-es/ is the directory containing simple resourcess for the Locales with the language code "es". Android loads the appropriate resources according to the locale settings of the device at run time.

    In your source code, you can refer to a string resource with the syntax R.string.<stringname>. There are a variety of methods that accept a string resource this way.

    // Get a string resource from your app's Resources
    String hello = getResources().getString(R.string.hello_world);
    
    // Or supply a string resource to a method that requires a string
    TextView textView = new TextView(this);
    textView.setText(R.string.hello_world);
    

    In other XML files, you can refer to a string resource with the syntax @string/<stringname> whenever the XML attribute accepts a string value.

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />
    

Supporting Different Screens

Android categorizes device screens using two general properties: size and density.

  • There are four generalized sizes: small, normal, large, xlarge
  • And four generalized densities: low (ldpi), medium (mdpi), high (hdpi), extra high (xhdpi)
  • Create Different Layouts

    Each layout should be saved into the appropriate resources directory, named with a -<screensize> suffix. For example, a unique layout for large screens should be saved under res/layout-large/.

    As another example, here's a project with an alternative layout for landscape orientation:

    MyProject/
        res/
            layout/
                main.xml
            layout-land/
                main.xml
    
  • Create Different Bitmaps

    To generate these images, you should start with your raw resource in vector format and generate the images for each density using the following size scale:

    • xhdpi: 2.0
    • hdpi: 1.5
    • mdpi: 1.0 (baseline)
    • ldpi: 0.75

    This means that if you generate a 200x200 image for xhdpi devices, you should generate the same resource in 150x150 for hdpi, 100x100 for mdpi, and 75x75 for ldpi devices.

Supporting Different Platform Versions

  • Specify Minimum and Target API Levels

    The AndroidManifest.xml file describes details about your app and identifies which versions of Android it supports. Specifically, the minSdkVersion and targetSdkVersion attributes for the <uses-sdk element identify the lowest API level with which your app is compatible and the highest API level against which you’ve designed and tested your app.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android" ... >
        <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="15" />
        ...
    </manifest>
    
  • Check System Version at Runtime
    private void setUpActionBar() {
        // Make sure we're running on Honeycomb or higher to use ActionBar APIs
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            ActionBar actionBar = getActionBar();
            actionBar.setDisplayHomeAsUpEnabled(true);
        }
    }
    
  • Use Platform Styles and Themes

    To make your activity look like a dialog box:

    <activity android:theme="@android:style/Theme.Dialog">

    To make your activity have a transparent background:

    <activity android:theme="@android:style/Theme.Translucent"> To apply your own custom theme defined in /res/values/styles.xml:

    <activity android:theme="@style/CustomTheme"> To apply a theme to your entire app (all activities), add the android:theme attribute to the <application> element:

    <application android:theme="@style/CustomTheme"> For more about creating and using themes, read the Styles and Themes guide.

Building a Dynamic UI with Fragments

Using the Support Library

Update your manifest file to set the minimum API level to 4 and the target API level to the latest release:

<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="15" /> Warning: To be sure that you don't accidentally use new APIs on an older system version, be certain that you import the Fragment class and related APIs from the android.support.v4.app package:

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;

Creating a Fragment

Building a Flexible UI

  • Add a Fragment to an Activity at Runtime

    To perform a transaction such as add or remove a fragment, you must use the FragmentManager to create a FragmentTransaction, which provides APIs to add, remove, replace, and perform other fragment transactions.

    Inside your activity, call getSupportFragmentManager() to get a FragmentManager using the Support Library APIs. Then call beginTransaction() to create a FragmentTransaction and call add() to add a fragment.

    You can perform multiple fragment transaction for the activity using the same FragmentTransaction. When you're ready to make the changes, you must call commit().

  • Replace One Fragment with Another

    To allow the user to navigate backward through the fragment transactions, you must call addToBackStack() before you commit the FragmentTransaction.

Communicating with Other Fragments

  • Define an Interface

    To allow a Fragment to communicate up to its Activity, you can define an interface in the Fragment class and implement it within the Activity. The Fragment captures the interface implementation during its onAttach() lifecycle method and can then call the Interface methods in order to communicate with the Activity.

  • Implement the Interface

    In order to receive event callbacks from the fragment, the activity that hosts it must implement the interface defined in the fragment class.

  • Deliver a Message to a Fragment

    The host activity can deliver messages to a fragment by capturing the Fragment instance with findFragmentById(), then directly call the fragment's public methods.

Saving Data

Saving Key-Value Sets

  • Get a Handle to a SharedPreferences
    • getSharedPreferences() — Use this if you need multiple shared preference files identified by name, which you specify with the first parameter. You can call this from any Context in your app
    • getPreferences() — Use this from an Activity if you need to use only one shared preference file for the activity. Because this retrieves a default shared preference file that belongs to the activity, you don't need to supply a name.
    Context context = getActivity();
    SharedPreferences sharedPref = context.getSharedPreferences(
            getString(R.string.preference_file_key), Context.MODE_PRIVATE);
    
    SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
    
  • Write to Shared Preferences

    Pass the keys and values you want to write with methods such as putInt() and putString(). Then call commit() to save the changes.

    SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
    SharedPreferences.Editor editor = sharedPref.edit();
    editor.putInt(getString(R.string.saved_high_score), newHighScore);
    editor.commit();
    
  • Read from Shared Preferences

    call methods such as getInt() and getString(),

    SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
    int defaultValue = getResources().getInteger(R.string.saved_high_score_default);
    long highScore = sharedPref.getInt(getString(R.string.saved_high_score), defaultValue);
    

Saving Files

  • Choose Internal or External Storage

    Internal storage:

    • It's always available.
    • Files saved here are accessible by only your app by default.
    • When the user uninstalls your app, the system removes all your app's files from internal storage.

    External storage:

    • It's not always available, because the user can mount the external storage as USB storage and in some cases remove it from the device.
    • It's world-readable, so files saved here may be read outside of your control.
    • When the user uninstalls your app, the system removes your app's files from here only if you save them in the directory from getExternalFilesDir().
  • Obtain Permissions for External Storage
    <manifest ...>
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        ...
    </manifest>
    
  • Save a File on Internal Storage
    • getFilesDir() Returns a File representing an internal directory for your app.
    • getCacheDir() Returns a File representing an internal directory for your app's temporary cache files.
    File file = new File(context.getFilesDir(), filename);
    
    String filename = "myfile";
    String string = "Hello world!";
    FileOutputStream outputStream;
    
    try {
      outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
      outputStream.write(string.getBytes());
      outputStream.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
    

    Or, if you need to cache some files, you should instead use createTempFile().

  • Save a File on External Storage
    /* Checks if external storage is available for read and write */
    public boolean isExternalStorageWritable() {
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state)) {
            return true;
        }
        return false;
    }
    
    /* Checks if external storage is available to at least read */
    public boolean isExternalStorageReadable() {
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state) ||
            Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
            return true;
        }
        return false;
    }
    

    If you want to save public files on the external storage, use the getExternalStoragePublicDirectory() method to get a File representing the appropriate directory on the external storage.

    public File getAlbumStorageDir(String albumName) {
        // Get the directory for the user's public pictures directory. 
        File file = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), albumName);
        if (!file.mkdirs()) {
            Log.e(LOG_TAG, "Directory not created");
        }
        return file;
    }
    

    If you want to save files that are private to your app, you can acquire the appropriate directory by calling getExternalFilesDir() and passing it a name indicating the type of directory you'd like.

  • Query Free Space

    calling getFreeSpace() or getTotalSpace().

  • Delete a File
    myFile.delete();
    //If the file is saved on internal storage, you can also ask the Context to locate and delete a file by calling deleteFile():
    myContext.deleteFile(fileName);
    

Saving Data in SQL Databases

The APIs you'll need to use a database on Android are available in the android.database.sqlite package

  • Define a Schema and Contract

    Note: By implementing the BaseColumns interface, your inner class can inherit a primary key field called _ID that some Android classes such as cursor adaptors will expect it to have. It's not required, but this can help your database work harmoniously with the Android framework.

    public static abstract class FeedEntry implements BaseColumns {
        public static final String TABLE_NAME = "entry";
        public static final String COLUMN_NAME_ENTRY_ID = "entryid";
        public static final String COLUMN_NAME_TITLE = "title";
        public static final String COLUMN_NAME_SUBTITLE = "subtitle";
        ...
    }
    
  • Create a Database Using a SQL Helper
    private static final String TEXT_TYPE = " TEXT";
    private static final String COMMA_SEP = ",";
    private static final String SQL_CREATE_ENTRIES =
        "CREATE TABLE " + FeedReaderContract.FeedEntry.TABLE_NAME + " (" +
        FeedReaderContract.FeedEntry._ID + " INTEGER PRIMARY KEY," +
        FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP +
        FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +
        ... // Any other options for the CREATE command
        " )";
    
    private static final String SQL_DELETE_ENTRIES =
        "DROP TABLE IF EXISTS " + TABLE_NAME_ENTRIES;
    

    A useful set of APIs is available in the SQLiteOpenHelper class.

    Note: Because they can be long-running, be sure that you call getWritableDatabase() or getReadableDatabase() in a background thread, such as with AsyncTask or IntentService.

    To use SQLiteOpenHelper, create a subclass that overrides the onCreate(), onUpgrade() and onOpen() callback methods.

    public class FeedReaderDbHelper extends SQLiteOpenHelper {
        // If you change the database schema, you must increment the database version.
        public static final int DATABASE_VERSION = 1;
        public static final String DATABASE_NAME = "FeedReader.db";
    
        public FeedReaderDbHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }
        public void onCreate(SQLiteDatabase db) {
            db.execSQL(SQL_CREATE_ENTRIES);
        }
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            // This database is only a cache for online data, so its upgrade policy is
            // to simply to discard the data and start over
            db.execSQL(SQL_DELETE_ENTRIES);
            onCreate(db);
        }
        public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            onUpgrade(db, oldVersion, newVersion);
        }
    }
    
    //To access your database, instantiate your subclass of SQLiteOpenHelper:
    FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());
    
  • Put Information into a Database

    Insert data into the database by passing a ContentValues object to the insert() method:

    // Gets the data repository in write mode
    SQLiteDatabase db = mDbHelper.getWritableDatabase();
    
    // Create a new map of values, where column names are the keys
    ContentValues values = new ContentValues();
    values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID, id);
    values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, title);
    values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_CONTENT, content);
    
    // Insert the new row, returning the primary key value of the new row
    long newRowId;
    newRowId = db.insert(
             FeedReaderContract.FeedEntry.TABLE_NAME,
             FeedReaderContract.FeedEntry.COLUMN_NAME_NULLABLE,
             values);
    
  • Read Information from a Database

    To read from a database, use the query() method, passing it your selection criteria and desired columns.

    SQLiteDatabase db = mDbHelper.getReadableDatabase();
    
    // Define a projection that specifies which columns from the database
    // you will actually use after this query.
    String[] projection = {
        FeedReaderContract.FeedEntry._ID,
        FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE,
        FeedReaderContract.FeedEntry.COLUMN_NAME_UPDATED,
        ...
        };
    
    // How you want the results sorted in the resulting Cursor
    String sortOrder =
        FeedReaderContract.FeedEntry.COLUMN_NAME_UPDATED + " DESC";
    
    Cursor c = db.query(
        FeedReaderContract.FeedEntry.TABLE_NAME,  // The table to query
        projection,                               // The columns to return
        selection,                                // The columns for the WHERE clause
        selectionArgs,                            // The values for the WHERE clause
        null,                                     // don't group the rows
        null,                                     // don't filter by row groups
        sortOrder                                 // The sort order
        );
    
    cursor.moveToFirst();
    long itemId = cursor.getLong(
        cursor.getColumnIndexOrThrow(FeedReaderContract.FeedEntry._ID)
    );
    
  • Delete Information from a Database
    // Define 'where' part of query.
    String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
    // Specify arguments in placeholder order.
    String[] selectionArgs = { String.valueOf(rowId) };
    // Issue SQL statement.
    db.delete(table_name, selection, selectionArgs);
    
  • Update a Database
    SQLiteDatabase db = mDbHelper.getReadableDatabase();
    
    // New value for one column
    ContentValues values = new ContentValues();
    values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, title);
    
    // Which row to update, based on the ID
    String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
    String[] selectionArgs = { String.valueOf(rowId) };
    
    int count = db.update(
        FeedReaderDbHelper.FeedEntry.TABLE_NAME,
        values,
        selection,
        selectionArgs);
    

Interacting with Other Apps

Sending the User to Another App

  • Build an Implicit Intent

    If your data is a Uri, there's a simple Intent() constructor you can use define the action and data.

    //View a map:
    // Map point based on address
    Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
    // Or map point based on latitude/longitude
    // Uri location = Uri.parse("geo:37.422219,-122.08364?z=14"); // z param is zoom level
    Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
    
    //View a web page:
    Uri webpage = Uri.parse("http://www.android.com");
    Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage);
    
    //Send an email with an attachment:
    Intent emailIntent = new Intent(Intent.ACTION_SEND);
    // The intent does not have a URI, so declare the "text/plain" MIME type
    emailIntent.setType(HTTP.PLAIN_TEXT_TYPE);
    emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"jon@example.com"}); // recipients
    emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Email subject");
    emailIntent.putExtra(Intent.EXTRA_TEXT, "Email message text");
    emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://path/to/email/attachment"));
    // You can also attach multiple items by passing an ArrayList of Uris
    
    //Create a calendar event:
    Intent calendarIntent = new Intent(Intent.ACTION_INSERT, Events.CONTENT_URI);
    Calendar beginTime = Calendar.getInstance().set(2012, 0, 19, 7, 30);
    Calendar endTime = Calendar.getInstance().set(2012, 0, 19, 10, 30);
    calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis());
    calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis());
    calendarIntent.putExtra(Events.TITLE, "Ninja class");
    calendarIntent.putExtra(Events.EVENT_LOCATION, "Secret dojo");
    
  • Verify There is an App to Receive the Intent
    PackageManager packageManager = getPackageManager();
    List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
    boolean isIntentSafe = activities.size() > 0;
    
  • Start an Activity with the Intent
    // Build the intent
    Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
    Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
    
    // Verify it resolves
    PackageManager packageManager = getPackageManager();
    List<ResolveInfo> activities = packageManager.queryIntentActivities(mapIntent, 0);
    boolean isIntentSafe = activities.size() > 0;
    
    // Start an activity if it's safe
    if (isIntentSafe) {
        startActivity(mapIntent);
    }
    
  • Show an App Chooser
    Intent intent = new Intent(Intent.ACTION_SEND);
    ...
    
    // Always use string resources for UI text. This says something like "Share this photo with"
    String title = getResources().getText(R.string.chooser_title);
    // Create and start the chooser
    Intent chooser = Intent.createChooser(intent, title);
    startActivity(chooser);
    

Getting a Result from an Activity

To receive a result, call startActivityForResult() (instead of startActivity()).

  • Start the Activity
    static final int PICK_CONTACT_REQUEST = 1;  // The request code
    ...
    private void pickContact() {
        Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts"));
        pickContactIntent.setType(Phone.CONTENT_TYPE); // Show user only contacts w/ phone numbers
        startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST);
    }
    
  • Receive the Result

    the system calls your activity's onActivityResult() method. This method includes three arguments:

    • The request code you passed to startActivityForResult().
    • A result code specified by the second activity. This is either RESULTOK if the operation was successful or RESULTCANCELED if the user backed out or the operation failed for some reason.
    • An Intent that carries the result data.
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // Check which request we're responding to
        if (requestCode == PICK_CONTACT_REQUEST) {
            // Make sure the request was successful
            if (resultCode == RESULT_OK) {
                // The user picked a contact.
                // The Intent's data Uri identifies which contact was selected.
    
                // Do something with the contact here (bigger example below)
            }
        }
    }
    
  • Bonus: Read the contact data
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // Check which request it is that we're responding to
        if (requestCode == PICK_CONTACT_REQUEST) {
            // Make sure the request was successful
            if (resultCode == RESULT_OK) {
                // Get the URI that points to the selected contact
                Uri contactUri = data.getData();
                // We only need the NUMBER column, because there will be only one row in the result
                String[] projection = {Phone.NUMBER};
    
                // Perform the query on the contact to get the NUMBER column
                // We don't need a selection or sort order (there's only one result for the given URI)
                // CAUTION: The query() method should be called from a separate thread to avoid blocking
                // your app's UI thread. (For simplicity of the sample, this code doesn't do that.)
                // Consider using CursorLoader to perform the query.
                Cursor cursor = getContentResolver()
                        .query(contactUri, projection, null, null, null);
                cursor.moveToFirst();
    
                // Retrieve the phone number from the NUMBER column
                int column = cursor.getColumnIndex(Phone.NUMBER);
                String number = cursor.getString(column);
    
                // Do something with the phone number...
            }
        }
    }
    

Allowing Other Apps to Start Your Activity

To allow other apps to start your activity, you need to add an <intent-filter> element in your manifest file for the corresponding <activity> element.

  • Add an Intent Filter
    • Action A string naming the action to perform. Usually one of the platform-defined values such as ACTIONSEND or ACTIONVIEW. Specify this in your intent filter with the <action> element.
    • Data A description of the data associated with the intent. Specify this in your intent filter with the <data> element.
    • Category Provides an additional way to characterize the activity handling the intent, all implicit intents are defined with CATEGORYDEFAULT by default.
    <activity android:name="ShareActivity">
        <!-- filter for sending text; accepts SENDTO action with sms URI schemes -->
        <intent-filter>
            <action android:name="android.intent.action.SENDTO"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:scheme="sms" />
            <data android:scheme="smsto" />
        </intent-filter>
        <!-- filter for sending text or images; accepts SEND action and text or image data -->
        <intent-filter>
            <action android:name="android.intent.action.SEND"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:mimeType="image/*"/>
            <data android:mimeType="text/plain"/>
        </intent-filter>
    </activity>
    
  • Handle the Intent in Your Activity

    call getIntent() to retrieve the Intent that started the activity. t you should generally do so during early callbacks such as onCreate() or onStart().

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        setContentView(R.layout.main);
    
        // Get the intent that started this activity
        Intent intent = getIntent();
        Uri data = intent.getData();
    
        // Figure out what to do based on the intent type
        if (intent.getType().indexOf("image/") != -1) {
            // Handle intents with image data ...
        } else if (intent.getType().equals("text/plain")) {
            // Handle intents with text ...
        }
    }
    
  • Return a Result

    call setResult() to specify the result code and result Intent. call finish() to close (and destroy) your activity

    // Create intent to deliver some kind of result data
    Intent result = new Intent("com.example.RESULT_ACTION", Uri.parse("content://result_uri");
    setResult(Activity.RESULT_OK, result);
    finish();
    

Sharing Content

using Intent APIs and the ActionProvider object.

Sending Content to Other Apps

  • Send Text Content
    Intent sendIntent = new Intent();
    sendIntent.setAction(Intent.ACTION_SEND);
    sendIntent.putExtra(Intent.EXTRA_TEXT, "This is my text to send.");
    sendIntent.setType("text/plain");
    startActivity(sendIntent);
    
    //or
    startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.send_to)));
    

    Optionally, you can set some standard extras for the intent: EXTRAEMAIL, EXTRACC, EXTRABCC, EXTRASUBJECT.

  • Send Binary Content
    Intent shareIntent = new Intent();
    shareIntent.setAction(Intent.ACTION_SEND);
    shareIntent.putExtra(Intent.EXTRA_STREAM, uriToImage);
    shareIntent.setType("image/jpeg");
    startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.send_to)));
    

    The receiving application needs permission to access the data the Uri points to. There are a number of ways to handle this:

    • Write the data to a file on external/shared storage (such as the SD card), which all apps can read. Use Uri.fromFile() to create the Uri that can be passed to the share intent. However, keep in mind that not all applications process a file:// style Uri.
    • Write the data to a file in your own application directory using openFileOutput() with mode MODEWORLDREADABLE after which getFileStreamPath() can be used to return a File. As with the previous option, Uri.fromFile() will create a file:// style Uri for your share intent.
    • Media files like images, videos and audio can be scanned and added to the system MediaStore using scanFile(). The onScanCompleted() callback returns a content:// style Uri suitable for including in your share intent.
    • Images can be inserted into the system MediaStore using insertImage() which will return a content:// style Uri suitable for including in a share intent.
    • Store the data in your own ContentProvider, make sure that other apps have the correct permission to access your provider (or use per-URI permissions).
  • Send Multiple Pieces of Content

    use the ACTIONSENDMULTIPLE action together with a list of URIs pointing to the content

    ArrayList<Uri> imageUris = new ArrayList<Uri>();
    imageUris.add(imageUri1); // Add your image URIs here
    imageUris.add(imageUri2);
    
    Intent shareIntent = new Intent();
    shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
    shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, imageUris);
    shareIntent.setType("image/*");
    startActivity(Intent.createChooser(shareIntent, "Share images to.."));
    

Receiving Content from Other Apps

  • Update Your Manifest
    <activity android:name=".ui.MyActivity" >
        <intent-filter>
            <action android:name="android.intent.action.SEND" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="image/*" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.SEND" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="text/plain" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.SEND_MULTIPLE" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="image/*" />
        </intent-filter>
    </activity>
    
  • Handle the Incoming Content
    void onCreate (Bundle savedInstanceState) {
        ...
        // Get intent, action and MIME type
        Intent intent = getIntent();
        String action = intent.getAction();
        String type = intent.getType();
    
        if (Intent.ACTION_SEND.equals(action) && type != null) {
            if ("text/plain".equals(type)) {
                handleSendText(intent); // Handle text being sent
            } else if (type.startsWith("image/")) {
                handleSendImage(intent); // Handle single image being sent
            }
        } else if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
            if (type.startsWith("image/")) {
                handleSendMultipleImages(intent); // Handle multiple images being sent
            }
        } else {
            // Handle other intents, such as being started from the home screen
        }
        ...
    }
    
    void handleSendText(Intent intent) {
        String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
        if (sharedText != null) {
            // Update UI to reflect text being shared
        }
    }
    
    void handleSendImage(Intent intent) {
        Uri imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
        if (imageUri != null) {
            // Update UI to reflect image being shared
        }
    }
    
    void handleSendMultipleImages(Intent intent) {
        ArrayList<Uri> imageUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
        if (imageUris != null) {
            // Update UI to reflect multiple images being shared
        }
    }
    

Adding an Easy Share Action

ActionProvider in Android 4.0 (API Level 14)

  • Update Menu Declarations

    To get started with ShareActionProviders, define the android:actionProviderClass attribute for the corresponding <item> in your menu resource file:

    <menu xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:id="@+id/menu_item_share"
            android:showAsAction="ifRoom"
            android:title="Share"
            android:actionProviderClass="android.widget.ShareActionProvider" />
        ...
    </menu>
    
  • Set the Share Intent
    private ShareActionProvider mShareActionProvider;
    ...
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate menu resource file.
        getMenuInflater().inflate(R.menu.share_menu, menu);
    
        // Locate MenuItem with ShareActionProvider
        MenuItem item = menu.findItem(R.id.menu_item_share);
    
        // Fetch and store ShareActionProvider
        mShareActionProvider = (ShareActionProvider) item.getActionProvider();
    
        // Return true to display menu
        return true;
    }
    
    // Call to update the share intent
    private void setShareIntent(Intent shareIntent) {
        if (mShareActionProvider != null) {
            mShareActionProvider.setShareIntent(shareIntent);
        }
    }
    

Managing Audio Playback

Controlling Your App’s Volume and Playback

  • Identify Which Audio Stream to Use

    Android maintains a separate audio stream for playing music, alarms, notifications, the incoming call ringer, system sounds, in-call volume, and DTMF tones. This is done primarily to allow users to control the volume of each stream independently.

    Most of these streams are restricted to system events, so unless your app is a replacement alarm clock, you’ll almost certainly be playing your audio using the STREAMMUSIC stream.

  • Use Hardware Volume Keys to Control Your App’s Audio Volume

    call it within the onCreate() method (of the Activity or Fragment that controls your media). This ensures that whenever your app is visible, the volume controls function as the user expects.

    setVolumeControlStream(AudioManager.STREAM_MUSIC);

  • Use Hardware Playback Control Keys to Control Your App’s Audio Playback
    • Whenever a user presses one of these hardware keys, the system broadcasts an intent with the ACTIONMEDIABUTTON action.
    • To respond to media button clicks, you need to register a BroadcastReceiver in your manifest that listens for this action broadcast
    <receiver android:name=".RemoteControlReceiver">
        <intent-filter>
            <action android:name="android.intent.action.MEDIA_BUTTON" />
        </intent-filter>
    </receiver>
    

    The Intent includes this under the EXTRAKEYEVENT key, while the KeyEvent class includes a list KEYCODEMEDIA* static constants that represents each of the possible media buttons, such as KEYCODEMEDIAPLAYPAUSE and KEYCODEMEDIANEXT.

    public class RemoteControlReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
                KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
                if (KeyEvent.KEYCODE_MEDIA_PLAY == event.getKeyCode()) {
                    // Handle key press.
                }
            }
        }
    }
    

    The following code can be used within your app to register and de-register your media button event receiver using the AudioManager. When registered, your broadcast receiver is the exclusive receiver of all media button broadcasts.

    AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
    ...
    
    // Start listening for button presses
    am.registerMediaButtonEventReceiver(RemoteControlReceiver);
    ...
    
    // Stop listening for button presses
    am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
    

Managing Audio Focus

  • Request the Audio Focus

    , it should hold the audio focus for the stream it will be using. This is done with a call to requestAudioFocus() which returns AUDIOFOCUSREQUESTGRANTED if your request is successful.

    The following snippet requests permanent audio focus on the music audio stream.

    AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
    ...
    
    // Request audio focus for playback
    int result = am.requestAudioFocus(afChangeListener,
                                     // Use the music stream.
                                     AudioManager.STREAM_MUSIC,
                                     // Request permanent focus.
                                     AudioManager.AUDIOFOCUS_GAIN);
    
    if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
        am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
        // Start playback.
    }
    

    abandoning transient focus, this allows any interupted app to continue playback.

    // Abandon audio focus when playback complete    
    am.abandonAudioFocus(afChangeListener);
    

    When requesting transient audio focus you have an additional option: whether or not you want to enable "ducking."

    // Request audio focus for playback
    int result = am.requestAudioFocus(afChangeListener,
                                 // Use the music stream.
                                 AudioManager.STREAM_MUSIC,
                                 // Request permanent focus.
                                 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
    
    if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
        // Start playback.
    }
    
  • Handle the Loss of Audio Focus

    The onAudioFocusChange() callback method of they audio focus change listener you registered when requesting audio focus receives a parameter that describes the focus change event.

    In the following code snippet, we pause the playback or our media player object if the audio loss is transient and resume it when we have regained the focus. If the loss is permanent, it unregisters our media button event receiver and stops monitoring audio focus changes.

    OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
        public void onAudioFocusChange(int focusChange) {
            if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT
                // Pause playback
            } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
                // Resume playback 
            } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
                am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
                am.abandonAudioFocus(afChangeListener);
                // Stop playback
            }
        }
    };
    
  • Duck!
    OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
        public void onAudioFocusChange(int focusChange) {
            if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
                // Lower the volume
            } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
                // Raise it back to normal
            }
        }
    };
    

Dealing with Audio Output Hardware

  • Check What Hardware is Being Used

    You can query the AudioManager to determine if the audio is currently being routed to the device speaker, wired headset, or attached Bluetooth device as shown in the following snippet:

    if (isBluetoothA2dpOn()) {
        // Adjust output for Bluetooth.
    } else if (isSpeakerphoneOn()) {
        // Adjust output for Speakerphone.
    } else if (isWiredHeadsetOn()) {
        // Adjust output for headsets
    } else { 
        // If audio plays and noone can hear it, is it still playing?
    }
    
  • Handle Changes in the Audio Output Hardware
    private class NoisyAudioStreamReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
                // Pause the playback
            }
        }
    }
    
    private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
    
    private void startPlayback() {
        registerReceiver(myNoisyAudioStreamReceiver(), intentFilter);
    }
    
    private void stopPlayback() {
        unregisterReceiver(myNoisyAudioStreamReceiver);
    }
    

Capturing Photos

Taking Photos Simply

  • Request Camera Permission
    <manifest ... >
        <uses-feature android:name="android.hardware.camera" />
        ...
    </manifest ... >
    
  • Take a Photo with the Camera App

    invokes an intent to capture a photo.

    private void dispatchTakePictureIntent(int actionCode) {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        startActivityForResult(takePictureIntent, actionCode);
    }
    

    Here is a function to check whether an app can handle your intent:

    public static boolean isIntentAvailable(Context context, String action) {
        final PackageManager packageManager = context.getPackageManager();
        final Intent intent = new Intent(action);
        List<ResolveInfo> list =
                packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        return list.size() > 0;
    }
    
  • View the Photo
    private void handleSmallCameraPhoto(Intent intent) {
        Bundle extras = intent.getExtras();
        mImageBitmap = (Bitmap) extras.get("data");
        mImageView.setImageBitmap(mImageBitmap);
    }
    
  • Save the Photo
    storageDir = new File(
        Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES
        ), 
        getAlbumName()
    );
    
    • Set the file name
      private File createImageFile() throws IOException {
          // Create an image file name
          String timeStamp = 
              new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
          String imageFileName = JPEG_FILE_PREFIX + timeStamp + "_";
          File image = File.createTempFile(
              imageFileName, 
              JPEG_FILE_SUFFIX, 
              getAlbumDir()
          );
          mCurrentPhotoPath = image.getAbsolutePath();
          return image;
      }
      
    • Append the file name onto the Intent
      File f = createImageFile();
      takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
      
  • Add the Photo to a Gallery
    private void galleryAddPic() {
        Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        File f = new File(mCurrentPhotoPath);
        Uri contentUri = Uri.fromFile(f);
        mediaScanIntent.setData(contentUri);
        this.sendBroadcast(mediaScanIntent);
    }
    
  • Decode a Scaled Image
    private void setPic() {
        // Get the dimensions of the View
        int targetW = mImageView.getWidth();
        int targetH = mImageView.getHeight();
    
        // Get the dimensions of the bitmap
        BitmapFactory.Options bmOptions = new BitmapFactory.Options();
        bmOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
        int photoW = bmOptions.outWidth;
        int photoH = bmOptions.outHeight;
    
        // Determine how much to scale down the image
        int scaleFactor = Math.min(photoW/targetW, photoH/targetH);
    
        // Decode the image file into a Bitmap sized to fill the View
        bmOptions.inJustDecodeBounds = false;
        bmOptions.inSampleSize = scaleFactor;
        bmOptions.inPurgeable = true;
    
        Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
        mImageView.setImageBitmap(bitmap);
    }
    

Recording Videos Simply

  • Request Camera Permission
    <manifest ... >
        <uses-feature android:name="android.hardware.camera" />
        ...
    </manifest ... >
    
  • Record a Video with a Camera App

    invoke an Intent

    private void dispatchTakeVideoIntent() {
        Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
        startActivityForResult(takeVideoIntent, ACTION_TAKE_VIDEO);
    }
    
  • View the Video
    private void handleCameraVideo(Intent intent) {
        mVideoUri = intent.getData();
        mVideoView.setVideoURI(mVideoUri);
    }
    

Controlling the Camera

  • Open the Camera Object

    Calling Camera.open() throws an exception if the camera is already in use by another application, so we wrap it in a try block.

    private boolean safeCameraOpen(int id) {
        boolean qOpened = false;
    
        try {
            releaseCameraAndPreview();
            mCamera = Camera.open(id);
            qOpened = (mCamera != null);
        } catch (Exception e) {
            Log.e(getString(R.string.app_name), "failed to open Camera");
            e.printStackTrace();
        }
    
        return qOpened;    
    }
    
    private void releaseCameraAndPreview() {
        mPreview.setCamera(null);
        if (mCamera != null) {
            mCamera.release();
            mCamera = null;
        }
    }
    
  • Create the Camera Preview
    • Preview Class

      To get started with displaying a preview, you need preview class. The preview requires an implementation of the android.view.SurfaceHolder.Callback interface, which is used to pass image data from the camera hardware to the application.

      class Preview extends ViewGroup implements SurfaceHolder.Callback {
      
          SurfaceView mSurfaceView;
          SurfaceHolder mHolder;
      
          Preview(Context context) {
              super(context);
      
              mSurfaceView = new SurfaceView(context);
              addView(mSurfaceView);
      
              // Install a SurfaceHolder.Callback so we get notified when the
              // underlying surface is created and destroyed.
              mHolder = mSurfaceView.getHolder();
              mHolder.addCallback(this);
              mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
          }
      ...
      }
      
    • Set and Start the Preview

      A camera instance and its related preview must be created in a specific order, with the camera object being first. In the snippet below, the process of initializing the camera is encapsulated so that Camera.startPreview() is called by the setCamera() method, whenever the user does something to change the camera.

      public void setCamera(Camera camera) {
          if (mCamera == camera) { return; }
      
          stopPreviewAndFreeCamera();
      
          mCamera = camera;
      
          if (mCamera != null) {
              List<Size> localSizes = mCamera.getParameters().getSupportedPreviewSizes();
              mSupportedPreviewSizes = localSizes;
              requestLayout();
      
              try {
                  mCamera.setPreviewDisplay(mHolder);
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
              /*
                Important: Call startPreview() to start updating the preview surface. Preview must 
                be started before you can take a picture.
                */
              mCamera.startPreview();
          }
      }
      
  • Modify Camera Settings
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // Now that the size is known, set up the camera parameters and begin
        // the preview.
        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
        requestLayout();
        mCamera.setParameters(parameters);
    
        /*
          Important: Call startPreview() to start updating the preview surface. Preview must be
          started before you can take a picture.
        */
        mCamera.startPreview();
    }
    
  • Set the Preview Orientation

    The setCameraDisplayOrientation() method lets you change how the preview is displayed without affecting how the image is recorded.

  • Take a Picture

    Use the Camera.takePicture() method to take a picture once the preview is started. You can create Camera.PictureCallback and Camera.ShutterCallback objects and pass them into Camera.takePicture().

  • Restart the Preview

    After a picture is taken, you must restart the preview before the user can take another picture.

    @Override
    public void onClick(View v) {
        switch(mPreviewState) {
        case K_STATE_FROZEN:
            mCamera.startPreview();
            mPreviewState = K_STATE_PREVIEW;
            break;
    
        default:
            mCamera.takePicture( null, rawCallback, null);
            mPreviewState = K_STATE_BUSY;
        } // switch
        shutterBtnConfig();
    }
    
  • Stop the Preview and Release the Camera
    public void surfaceDestroyed(SurfaceHolder holder) {
        // Surface will be destroyed when we return, so stop the preview.
        if (mCamera != null) {
            /*
              Call stopPreview() to stop updating the preview surface.
            */
            mCamera.stopPreview();
        }
    }
    
    /**
      * When this function returns, mCamera will be null.
      */
    private void stopPreviewAndFreeCamera() {
    
        if (mCamera != null) {
            /*
              Call stopPreview() to stop updating the preview surface.
            */
            mCamera.stopPreview();
    
            /*
              Important: Call release() to release the camera for use by other applications. 
              Applications should release the camera immediately in onPause() (and re-open() it in
              onResume()).
            */
            mCamera.release();
    
            mCamera = null;
        }
    }
    

Building Apps with Graphics & Animation

Displaying Bitmaps Efficiently

  • Loading Large Bitmaps Efficiently
    • Read Bitmap Dimensions and Type

      The BitmapFactory class provides several decoding methods (decodeByteArray(), decodeFile(), decodeResource(), etc.) for creating a Bitmap from various sources.

      Each type of decode method has additional signatures that let you specify decoding options via the BitmapFactory.Options class. Setting the inJustDecodeBounds property to true while decoding avoids memory allocation, returning null for the bitmap object but setting outWidth, outHeight and outMimeType. This technique allows you to read the dimensions and type of the image data prior to construction (and memory allocation) of the bitmap.

      BitmapFactory.Options options = new BitmapFactory.Options();
      options.inJustDecodeBounds = true;
      BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
      int imageHeight = options.outHeight;
      int imageWidth = options.outWidth;
      String imageType = options.outMimeType;
      
    • Load a Scaled Down Version into Memory

      Here are some factors to consider:

      • Estimated memory usage of loading the full image in memory.
      • Amount of memory you are willing to commit to loading this image given any other memory requirements of your application.
      • Dimensions of the target ImageView or UI component that the image is to be loaded into.
      • Screen size and density of the current device.

      Here’s a method to calculate a the sample size value based on a target width and height:

      public static int calculateInSampleSize(
                  BitmapFactory.Options options, int reqWidth, int reqHeight) {
          // Raw height and width of image
          final int height = options.outHeight;
          final int width = options.outWidth;
          int inSampleSize = 1;
      
          if (height > reqHeight || width > reqWidth) {
      
              // Calculate ratios of height and width to requested height and width
              final int heightRatio = Math.round((float) height / (float) reqHeight);
              final int widthRatio = Math.round((float) width / (float) reqWidth);
      
              // Choose the smallest ratio as inSampleSize value, this will guarantee
              // a final image with both dimensions larger than or equal to the
              // requested height and width.
              inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
          }
      
          return inSampleSize;
      }
      

      To use this method, first decode with inJustDecodeBounds set to true, pass the options through and then decode again using the new inSampleSize value and inJustDecodeBounds set to false:

      public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
              int reqWidth, int reqHeight) {
      
          // First decode with inJustDecodeBounds=true to check dimensions
          final BitmapFactory.Options options = new BitmapFactory.Options();
          options.inJustDecodeBounds = true;
          BitmapFactory.decodeResource(res, resId, options);
      
          // Calculate inSampleSize
          options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
      
          // Decode bitmap with inSampleSize set
          options.inJustDecodeBounds = false;
          return BitmapFactory.decodeResource(res, resId, options);
      }
      
  • Processing Bitmaps Off the UI Thread

    processing bitmaps in a background thread using AsyncTask

  • Caching Bitmaps
  • Managing Bitmap Memory
  • Displaying Bitmaps in Your UI

    load multiple bitmaps into ViewPager and GridView components using a background thread and bitmap cache

Displaying Graphics with OpenGL ES

Adding Animations


Activities

Fragments

Loaders

Tasks and Back Stack

Providing Resources

Handling Runtime Changes

Localization

Layouts

Styles and Themes

Data Storage

Storage Options

Data Backup

App Install Location

Design

Iconography

Testing

C program using Android Toolchain

use agcc

agcc hello.c -o hello
adb shell push /data # to the device

arm-eabi-gcc -o hello hello.c -Wl,-rpath-link=$Andoid_PATH/out/target/product/generic/obj/lib 
-L=$Andoid_PATH/out/target/product/generic/obj/lib -nostdlib 
$Andoid_PATH/out/target/product/generic/obj/lib/crtbegin_dynamic.o -lc

other component

dd


FAQ

permission denied: ant

folder adt-bundle-linux-x86-20130219/sdk/tools/ant

Description Resource Path Location Type Unexpected namespace prefix "xmlns" found for tag RelativeLayout activitya.xml

The codes were running absolutely fine in the past but now they are showing these errors….

I found a very simple solution, doesn't require any code changes (Addition, edit or deletion).

Go to the Project Tab at the tab Click on the tab Select the "Clean…." from the list

Author: Shi Shougang

Created: 2015-03-05 Thu 23:20

Emacs 24.3.1 (Org mode 8.2.10)

Validate