New sample code for new external storage paths/APIs.
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index e45e19c..fb416ed 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -28,6 +28,7 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- We will request access to the camera, saying we require a camera
of some sort but not one with autofocus capability. -->
@@ -588,6 +589,14 @@
<!-- CONTENT PACKAGE SAMPLES -->
<!-- ************************************* -->
+ <activity android:name=".content.ExternalStorage" android:label="@string/activity_external_storage">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.SAMPLE_CODE" />
+ <category android:name="android.intent.category.EMBED" />
+ </intent-filter>
+ </activity>
+
<activity android:name=".content.StyledText" android:label="@string/activity_styled_text">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/samples/ApiDemos/res/layout/external_storage.xml b/samples/ApiDemos/res/layout/external_storage.xml
new file mode 100644
index 0000000..28105a3
--- /dev/null
+++ b/samples/ApiDemos/res/layout/external_storage.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbars="none">
+
+ <LinearLayout
+ android:id="@+id/layout"
+ android:orientation="vertical"
+ android:layout_width="match_parent" android:layout_height="wrap_content">
+ </LinearLayout>
+</ScrollView>
diff --git a/samples/ApiDemos/res/layout/external_storage_item.xml b/samples/ApiDemos/res/layout/external_storage_item.xml
new file mode 100644
index 0000000..6ba058b
--- /dev/null
+++ b/samples/ApiDemos/res/layout/external_storage_item.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent" android:layout_height="wrap_content">
+ <TextView android:id="@+id/label" style="?android:attr/textAppearanceMediumInverse"
+ android:layout_width="match_parent" android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:padding="4dip"
+ android:singleLine="true"
+ android:color="?android:attr/textColorPrimaryInverse"
+ android:background="#888" />
+ <TextView android:id="@+id/path" style="?android:attr/textAppearanceSmall"
+ android:layout_width="match_parent" android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:padding="4dip"
+ android:singleLine="true" />
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent" android:layout_height="wrap_content">
+ <View
+ android:layout_width="0dip"
+ android:layout_height="0dip"
+ android:layout_weight="1" />
+ <Button android:id="@+id/create"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/create" />
+ <View
+ android:layout_width="0dip"
+ android:layout_height="0dip"
+ android:layout_weight="1" />
+ <Button android:id="@+id/delete"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/delete" />
+ <View
+ android:layout_width="0dip"
+ android:layout_height="0dip"
+ android:layout_weight="1" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml
index 415e681..3ab923b 100644
--- a/samples/ApiDemos/res/values/strings.xml
+++ b/samples/ApiDemos/res/values/strings.xml
@@ -228,6 +228,10 @@
<!-- app/content examples strings -->
<!-- ============================== -->
+ <string name="activity_external_storage">Content/Storage/External Storage</string>
+ <string name="create">Create</string>
+ <string name="delete">Delete</string>
+
<string name="activity_styled_text">Content/Resources/<i>Styled</i> <b>Text</b></string>
<string name="styled_text_rsrc">Initialized from a resource:</string>
<string name="styled_text">Plain, <b>bold</b>, <i>italic</i>, <b><i>bold-italic</i></b></string>
diff --git a/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java b/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
new file mode 100644
index 0000000..d950a2c
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.content;
+
+//Need the following import to get access to the app resources, since this
+//class is in a sub-package.
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.MediaScannerConnection;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+
+/**
+* Demonstration of styled text resources.
+*/
+public class ExternalStorage extends Activity {
+ ViewGroup mLayout;
+
+ static class Item {
+ View mRoot;
+ Button mCreate;
+ Button mDelete;
+ }
+
+ Item mExternalStoragePublicPicture;
+ Item mExternalStoragePrivatePicture;
+ Item mExternalStoragePrivateFile;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.external_storage);
+ mLayout = (ViewGroup)findViewById(R.id.layout);
+ mExternalStoragePublicPicture = createStorageControls(
+ "Picture: getExternalStoragePublicDirectory",
+ Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PICTURES),
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ createExternalStoragePublicPicture();
+ updateExternalStorageState();
+ }
+ },
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ deleteExternalStoragePublicPicture();
+ updateExternalStorageState();
+ }
+ });
+ mLayout.addView(mExternalStoragePublicPicture.mRoot);
+ mExternalStoragePrivatePicture = createStorageControls(
+ "Picture getExternalFilesDir",
+ getExternalFilesDir(Environment.DIRECTORY_PICTURES),
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ createExternalStoragePrivatePicture();
+ updateExternalStorageState();
+ }
+ },
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ deleteExternalStoragePrivatePicture();
+ updateExternalStorageState();
+ }
+ });
+ mLayout.addView(mExternalStoragePrivatePicture.mRoot);
+ mExternalStoragePrivateFile = createStorageControls(
+ "File getExternalFilesDir",
+ getExternalFilesDir(null),
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ createExternalStoragePrivateFile();
+ updateExternalStorageState();
+ }
+ },
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ deleteExternalStoragePrivateFile();
+ updateExternalStorageState();
+ }
+ });
+ mLayout.addView(mExternalStoragePrivateFile.mRoot);
+
+ startWatchingExternalStorage();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ stopWatchingExternalStorage();
+ }
+
+ void handleExternalStorageState(boolean available, boolean writeable) {
+ boolean has = hasExternalStoragePublicPicture();
+ mExternalStoragePublicPicture.mCreate.setEnabled(writeable && !has);
+ mExternalStoragePublicPicture.mDelete.setEnabled(writeable && has);
+ has = hasExternalStoragePrivatePicture();
+ mExternalStoragePrivatePicture.mCreate.setEnabled(writeable && !has);
+ mExternalStoragePrivatePicture.mDelete.setEnabled(writeable && has);
+ has = hasExternalStoragePrivateFile();
+ mExternalStoragePrivateFile.mCreate.setEnabled(writeable && !has);
+ mExternalStoragePrivateFile.mDelete.setEnabled(writeable && has);
+ }
+
+// BEGIN_INCLUDE(monitor_storage)
+ BroadcastReceiver mExternalStorageReceiver;
+ boolean mExternalStorageAvailable = false;
+ boolean mExternalStorageWriteable = false;
+
+ void updateExternalStorageState() {
+ String state = Environment.getExternalStorageState();
+ if (Environment.MEDIA_MOUNTED.equals(state)) {
+ mExternalStorageAvailable = mExternalStorageWriteable = true;
+ } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
+ mExternalStorageAvailable = true;
+ mExternalStorageWriteable = false;
+ } else {
+ mExternalStorageAvailable = mExternalStorageWriteable = false;
+ }
+ handleExternalStorageState(mExternalStorageAvailable,
+ mExternalStorageWriteable);
+ }
+
+ void startWatchingExternalStorage() {
+ mExternalStorageReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i("test", "Storage: " + intent.getData());
+ updateExternalStorageState();
+ }
+ };
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
+ filter.addAction(Intent.ACTION_MEDIA_REMOVED);
+ registerReceiver(mExternalStorageReceiver, filter);
+ updateExternalStorageState();
+ }
+
+ void stopWatchingExternalStorage() {
+ unregisterReceiver(mExternalStorageReceiver);
+ }
+ // END_INCLUDE(monitor_storage)
+
+ // BEGIN_INCLUDE(public_picture)
+ void createExternalStoragePublicPicture() {
+ // Create a path where we will place our picture in the user's
+ // public pictures directory. Note that you should be careful about
+ // what you place here, since the user often manages these files. For
+ // pictures and other media owned by the application, consider
+ // Context.getExternalMediaDir().
+ File path = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PICTURES);
+ File file = new File(path, "DemoPicture.jpg");
+
+ try {
+ // Make sure the Pictures directory exists.
+ path.mkdirs();
+
+ // Very simple code to copy a picture from the application's
+ // resource into the external file. Note that this code does
+ // no error checking, and assumes the picture is small (does not
+ // try to copy it in chunks). Note that if external storage is
+ // not currently mounted this will silently fail.
+ InputStream is = getResources().openRawResource(R.drawable.balloons);
+ OutputStream os = new FileOutputStream(file);
+ byte[] data = new byte[is.available()];
+ is.read(data);
+ os.write(data);
+ is.close();
+ os.close();
+
+ // Tell the media scanner about the new file so that it is
+ // immediately available to the user.
+ MediaScannerConnection.scanFile(this,
+ new String[] { file.toString() }, null,
+ new MediaScannerConnection.ScanResultListener() {
+ public void onScanCompleted(String path, Uri uri) {
+ Log.i("ExternalStorage", "Scanned " + path + ":");
+ Log.i("ExternalStorage", "-> uri=" + uri);
+ }
+ });
+ } catch (IOException e) {
+ // Unable to create file, likely because external storage is
+ // not currently mounted.
+ Log.w("ExternalStorage", "Error writing " + file, e);
+ }
+ }
+
+ void deleteExternalStoragePublicPicture() {
+ // Create a path where we will place our picture in the user's
+ // public pictures directory and delete the file. If external
+ // storage is not currently mounted this will fail.
+ File path = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PICTURES);
+ File file = new File(path, "DemoPicture.jpg");
+ file.delete();
+ }
+
+ boolean hasExternalStoragePublicPicture() {
+ // Create a path where we will place our picture in the user's
+ // public pictures directory and check if the file exists. If
+ // external storage is not currently mounted this will think the
+ // picture doesn't exist.
+ File path = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PICTURES);
+ File file = new File(path, "DemoPicture.jpg");
+ return file.exists();
+ }
+// END_INCLUDE(public_picture)
+
+// BEGIN_INCLUDE(private_picture)
+ void createExternalStoragePrivatePicture() {
+ // Create a path where we will place our picture in our own private
+ // pictures directory. Note that we don't really need to place a
+ // picture in DIRECTORY_PICTURES, since the media scanner will see
+ // all media in these directories; this may be useful with other
+ // media types such as DIRECTORY_MUSIC however to help it classify
+ // your media for display to the user.
+ File path = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
+ File file = new File(path, "DemoPicture.jpg");
+
+ try {
+ // Very simple code to copy a picture from the application's
+ // resource into the external file. Note that this code does
+ // no error checking, and assumes the picture is small (does not
+ // try to copy it in chunks). Note that if external storage is
+ // not currently mounted this will silently fail.
+ InputStream is = getResources().openRawResource(R.drawable.balloons);
+ OutputStream os = new FileOutputStream(file);
+ byte[] data = new byte[is.available()];
+ is.read(data);
+ os.write(data);
+ is.close();
+ os.close();
+
+ // Tell the media scanner about the new file so that it is
+ // immediately available to the user.
+ MediaScannerConnection.scanFile(this,
+ new String[] { file.toString() }, null,
+ new MediaScannerConnection.ScanResultListener() {
+ public void onScanCompleted(String path, Uri uri) {
+ Log.i("ExternalStorage", "Scanned " + path + ":");
+ Log.i("ExternalStorage", "-> uri=" + uri);
+ }
+ });
+ } catch (IOException e) {
+ // Unable to create file, likely because external storage is
+ // not currently mounted.
+ Log.w("ExternalStorage", "Error writing " + file, e);
+ }
+ }
+
+ void deleteExternalStoragePrivatePicture() {
+ // Create a path where we will place our picture in the user's
+ // public pictures directory and delete the file. If external
+ // storage is not currently mounted this will fail.
+ File path = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
+ if (path != null) {
+ File file = new File(path, "DemoPicture.jpg");
+ file.delete();
+ }
+ }
+
+ boolean hasExternalStoragePrivatePicture() {
+ // Create a path where we will place our picture in the user's
+ // public pictures directory and check if the file exists. If
+ // external storage is not currently mounted this will think the
+ // picture doesn't exist.
+ File path = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
+ if (path != null) {
+ File file = new File(path, "DemoPicture.jpg");
+ return file.exists();
+ }
+ return false;
+ }
+// END_INCLUDE(private_picture)
+
+// BEGIN_INCLUDE(private_file)
+ void createExternalStoragePrivateFile() {
+ // Create a path where we will place our private file on external
+ // storage.
+ File file = new File(getExternalFilesDir(null), "DemoFile.jpg");
+
+ try {
+ // Very simple code to copy a picture from the application's
+ // resource into the external file. Note that this code does
+ // no error checking, and assumes the picture is small (does not
+ // try to copy it in chunks). Note that if external storage is
+ // not currently mounted this will silently fail.
+ InputStream is = getResources().openRawResource(R.drawable.balloons);
+ OutputStream os = new FileOutputStream(file);
+ byte[] data = new byte[is.available()];
+ is.read(data);
+ os.write(data);
+ is.close();
+ os.close();
+ } catch (IOException e) {
+ // Unable to create file, likely because external storage is
+ // not currently mounted.
+ Log.w("ExternalStorage", "Error writing " + file, e);
+ }
+ }
+
+ void deleteExternalStoragePrivateFile() {
+ // Get path for the file on external storage. If external
+ // storage is not currently mounted this will fail.
+ File file = new File(getExternalFilesDir(null), "DemoFile.jpg");
+ if (file != null) {
+ file.delete();
+ }
+ }
+
+ boolean hasExternalStoragePrivateFile() {
+ // Get path for the file on external storage. If external
+ // storage is not currently mounted this will fail.
+ File file = new File(getExternalFilesDir(null), "DemoFile.jpg");
+ if (file != null) {
+ return file.exists();
+ }
+ return false;
+ }
+ // END_INCLUDE(private_file)
+
+ Item createStorageControls(CharSequence label, File path,
+ View.OnClickListener createClick,
+ View.OnClickListener deleteClick) {
+ LayoutInflater inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
+ Item item = new Item();
+ item.mRoot = inflater.inflate(R.layout.external_storage_item, null);
+ TextView tv = (TextView)item.mRoot.findViewById(R.id.label);
+ tv.setText(label);
+ if (path != null) {
+ tv = (TextView)item.mRoot.findViewById(R.id.path);
+ tv.setText(path.toString());
+ }
+ item.mCreate = (Button)item.mRoot.findViewById(R.id.create);
+ item.mCreate.setOnClickListener(createClick);
+ item.mDelete = (Button)item.mRoot.findViewById(R.id.delete);
+ item.mDelete.setOnClickListener(deleteClick);
+ return item;
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/content/_index.html b/samples/ApiDemos/src/com/example/android/apis/content/_index.html
index 1aa52b3..769a927 100644
--- a/samples/ApiDemos/src/com/example/android/apis/content/_index.html
+++ b/samples/ApiDemos/src/com/example/android/apis/content/_index.html
@@ -1,5 +1,8 @@
<h3>Resources</h3>
<dl>
+ <dt><a href="StyledText.html">External Storage</a></dt>
+ <dd>Demonstrates reading and writing files in external storage. </dd>
+
<dt><a href="StyledText.html">Styled Text</a></dt>
<dd>Demonstrates loading styled text (bold, italic) defined in a resource file. </dd>