+ */
+public class TextToSpeechActivity extends Activity implements TextToSpeech.OnInitListener {
+
+ private static final String TAG = "TextToSpeechDemo";
+
+ private TextToSpeech mTts;
+ private Button mAgainButton;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.text_to_speech);
+
+ // Initialize text-to-speech. This is an asynchronous operation.
+ // The OnInitListener (second argument) is called after initialization completes.
+ mTts = new TextToSpeech(this,
+ this // TextToSpeech.OnInitListener
+ );
+
+ // The button is disabled in the layout.
+ // It will be enabled upon initialization of the TTS engine.
+ mAgainButton = (Button) findViewById(R.id.again_button);
+
+ mAgainButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ sayHello();
+ }
+ });
+ }
+
+ @Override
+ public void onDestroy() {
+ // Don't forget to shutdown!
+ if (mTts != null) {
+ mTts.stop();
+ mTts.shutdown();
+ }
+
+ super.onDestroy();
+ }
+
+ // Implements TextToSpeech.OnInitListener.
+ public void onInit(int status) {
+ // status can be either TextToSpeech.SUCCESS or TextToSpeech.ERROR.
+ if (status == TextToSpeech.SUCCESS) {
+ // Set preferred language to US english.
+ // Note that a language may not be available, and the result will indicate this.
+ int result = mTts.setLanguage(Locale.US);
+ // Try this someday for some interesting results.
+ // int result mTts.setLanguage(Locale.FRANCE);
+ if (result == TextToSpeech.LANG_MISSING_DATA ||
+ result == TextToSpeech.LANG_NOT_SUPPORTED) {
+ // Lanuage data is missing or the language is not supported.
+ Log.e(TAG, "Language is not available.");
+ } else {
+ // Check the documentation for other possible result codes.
+ // For example, the language may be available for the locale,
+ // but not for the specified country and variant.
+
+ // The TTS engine has been successfully initialized.
+ // Allow the user to press the button for the app to speak again.
+ mAgainButton.setEnabled(true);
+ // Greet the user.
+ sayHello();
+ }
+ } else {
+ // Initialization failed.
+ Log.e(TAG, "Could not initialize TextToSpeech.");
+ }
+ }
+
+ private static final Random RANDOM = new Random();
+ private static final String[] HELLOS = {
+ "Hello",
+ "Salutations",
+ "Greetings",
+ "Howdy",
+ "What's crack-a-lackin?",
+ "That explains the stench!"
+ };
+
+ private void sayHello() {
+ // Select a random hello.
+ int helloLength = HELLOS.length;
+ String hello = HELLOS[RANDOM.nextInt(helloLength)];
+ mTts.speak(hello,
+ TextToSpeech.QUEUE_FLUSH, // Drop all pending entries in the playback queue.
+ null);
+ }
+
+}
From 25b6aed7b2e01ce7bdc0dfa1a79eaf009ad178fe Mon Sep 17 00:00:00 2001
From: Scott Main
Date: Tue, 8 Dec 2009 14:50:17 -0800
Subject: [PATCH 02/17] change the way bytes are read from InputStream. Read
only the bytes received and don't allocate new arrays Also revise the
mechanism for writing the outgoing messages to the screen to be more
consistent with reading and cleanup line lengths to be <100 chars.
---
.../android/BluetoothChat/BluetoothChat.java | 37 ++++++++++++-------
.../BluetoothChat/BluetoothChatService.java | 21 ++++-------
2 files changed, 31 insertions(+), 27 deletions(-)
diff --git a/samples/BluetoothChat/src/com/example/android/BluetoothChat/BluetoothChat.java b/samples/BluetoothChat/src/com/example/android/BluetoothChat/BluetoothChat.java
index e4b9d5289..d05bbd611 100644
--- a/samples/BluetoothChat/src/com/example/android/BluetoothChat/BluetoothChat.java
+++ b/samples/BluetoothChat/src/com/example/android/BluetoothChat/BluetoothChat.java
@@ -50,14 +50,13 @@ public class BluetoothChat extends Activity {
// Message types sent from the BluetoothChatService Handler
public static final int MESSAGE_STATE_CHANGE = 1;
public static final int MESSAGE_READ = 2;
- public static final int MESSAGE_OUTGOING_STRING = 3;
+ public static final int MESSAGE_WRITE = 3;
public static final int MESSAGE_DEVICE_NAME = 4;
public static final int MESSAGE_TOAST = 5;
// Key names received from the BluetoothChatService Handler
public static final String DEVICE_NAME = "device_name";
public static final String TOAST = "toast";
- public static final String MESSAGE_WRITE = "message_write";
// Intent request codes
private static final int REQUEST_CONNECT_DEVICE = 1;
@@ -192,9 +191,10 @@ public class BluetoothChat extends Activity {
private void ensureDiscoverable() {
if(D) Log.d(TAG, "ensure discoverable");
- if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
+ if (mBluetoothAdapter.getScanMode() !=
+ BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
- discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); // set max duration
+ discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
}
}
@@ -223,7 +223,8 @@ public class BluetoothChat extends Activity {
}
// The action listener for the EditText widget, to listen for the return key
- private TextView.OnEditorActionListener mWriteListener = new TextView.OnEditorActionListener() {
+ private TextView.OnEditorActionListener mWriteListener =
+ new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
// If the action is a key-up event on the return key, send the message
if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) {
@@ -257,20 +258,27 @@ public class BluetoothChat extends Activity {
break;
}
break;
- case MESSAGE_OUTGOING_STRING:
- mConversationArrayAdapter.add("Me: " + new String(msg.getData().getByteArray(MESSAGE_WRITE)).trim());
- mOutEditText.setText(mOutStringBuffer);
+ case MESSAGE_WRITE:
+ byte[] writeBuf = (byte[]) msg.obj;
+ // construct a string from the buffer
+ String writeMessage = new String(writeBuf);
+ mConversationArrayAdapter.add("Me: " + writeMessage);
break;
case MESSAGE_READ:
- byte[] buf = (byte[]) msg.obj;
- mConversationArrayAdapter.add(mConnectedDeviceName+": " + new String(buf).trim());
+ byte[] readBuf = (byte[]) msg.obj;
+ // construct a string from the valid bytes in the buffer
+ String readMessage = new String(readBuf, 0, msg.arg1);
+ mConversationArrayAdapter.add(mConnectedDeviceName+": " + readMessage);
break;
case MESSAGE_DEVICE_NAME:
- mConnectedDeviceName = msg.getData().getString(DEVICE_NAME); // save the connected device's name
- Toast.makeText(getApplicationContext(), "Connected to " + mConnectedDeviceName, Toast.LENGTH_SHORT).show();
+ // save the connected device's name
+ mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
+ Toast.makeText(getApplicationContext(), "Connected to "
+ + mConnectedDeviceName, Toast.LENGTH_SHORT).show();
break;
case MESSAGE_TOAST:
- Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST), Toast.LENGTH_SHORT).show();
+ Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST),
+ Toast.LENGTH_SHORT).show();
break;
}
}
@@ -283,7 +291,8 @@ public class BluetoothChat extends Activity {
// When DeviceListActivity returns with a device to connect
if (resultCode == Activity.RESULT_OK) {
// Get the device MAC address
- String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
+ String address = data.getExtras()
+ .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
// Get the BLuetoothDevice object
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
// Attempt to connect to the device
diff --git a/samples/BluetoothChat/src/com/example/android/BluetoothChat/BluetoothChatService.java b/samples/BluetoothChat/src/com/example/android/BluetoothChat/BluetoothChatService.java
index 8d29c6a99..d0c1d43cd 100644
--- a/samples/BluetoothChat/src/com/example/android/BluetoothChat/BluetoothChatService.java
+++ b/samples/BluetoothChat/src/com/example/android/BluetoothChat/BluetoothChatService.java
@@ -57,7 +57,7 @@ public class BluetoothChatService {
private int mState;
// Constants that indicate the current connection state
- public static final int STATE_NONE = 0; // we're doing nothing. only valid during setup/shutdown
+ public static final int STATE_NONE = 0; // we're doing nothing
public static final int STATE_LISTEN = 1; // now listening for incoming connections
public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
public static final int STATE_CONNECTED = 3; // now connected to a remote device
@@ -397,10 +397,8 @@ public class BluetoothChatService {
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
- mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer).sendToTarget();
-
- // Reload the buffer to clear extra bytes from the previous read
- buffer = new byte[1024];
+ mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
+ .sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
@@ -411,18 +409,15 @@ public class BluetoothChatService {
/**
* Write to the connected OutStream.
- * @param b The bytes to write
+ * @param buffer The bytes to write
*/
- public void write(byte[] b) {
+ public void write(byte[] buffer) {
try {
- mmOutStream.write(b);
+ mmOutStream.write(buffer);
// Share the sent message back to the UI Activity
- Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_OUTGOING_STRING);
- Bundle bundle = new Bundle();
- bundle.putByteArray(BluetoothChat.MESSAGE_WRITE, b);
- msg.setData(bundle);
- mHandler.sendMessage(msg);
+ mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer)
+ .sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
From c6d634b850ef17c77457b47e352a5136f78bd33f Mon Sep 17 00:00:00 2001
From: Trevor Johns
Date: Tue, 24 Nov 2009 05:16:35 -0800
Subject: [PATCH 03/17] Adding ContactManager sample to SDK samples.
This sample demonstrates use of the ContactsContract classes introduced in API Level 5.
---
samples/ContactManager/Android.mk | 16 +
samples/ContactManager/AndroidManifest.xml | 35 ++
samples/ContactManager/_index.html | 10 +
.../ContactManager/res/drawable-hdpi/icon.png | Bin 0 -> 4147 bytes
.../ContactManager/res/drawable-ldpi/icon.png | Bin 0 -> 1723 bytes
.../ContactManager/res/drawable-mdpi/icon.png | Bin 0 -> 2574 bytes
.../res/layout/account_entry.xml | 49 +++
.../res/layout/contact_adder.xml | 80 ++++
.../res/layout/contact_entry.xml | 24 ++
.../res/layout/contact_manager.xml | 33 ++
samples/ContactManager/res/values/strings.xml | 33 ++
.../android/contactmanager/ContactAdder.java | 390 ++++++++++++++++++
.../contactmanager/ContactManager.java | 124 ++++++
13 files changed, 794 insertions(+)
create mode 100644 samples/ContactManager/Android.mk
create mode 100644 samples/ContactManager/AndroidManifest.xml
create mode 100644 samples/ContactManager/_index.html
create mode 100644 samples/ContactManager/res/drawable-hdpi/icon.png
create mode 100644 samples/ContactManager/res/drawable-ldpi/icon.png
create mode 100644 samples/ContactManager/res/drawable-mdpi/icon.png
create mode 100644 samples/ContactManager/res/layout/account_entry.xml
create mode 100644 samples/ContactManager/res/layout/contact_adder.xml
create mode 100644 samples/ContactManager/res/layout/contact_entry.xml
create mode 100644 samples/ContactManager/res/layout/contact_manager.xml
create mode 100644 samples/ContactManager/res/values/strings.xml
create mode 100644 samples/ContactManager/src/com/google/example/android/contactmanager/ContactAdder.java
create mode 100644 samples/ContactManager/src/com/google/example/android/contactmanager/ContactManager.java
diff --git a/samples/ContactManager/Android.mk b/samples/ContactManager/Android.mk
new file mode 100644
index 000000000..c58571685
--- /dev/null
+++ b/samples/ContactManager/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := ContactManager
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/ContactManager/AndroidManifest.xml b/samples/ContactManager/AndroidManifest.xml
new file mode 100644
index 000000000..c0a01cd31
--- /dev/null
+++ b/samples/ContactManager/AndroidManifest.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ContactManager/_index.html b/samples/ContactManager/_index.html
new file mode 100644
index 000000000..1fd51f06f
--- /dev/null
+++ b/samples/ContactManager/_index.html
@@ -0,0 +1,10 @@
+
A sample application that demonstrates how to manually query the system contacts provider using
+the new ContactsContract API, as
+well as manually insert contacts into a specific account.
+
+
In most cases, you will want to ask the existing Contacts activity to handle these tasks for you,
+resulting in a more consistent user experience.
+
+
+
\ No newline at end of file
diff --git a/samples/ContactManager/res/drawable-hdpi/icon.png b/samples/ContactManager/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..8074c4c571b8cd19e27f4ee5545df367420686d7
GIT binary patch
literal 4147
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
literal 0
HcmV?d00001
diff --git a/samples/ContactManager/res/drawable-ldpi/icon.png b/samples/ContactManager/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..1095584ec21f71cd0afc9e0993aa2209671b590c
GIT binary patch
literal 1723
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
+
+
+
+
+
+
+
diff --git a/samples/ContactManager/res/layout/contact_adder.xml b/samples/ContactManager/res/layout/contact_adder.xml
new file mode 100644
index 000000000..92d06f305
--- /dev/null
+++ b/samples/ContactManager/res/layout/contact_adder.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ContactManager/res/layout/contact_entry.xml b/samples/ContactManager/res/layout/contact_entry.xml
new file mode 100644
index 000000000..9909025a7
--- /dev/null
+++ b/samples/ContactManager/res/layout/contact_entry.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
diff --git a/samples/ContactManager/res/layout/contact_manager.xml b/samples/ContactManager/res/layout/contact_manager.xml
new file mode 100644
index 000000000..73f6f8151
--- /dev/null
+++ b/samples/ContactManager/res/layout/contact_manager.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
diff --git a/samples/ContactManager/res/values/strings.xml b/samples/ContactManager/res/values/strings.xml
new file mode 100644
index 000000000..c7a65b4a0
--- /dev/null
+++ b/samples/ContactManager/res/values/strings.xml
@@ -0,0 +1,33 @@
+
+
+
+
+ Account
+ Add Contact
+ Add Contact
+ All Accounts
+ Contact Manager
+ Contact creation failed, check logs.
+ Contact Email
+ Contact Name
+ Contact Phone
+ Save
+ Select
+ Select label
+ Show Invisible Contacts (Only)
+ Target Account
+ (Undefined)
+
diff --git a/samples/ContactManager/src/com/google/example/android/contactmanager/ContactAdder.java b/samples/ContactManager/src/com/google/example/android/contactmanager/ContactAdder.java
new file mode 100644
index 000000000..92ad5a237
--- /dev/null
+++ b/samples/ContactManager/src/com/google/example/android/contactmanager/ContactAdder.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2009 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.google.example.android.contactmanager;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorDescription;
+import android.accounts.OnAccountsUpdateListener;
+import android.app.Activity;
+import android.content.ContentProviderOperation;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.AdapterView.OnItemSelectedListener;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+public final class ContactAdder extends Activity implements OnAccountsUpdateListener
+{
+ public static final String TAG = "ContactsAdder";
+ public static final String ACCOUNT_NAME =
+ "com.google.example.android.contactmanager.ContactsAdder.ACCOUNT_NAME";
+ public static final String ACCOUNT_TYPE =
+ "com.google.example.android.contactmanager.ContactsAdder.ACCOUNT_TYPE";
+
+ private ArrayList mAccounts;
+ private AccountAdapter mAccountAdapter;
+ private Spinner mAccountSpinner;
+ private EditText mContactEmailEditText;
+ private ArrayList mContactEmailTypes;
+ private Spinner mContactEmailTypeSpinner;
+ private EditText mContactNameEditText;
+ private EditText mContactPhoneEditText;
+ private ArrayList mContactPhoneTypes;
+ private Spinner mContactPhoneTypeSpinner;
+ private Button mContactSaveButton;
+ private AccountData mSelectedAccount;
+
+ /**
+ * Called when the activity is first created. Responsible for initializing the UI.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ Log.v(TAG, "Activity State: onCreate()");
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.contact_adder);
+
+ // Obtain handles to UI objects
+ mAccountSpinner = (Spinner) findViewById(R.id.accountSpinner);
+ mContactNameEditText = (EditText) findViewById(R.id.contactNameEditText);
+ mContactPhoneEditText = (EditText) findViewById(R.id.contactPhoneEditText);
+ mContactEmailEditText = (EditText) findViewById(R.id.contactEmailEditText);
+ mContactPhoneTypeSpinner = (Spinner) findViewById(R.id.contactPhoneTypeSpinner);
+ mContactEmailTypeSpinner = (Spinner) findViewById(R.id.contactEmailTypeSpinner);
+ mContactSaveButton = (Button) findViewById(R.id.contactSaveButton);
+
+ // Prepare list of supported account types
+ // Note: Other types are available in ContactsContract.CommonDataKinds
+ // Also, be aware that type IDs differ between Phone and Email, and MUST be computed
+ // separately.
+ mContactPhoneTypes = new ArrayList();
+ mContactPhoneTypes.add(ContactsContract.CommonDataKinds.Phone.TYPE_HOME);
+ mContactPhoneTypes.add(ContactsContract.CommonDataKinds.Phone.TYPE_WORK);
+ mContactPhoneTypes.add(ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
+ mContactPhoneTypes.add(ContactsContract.CommonDataKinds.Phone.TYPE_OTHER);
+ mContactEmailTypes = new ArrayList();
+ mContactEmailTypes.add(ContactsContract.CommonDataKinds.Email.TYPE_HOME);
+ mContactEmailTypes.add(ContactsContract.CommonDataKinds.Email.TYPE_WORK);
+ mContactEmailTypes.add(ContactsContract.CommonDataKinds.Email.TYPE_MOBILE);
+ mContactEmailTypes.add(ContactsContract.CommonDataKinds.Email.TYPE_OTHER);
+
+ // Prepare model for account spinner
+ mAccounts = new ArrayList();
+ mAccountAdapter = new AccountAdapter(this, mAccounts);
+ mAccountSpinner.setAdapter(mAccountAdapter);
+
+ // Populate list of account types for phone
+ ArrayAdapter adapter;
+ adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ Iterator iter;
+ iter = mContactPhoneTypes.iterator();
+ while (iter.hasNext()) {
+ adapter.add(ContactsContract.CommonDataKinds.Phone.getTypeLabel(
+ this.getResources(),
+ iter.next(),
+ getString(R.string.undefinedTypeLabel)).toString());
+ }
+ mContactPhoneTypeSpinner.setAdapter(adapter);
+ mContactPhoneTypeSpinner.setPrompt(getString(R.string.selectLabel));
+
+ // Populate list of account types for email
+ adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ iter = mContactEmailTypes.iterator();
+ while (iter.hasNext()) {
+ adapter.add(ContactsContract.CommonDataKinds.Email.getTypeLabel(
+ this.getResources(),
+ iter.next(),
+ getString(R.string.undefinedTypeLabel)).toString());
+ }
+ mContactEmailTypeSpinner.setAdapter(adapter);
+ mContactEmailTypeSpinner.setPrompt(getString(R.string.selectLabel));
+
+ // Prepare the system account manager. On registering the listener below, we also ask for
+ // an initial callback to pre-populate the account list.
+ AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
+
+ // Register handlers for UI elements
+ mAccountSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
+ public void onItemSelected(AdapterView> parent, View view, int position, long i) {
+ updateAccountSelection();
+ }
+
+ public void onNothingSelected(AdapterView> parent) {
+ // We don't need to worry about nothing being selected, since Spinners don't allow
+ // this.
+ }
+ });
+ mContactSaveButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ onSaveButtonClicked();
+ }
+ });
+ }
+
+ /**
+ * Actions for when the Save button is clicked. Creates a contact entry and terminates the
+ * activity.
+ */
+ private void onSaveButtonClicked() {
+ Log.v(TAG, "Save button clicked");
+ createContactEntry();
+ finish();
+ }
+
+ /**
+ * Creates a contact entry from the current UI values in the account named by mSelectedAccount.
+ */
+ protected void createContactEntry() {
+ // Get values from UI
+ String name = mContactNameEditText.getText().toString();
+ String phone = mContactPhoneEditText.getText().toString();
+ String email = mContactEmailEditText.getText().toString();
+ int phoneType = mContactPhoneTypes.get(
+ mContactPhoneTypeSpinner.getSelectedItemPosition());
+ int emailType = mContactEmailTypes.get(
+ mContactEmailTypeSpinner.getSelectedItemPosition());;
+
+ // Prepare contact creation request
+ //
+ // Note: We use RawContacts because this data must be associated with a particular account.
+ // The system will aggregate this with any other data for this contact and create a
+ // coresponding entry in the ContactsContract.Contacts provider for us.
+ ArrayList ops = new ArrayList();
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
+ .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType())
+ .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName())
+ .build());
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+ .withValue(ContactsContract.Data.MIMETYPE,
+ ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
+ .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name)
+ .build());
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+ .withValue(ContactsContract.Data.MIMETYPE,
+ ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
+ .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
+ .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType)
+ .build());
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+ .withValue(ContactsContract.Data.MIMETYPE,
+ ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
+ .withValue(ContactsContract.CommonDataKinds.Email.DATA, email)
+ .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType)
+ .build());
+
+ // Ask the Contact provider to create a new contact
+ Log.i(TAG,"Selected account: " + mSelectedAccount.getName() + " (" +
+ mSelectedAccount.getType() + ")");
+ Log.i(TAG,"Creating contact: " + name);
+ try {
+ getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
+ } catch (Exception e) {
+ // Display warning
+ Context ctx = getApplicationContext();
+ CharSequence txt = getString(R.string.contactCreationFailure);
+ int duration = Toast.LENGTH_SHORT;
+ Toast toast = Toast.makeText(ctx, txt, duration);
+ toast.show();
+
+ // Log exception
+ Log.e(TAG, "Exceptoin encoutered while inserting contact: " + e);
+ }
+ }
+
+ /**
+ * Called when this activity is about to be destroyed by the system.
+ */
+ @Override
+ public void onDestroy() {
+ // Remove AccountManager callback
+ AccountManager.get(this).removeOnAccountsUpdatedListener(this);
+ super.onDestroy();
+ }
+
+ /**
+ * Updates account list spinner when the list of Accounts on the system changes. Satisfies
+ * OnAccountsUpdateListener implementation.
+ */
+ public void onAccountsUpdated(Account[] a) {
+ Log.i(TAG, "Account list update detected");
+ // Clear out any old data to prevent duplicates
+ mAccounts.clear();
+
+ // Get account data from system
+ AuthenticatorDescription[] accountTypes = AccountManager.get(this).getAuthenticatorTypes();
+
+ // Populate tables
+ for (int i = 0; i < a.length; i++) {
+ // The user may have multiple accounts with the same name, so we need to construct a
+ // meaningful display name for each.
+ String systemAccountType = a[i].type;
+ AuthenticatorDescription ad = getAuthenticatorDescription(systemAccountType,
+ accountTypes);
+ AccountData data = new AccountData(a[i].name, ad);
+ mAccounts.add(data);
+ }
+
+ // Update the account spinner
+ mAccountAdapter.notifyDataSetChanged();
+ }
+
+ /**
+ * Obtain the AuthenticatorDescription for a given account type.
+ * @param type The account type to locate.
+ * @param dictionary An array of AuthenticatorDescriptions, as returned by AccountManager.
+ * @return The description for the specified account type.
+ */
+ private static AuthenticatorDescription getAuthenticatorDescription(String type,
+ AuthenticatorDescription[] dictionary) {
+ for (int i = 0; i < dictionary.length; i++) {
+ if (dictionary[i].type.equals(type)) {
+ return dictionary[i];
+ }
+ }
+ // No match found
+ throw new RuntimeException("Unable to find matching authenticator");
+ }
+
+ /**
+ * Update account selection. If NO_ACCOUNT is selected, then we prohibit inserting new contacts.
+ */
+ private void updateAccountSelection() {
+ // Read current account selection
+ mSelectedAccount = (AccountData) mAccountSpinner.getSelectedItem();
+ }
+
+ /**
+ * A container class used to repreresent all known information about an account.
+ */
+ private class AccountData {
+ private String mName;
+ private String mType;
+ private CharSequence mTypeLabel;
+ private Drawable mIcon;
+
+ /**
+ * @param name The name of the account. This is usually the user's email address or
+ * username.
+ * @param description The description for this account. This will be dictated by the
+ * type of account returned, and can be obtained from the system AccountManager.
+ */
+ public AccountData(String name, AuthenticatorDescription description) {
+ mName = name;
+ if (description != null) {
+ mType = description.type;
+
+ // The type string is stored in a resource, so we need to convert it into something
+ // human readable.
+ String packageName = description.packageName;
+ PackageManager pm = getPackageManager();
+
+ if (description.labelId != 0) {
+ mTypeLabel = pm.getText(packageName, description.labelId, null);
+ if (mTypeLabel == null) {
+ throw new IllegalArgumentException("LabelID provided, but label not found");
+ }
+ } else {
+ mTypeLabel = "";
+ }
+
+ if (description.iconId != 0) {
+ mIcon = pm.getDrawable(packageName, description.iconId, null);
+ if (mIcon == null) {
+ throw new IllegalArgumentException("IconID provided, but drawable not " +
+ "found");
+ }
+ } else {
+ mIcon = getResources().getDrawable(android.R.drawable.sym_def_app_icon);
+ }
+ }
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getType() {
+ return mType;
+ }
+
+ public CharSequence getTypeLabel() {
+ return mTypeLabel;
+ }
+
+ public Drawable getIcon() {
+ return mIcon;
+ }
+
+ public String toString() {
+ return mName;
+ }
+ }
+
+ /**
+ * Custom adapter used to display account icons and descriptions in the account spinner.
+ */
+ private class AccountAdapter extends ArrayAdapter {
+ public AccountAdapter(Context context, ArrayList accountData) {
+ super(context, android.R.layout.simple_spinner_item, accountData);
+ setDropDownViewResource(R.layout.account_entry);
+ }
+
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ // Inflate a view template
+ if (convertView == null) {
+ LayoutInflater layoutInflater = getLayoutInflater();
+ convertView = layoutInflater.inflate(R.layout.account_entry, parent, false);
+ }
+ TextView firstAccountLine = (TextView) convertView.findViewById(R.id.firstAccountLine);
+ TextView secondAccountLine = (TextView) convertView.findViewById(R.id.secondAccountLine);
+ ImageView accountIcon = (ImageView) convertView.findViewById(R.id.accountIcon);
+
+ // Populate template
+ AccountData data = getItem(position);
+ firstAccountLine.setText(data.getName());
+ secondAccountLine.setText(data.getTypeLabel());
+ Drawable icon = data.getIcon();
+ if (icon == null) {
+ icon = getResources().getDrawable(android.R.drawable.ic_menu_search);
+ }
+ accountIcon.setImageDrawable(icon);
+ return convertView;
+ }
+ }
+}
diff --git a/samples/ContactManager/src/com/google/example/android/contactmanager/ContactManager.java b/samples/ContactManager/src/com/google/example/android/contactmanager/ContactManager.java
new file mode 100644
index 000000000..c4d9803fa
--- /dev/null
+++ b/samples/ContactManager/src/com/google/example/android/contactmanager/ContactManager.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2009 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.google.example.android.contactmanager;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+
+public final class ContactManager extends Activity
+{
+
+ public static final String TAG = "ContactManager";
+
+ private Button mAddAccountButton;
+ private ListView mContactList;
+ private boolean mShowInvisible;
+ private CheckBox mShowInvisibleControl;
+
+ /**
+ * Called when the activity is first created. Responsible for initializing the UI.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ Log.v(TAG, "Activity State: onCreate()");
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.contact_manager);
+
+ // Obtain handles to UI objects
+ mAddAccountButton = (Button) findViewById(R.id.addContactButton);
+ mContactList = (ListView) findViewById(R.id.contactList);
+ mShowInvisibleControl = (CheckBox) findViewById(R.id.showInvisible);
+
+ // Initialize class properties
+ mShowInvisible = false;
+ mShowInvisibleControl.setChecked(mShowInvisible);
+
+ // Register handler for UI elements
+ mAddAccountButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Log.d(TAG, "mAddAccountButton clicked");
+ launchContactAdder();
+ }
+ });
+ mShowInvisibleControl.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ Log.d(TAG, "mShowInvisibleControl changed: " + isChecked);
+ mShowInvisible = isChecked;
+ populateContactList();
+ }
+ });
+
+ // Populate the contact list
+ populateContactList();
+ }
+
+ /**
+ * Populate the contact list based on account currently selected in the account spinner.
+ */
+ private void populateContactList() {
+ // Build adapter with contact entries
+ Cursor cursor = getContacts();
+ String[] fields = new String[] {
+ ContactsContract.Data.DISPLAY_NAME
+ };
+ SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.contact_entry, cursor,
+ fields, new int[] {R.id.contactEntryText});
+ mContactList.setAdapter(adapter);
+ }
+
+ /**
+ * Obtains the contact list for the currently selected account.
+ *
+ * @return A cursor for for accessing the contact list.
+ */
+ private Cursor getContacts()
+ {
+ // Run query
+ Uri uri = ContactsContract.Contacts.CONTENT_URI;
+ String[] projection = new String[] {
+ ContactsContract.Contacts.LOOKUP_KEY,
+ ContactsContract.Contacts.DISPLAY_NAME
+ };
+ String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '" +
+ (mShowInvisible ? "0" : "1") + "'";
+ String[] selectionArgs = null;
+ String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
+
+ return managedQuery(uri, projection, selection, selectionArgs, sortOrder);
+ }
+
+ /**
+ * Launches the ContactAdder activity to add a new contact to the selected accont.
+ */
+ protected void launchContactAdder() {
+ Intent i = new Intent(this, ContactAdder.class);
+ startActivity(i);
+ }
+}
From bba4ff7c38e7e6837fabdb225cdb24d12a17eabc Mon Sep 17 00:00:00 2001
From: Trevor Johns
Date: Tue, 8 Dec 2009 23:23:53 -0800
Subject: [PATCH 04/17] Fixing crash in ContactManager: Changing
ContactsContract.Contacts.LOOKUP_KEY to ContactsContract.Contacts._ID.
---
.../google/example/android/contactmanager/ContactManager.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/samples/ContactManager/src/com/google/example/android/contactmanager/ContactManager.java b/samples/ContactManager/src/com/google/example/android/contactmanager/ContactManager.java
index c4d9803fa..7b416b084 100644
--- a/samples/ContactManager/src/com/google/example/android/contactmanager/ContactManager.java
+++ b/samples/ContactManager/src/com/google/example/android/contactmanager/ContactManager.java
@@ -103,7 +103,7 @@ public final class ContactManager extends Activity
// Run query
Uri uri = ContactsContract.Contacts.CONTENT_URI;
String[] projection = new String[] {
- ContactsContract.Contacts.LOOKUP_KEY,
+ ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME
};
String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '" +
From 5dc449896cb596159652d15e7333d74e53601609 Mon Sep 17 00:00:00 2001
From: Jack Palevich
Date: Fri, 27 Nov 2009 19:54:46 +0800
Subject: [PATCH 05/17] Add OpenGL ES 2.0 sample.
This sample renders a textured triangle using OpenGL ES 2.0.
If OpenGL ES 2.0 is not available, the sample falls back to
using OpenGL ES 1.0.
---
samples/ApiDemos/AndroidManifest.xml | 10 +
.../android/apis/graphics/GLES20Activity.java | 72 +++++
.../apis/graphics/GLES20TriangleRenderer.java | 255 ++++++++++++++++++
3 files changed, 337 insertions(+)
create mode 100644 samples/ApiDemos/src/com/example/android/apis/graphics/GLES20Activity.java
create mode 100644 samples/ApiDemos/src/com/example/android/apis/graphics/GLES20TriangleRenderer.java
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index a0b8705fb..c96417889 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -1416,6 +1416,16 @@
+
+
+
+
+
+
+
= 0x20000);
+ }
+
+ @Override
+ protected void onResume() {
+ // Ideally a game should implement onResume() and onPause()
+ // to take appropriate action when the activity looses focus
+ super.onResume();
+ mGLSurfaceView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ // Ideally a game should implement onResume() and onPause()
+ // to take appropriate action when the activity looses focus
+ super.onPause();
+ mGLSurfaceView.onPause();
+ }
+
+ private GLSurfaceView mGLSurfaceView;
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/GLES20TriangleRenderer.java b/samples/ApiDemos/src/com/example/android/apis/graphics/GLES20TriangleRenderer.java
new file mode 100644
index 000000000..956eb6862
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/GLES20TriangleRenderer.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2009 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.graphics;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+import android.opengl.GLUtils;
+import android.opengl.Matrix;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.example.android.apis.R;
+
+class GLES20TriangleRenderer implements GLSurfaceView.Renderer {
+
+ public GLES20TriangleRenderer(Context context) {
+ mContext = context;
+ mTriangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length
+ * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
+ mTriangleVertices.put(mTriangleVerticesData).position(0);
+ }
+
+ public void onDrawFrame(GL10 glUnused) {
+ // Ignore the passed-in GL10 interface, and use the GLES20
+ // class's static methods instead.
+ GLES20.glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
+ GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
+ GLES20.glUseProgram(mProgram);
+ checkGlError("glUseProgram");
+
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);
+
+ mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
+ GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
+ TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
+ checkGlError("glVertexAttribPointer maPosition");
+ mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
+ GLES20.glEnableVertexAttribArray(maPositionHandle);
+ checkGlError("glEnableVertexAttribArray maPositionHandle");
+ GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false,
+ TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
+ checkGlError("glVertexAttribPointer maTextureHandle");
+ GLES20.glEnableVertexAttribArray(maTextureHandle);
+ checkGlError("glEnableVertexAttribArray maTextureHandle");
+
+ long time = SystemClock.uptimeMillis() % 4000L;
+ float angle = 0.090f * ((int) time);
+ Matrix.setRotateM(mMMatrix, 0, angle, 0, 0, 1.0f);
+ Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);
+ Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
+
+ GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
+ checkGlError("glDrawArrays");
+ }
+
+ public void onSurfaceChanged(GL10 glUnused, int width, int height) {
+ // Ignore the passed-in GL10 interface, and use the GLES20
+ // class's static methods instead.
+ GLES20.glViewport(0, 0, width, height);
+ float ratio = (float) width / height;
+ Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
+ }
+
+ public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
+ // Ignore the passed-in GL10 interface, and use the GLES20
+ // class's static methods instead.
+ mProgram = createProgram(mVertexShader, mFragmentShader);
+ if (mProgram == 0) {
+ return;
+ }
+ maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
+ checkGlError("glGetAttribLocation aPosition");
+ if (maPositionHandle == -1) {
+ throw new RuntimeException("Could not get attrib location for aPosition");
+ }
+ maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
+ checkGlError("glGetAttribLocation aTextureCoord");
+ if (maTextureHandle == -1) {
+ throw new RuntimeException("Could not get attrib location for aTextureCoord");
+ }
+
+ muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
+ checkGlError("glGetUniformLocation uMVPMatrix");
+ if (muMVPMatrixHandle == -1) {
+ throw new RuntimeException("Could not get attrib location for uMVPMatrix");
+ }
+
+ /*
+ * Create our texture. This has to be done each time the
+ * surface is created.
+ */
+
+ int[] textures = new int[1];
+ GLES20.glGenTextures(1, textures, 0);
+
+ mTextureID = textures[0];
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);
+
+ GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_NEAREST);
+ GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
+ GLES20.GL_TEXTURE_MAG_FILTER,
+ GLES20.GL_LINEAR);
+
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
+ GLES20.GL_REPEAT);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
+ GLES20.GL_REPEAT);
+
+ InputStream is = mContext.getResources()
+ .openRawResource(R.raw.robot);
+ Bitmap bitmap;
+ try {
+ bitmap = BitmapFactory.decodeStream(is);
+ } finally {
+ try {
+ is.close();
+ } catch(IOException e) {
+ // Ignore.
+ }
+ }
+
+ GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
+ bitmap.recycle();
+
+ Matrix.setLookAtM(mVMatrix, 0, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
+ }
+
+ private int loadShader(int shaderType, String source) {
+ int shader = GLES20.glCreateShader(shaderType);
+ if (shader != 0) {
+ GLES20.glShaderSource(shader, source);
+ GLES20.glCompileShader(shader);
+ int[] compiled = new int[1];
+ GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
+ if (compiled[0] == 0) {
+ Log.e(TAG, "Could not compile shader " + shaderType + ":");
+ Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
+ GLES20.glDeleteShader(shader);
+ shader = 0;
+ }
+ }
+ return shader;
+ }
+
+ private int createProgram(String vertexSource, String fragmentSource) {
+ int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
+ if (vertexShader == 0) {
+ return 0;
+ }
+
+ int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
+ if (pixelShader == 0) {
+ return 0;
+ }
+
+ int program = GLES20.glCreateProgram();
+ if (program != 0) {
+ GLES20.glAttachShader(program, vertexShader);
+ checkGlError("glAttachShader");
+ GLES20.glAttachShader(program, pixelShader);
+ checkGlError("glAttachShader");
+ GLES20.glLinkProgram(program);
+ int[] linkStatus = new int[1];
+ GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
+ if (linkStatus[0] != GLES20.GL_TRUE) {
+ Log.e(TAG, "Could not link program: ");
+ Log.e(TAG, GLES20.glGetProgramInfoLog(program));
+ GLES20.glDeleteProgram(program);
+ program = 0;
+ }
+ }
+ return program;
+ }
+
+ private void checkGlError(String op) {
+ int error;
+ while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+ Log.e(TAG, op + ": glError " + error);
+ throw new RuntimeException(op + ": glError " + error);
+ }
+ }
+
+ private static final int FLOAT_SIZE_BYTES = 4;
+ private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
+ private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
+ private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
+ private final float[] mTriangleVerticesData = {
+ // X, Y, Z, U, V
+ -1.0f, -0.5f, 0, -0.5f, 0.0f,
+ 1.0f, -0.5f, 0, 1.5f, -0.0f,
+ 0.0f, 1.11803399f, 0, 0.5f, 1.61803399f };
+
+ private FloatBuffer mTriangleVertices;
+
+ private final String mVertexShader =
+ "uniform mat4 uMVPMatrix;\n" +
+ "attribute vec4 aPosition;\n" +
+ "attribute vec2 aTextureCoord;\n" +
+ "varying vec2 vTextureCoord;\n" +
+ "void main() {\n" +
+ " gl_Position = uMVPMatrix * aPosition;\n" +
+ " vTextureCoord = aTextureCoord;\n" +
+ "}\n";
+
+ private final String mFragmentShader =
+ "precision mediump float;\n" +
+ "varying vec2 vTextureCoord;\n" +
+ "uniform sampler2D sTexture;\n" +
+ "void main() {\n" +
+ " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
+ "}\n";
+
+ private float[] mMVPMatrix = new float[16];
+ private float[] mProjMatrix = new float[16];
+ private float[] mMMatrix = new float[16];
+ private float[] mVMatrix = new float[16];
+
+ private int mProgram;
+ private int mTextureID;
+ private int muMVPMatrixHandle;
+ private int maPositionHandle;
+ private int maTextureHandle;
+
+ private Context mContext;
+ private static String TAG = "GLES20TriangleRenderer";
+}
From 3c31d6cded51b6e6719b0052ec9987ca7218c868 Mon Sep 17 00:00:00 2001
From: Jack Palevich
Date: Wed, 9 Dec 2009 15:32:35 +0800
Subject: [PATCH 06/17] Simple Matrix Palette skinning sample.
---
samples/ApiDemos/AndroidManifest.xml | 9 +
.../apis/graphics/MatrixPaletteActivity.java | 52 +++
.../apis/graphics/MatrixPaletteRenderer.java | 408 ++++++++++++++++++
3 files changed, 469 insertions(+)
create mode 100644 samples/ApiDemos/src/com/example/android/apis/graphics/MatrixPaletteActivity.java
create mode 100644 samples/ApiDemos/src/com/example/android/apis/graphics/MatrixPaletteRenderer.java
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index c96417889..4dbf13683 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -1426,6 +1426,15 @@
+
+
+
+
+
+
+
= 65536) {
+ throw new IllegalArgumentException("w");
+ }
+ if (h < 0 || h >= 65536) {
+ throw new IllegalArgumentException("h");
+ }
+ if (w * h >= 65536) {
+ throw new IllegalArgumentException("w * h >= 65536");
+ }
+
+ mW = w;
+ mH = h;
+ int size = w * h;
+
+ mVertexByteBuffer = ByteBuffer.allocateDirect(VERTEX_SIZE * size)
+ .order(ByteOrder.nativeOrder());
+ mVertexBuffer = mVertexByteBuffer.asFloatBuffer();
+
+ int quadW = mW - 1;
+ int quadH = mH - 1;
+ int quadCount = quadW * quadH;
+ int indexCount = quadCount * 6;
+ mIndexCount = indexCount;
+ mIndexBuffer = ByteBuffer.allocateDirect(CHAR_SIZE * indexCount)
+ .order(ByteOrder.nativeOrder()).asCharBuffer();
+
+ /*
+ * Initialize triangle list mesh.
+ *
+ * [0]-----[ 1] ...
+ * | / |
+ * | / |
+ * | / |
+ * [w]-----[w+1] ...
+ * | |
+ *
+ */
+
+ {
+ int i = 0;
+ for (int y = 0; y < quadH; y++) {
+ for (int x = 0; x < quadW; x++) {
+ char a = (char) (y * mW + x);
+ char b = (char) (y * mW + x + 1);
+ char c = (char) ((y + 1) * mW + x);
+ char d = (char) ((y + 1) * mW + x + 1);
+
+ mIndexBuffer.put(i++, a);
+ mIndexBuffer.put(i++, c);
+ mIndexBuffer.put(i++, b);
+
+ mIndexBuffer.put(i++, b);
+ mIndexBuffer.put(i++, c);
+ mIndexBuffer.put(i++, d);
+ }
+ }
+ }
+
+ }
+
+ public void set(int i, int j, float x, float y, float z,
+ float u, float v,
+ float w0, float w1,
+ int p0, int p1) {
+ if (i < 0 || i >= mW) {
+ throw new IllegalArgumentException("i");
+ }
+ if (j < 0 || j >= mH) {
+ throw new IllegalArgumentException("j");
+ }
+
+ if (w0 + w1 != 1.0f) {
+ throw new IllegalArgumentException("Weights must add up to 1.0f");
+ }
+
+ int index = mW * j + i;
+
+ mVertexBuffer.position(index * VERTEX_SIZE / FLOAT_SIZE);
+ mVertexBuffer.put(x);
+ mVertexBuffer.put(y);
+ mVertexBuffer.put(z);
+ mVertexBuffer.put(u);
+ mVertexBuffer.put(v);
+ mVertexBuffer.put(w0);
+ mVertexBuffer.put(w1);
+
+ mVertexByteBuffer.position(index * VERTEX_SIZE + VERTEX_PALETTE_INDEX_OFFSET);
+ mVertexByteBuffer.put((byte) p0);
+ mVertexByteBuffer.put((byte) p1);
+ }
+
+ public void createBufferObjects(GL gl) {
+ // Generate a the vertex and element buffer IDs
+ int[] vboIds = new int[2];
+ GL11 gl11 = (GL11) gl;
+ gl11.glGenBuffers(2, vboIds, 0);
+ mVertexBufferObjectId = vboIds[0];
+ mElementBufferObjectId = vboIds[1];
+
+ // Upload the vertex data
+ gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertexBufferObjectId);
+ mVertexByteBuffer.position(0);
+ gl11.glBufferData(GL11.GL_ARRAY_BUFFER, mVertexByteBuffer.capacity(), mVertexByteBuffer, GL11.GL_STATIC_DRAW);
+
+ gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mElementBufferObjectId);
+ mIndexBuffer.position(0);
+ gl11.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer.capacity() * CHAR_SIZE, mIndexBuffer, GL11.GL_STATIC_DRAW);
+
+ // We don't need the in-memory data any more
+ mVertexBuffer = null;
+ mVertexByteBuffer = null;
+ mIndexBuffer = null;
+ }
+
+ public void draw(GL10 gl) {
+ GL11 gl11 = (GL11) gl;
+ GL11Ext gl11Ext = (GL11Ext) gl;
+
+ gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+
+ gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertexBufferObjectId);
+ gl11.glVertexPointer(3, GL10.GL_FLOAT, VERTEX_SIZE, 0);
+ gl11.glTexCoordPointer(2, GL10.GL_FLOAT, VERTEX_SIZE, VERTEX_TEXTURE_BUFFER_INDEX_OFFSET * FLOAT_SIZE);
+
+ gl.glEnableClientState(GL11Ext.GL_MATRIX_INDEX_ARRAY_OES);
+ gl.glEnableClientState(GL11Ext.GL_WEIGHT_ARRAY_OES);
+
+ gl11Ext.glWeightPointerOES(2, GL10.GL_FLOAT, VERTEX_SIZE, VERTEX_WEIGHT_BUFFER_INDEX_OFFSET * FLOAT_SIZE);
+ gl11Ext.glMatrixIndexPointerOES(2, GL10.GL_UNSIGNED_BYTE, VERTEX_SIZE, VERTEX_PALETTE_INDEX_OFFSET );
+
+ gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mElementBufferObjectId);
+ gl11.glDrawElements(GL10.GL_TRIANGLES, mIndexCount, GL10.GL_UNSIGNED_SHORT, 0);
+ gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
+ gl.glDisableClientState(GL11Ext.GL_MATRIX_INDEX_ARRAY_OES);
+ gl.glDisableClientState(GL11Ext.GL_WEIGHT_ARRAY_OES);
+ gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
+ gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+ }
+
+ public MatrixPaletteRenderer(Context context) {
+ mContext = context;
+ }
+
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ /*
+ * By default, OpenGL enables features that improve quality
+ * but reduce performance. One might want to tweak that
+ * especially on software renderer.
+ */
+ gl.glDisable(GL10.GL_DITHER);
+
+ /*
+ * Some one-time OpenGL initialization can be made here
+ * probably based on features of this particular context
+ */
+ gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,
+ GL10.GL_FASTEST);
+
+ gl.glClearColor(.5f, .5f, .5f, 1);
+ gl.glShadeModel(GL10.GL_SMOOTH);
+ gl.glEnable(GL10.GL_DEPTH_TEST);
+ gl.glEnable(GL10.GL_TEXTURE_2D);
+
+ /*
+ * Create our texture. This has to be done each time the
+ * surface is created.
+ */
+
+ int[] textures = new int[1];
+ gl.glGenTextures(1, textures, 0);
+
+ mTextureID = textures[0];
+ gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
+
+ gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
+ GL10.GL_NEAREST);
+ gl.glTexParameterf(GL10.GL_TEXTURE_2D,
+ GL10.GL_TEXTURE_MAG_FILTER,
+ GL10.GL_LINEAR);
+
+ gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
+ GL10.GL_CLAMP_TO_EDGE);
+ gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
+ GL10.GL_CLAMP_TO_EDGE);
+
+ gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
+ GL10.GL_REPLACE);
+
+ InputStream is = mContext.getResources()
+ .openRawResource(R.raw.robot);
+ Bitmap bitmap;
+ try {
+ bitmap = BitmapFactory.decodeStream(is);
+ } finally {
+ try {
+ is.close();
+ } catch(IOException e) {
+ // Ignore.
+ }
+ }
+
+ GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
+ bitmap.recycle();
+
+ mGrid = generateWeightedGrid(gl);
+ }
+
+ public void onDrawFrame(GL10 gl) {
+ /*
+ * By default, OpenGL enables features that improve quality
+ * but reduce performance. One might want to tweak that
+ * especially on software renderer.
+ */
+ gl.glDisable(GL10.GL_DITHER);
+
+ gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
+ GL10.GL_MODULATE);
+
+ /*
+ * Usually, the first thing one might want to do is to clear
+ * the screen. The most efficient way of doing this is to use
+ * glClear().
+ */
+
+ gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+
+ gl.glEnable(GL10.GL_DEPTH_TEST);
+
+ gl.glEnable(GL10.GL_CULL_FACE);
+
+ /*
+ * Now we're ready to draw some 3D objects
+ */
+
+ gl.glMatrixMode(GL10.GL_MODELVIEW);
+ gl.glLoadIdentity();
+
+ GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
+
+ gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+ gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
+
+ gl.glActiveTexture(GL10.GL_TEXTURE0);
+ gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
+ gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
+ GL10.GL_REPEAT);
+ gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
+ GL10.GL_REPEAT);
+
+ long time = SystemClock.uptimeMillis() % 4000L;
+
+ // Rock back and forth
+ double animationUnit = ((double) time) / 4000;
+ float unitAngle = (float) Math.cos(animationUnit * 2 * Math.PI);
+ float angle = unitAngle * 135f;
+
+ gl.glEnable(GL11Ext.GL_MATRIX_PALETTE_OES);
+ gl.glMatrixMode(GL11Ext.GL_MATRIX_PALETTE_OES);
+
+ GL11Ext gl11Ext = (GL11Ext) gl;
+
+ // matrix 0: no transformation
+ gl11Ext.glCurrentPaletteMatrixOES(0);
+ gl11Ext.glLoadPaletteFromModelViewMatrixOES();
+
+
+ // matrix 1: rotate by "angle"
+ gl.glRotatef(angle, 0, 0, 1.0f);
+
+ gl11Ext.glCurrentPaletteMatrixOES(1);
+ gl11Ext.glLoadPaletteFromModelViewMatrixOES();
+
+ mGrid.draw(gl);
+
+ gl.glDisable(GL11Ext.GL_MATRIX_PALETTE_OES);
+ }
+
+ public void onSurfaceChanged(GL10 gl, int w, int h) {
+ gl.glViewport(0, 0, w, h);
+
+ /*
+ * Set our projection matrix. This doesn't have to be done
+ * each time we draw, but usually a new projection needs to
+ * be set when the viewport is resized.
+ */
+
+ float ratio = (float) w / h;
+ gl.glMatrixMode(GL10.GL_PROJECTION);
+ gl.glLoadIdentity();
+ gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7);
+ }
+
+ private Grid generateWeightedGrid(GL gl) {
+ final int uSteps = 20;
+ final int vSteps = 20;
+
+ float radius = 0.25f;
+ float height = 2.0f;
+ Grid grid = new Grid(uSteps + 1, vSteps + 1);
+
+ for (int j = 0; j <= vSteps; j++) {
+ for (int i = 0; i <= uSteps; i++) {
+ double angle = Math.PI * 2 * i / uSteps;
+ float x = radius * (float) Math.cos(angle);
+ float y = height * ((float) j / vSteps - 0.5f);
+ float z = radius * (float) Math.sin(angle);
+ float u = -4.0f * (float) i / uSteps;
+ float v = -4.0f * (float) j / vSteps;
+ float w0 = (float) j / vSteps;
+ float w1 = 1.0f - w0;
+ grid.set(i, j, x, y, z, u, v, w0, w1, 0, 1);
+ }
+ }
+
+ grid.createBufferObjects(gl);
+ return grid;
+ }
+}
From 2c0f89aaf61d62ac2f28ff3e46db6ba27983c6e5 Mon Sep 17 00:00:00 2001
From: Grace Kloba
Date: Wed, 9 Dec 2009 00:13:05 -0800
Subject: [PATCH 07/17] Update SampleBrowserPlugin to show handling long press
and double tap events.
---
.../jni/background/BackgroundPlugin.cpp | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/samples/BrowserPlugin/jni/background/BackgroundPlugin.cpp b/samples/BrowserPlugin/jni/background/BackgroundPlugin.cpp
index f79d9ac14..699fa4da3 100644
--- a/samples/BrowserPlugin/jni/background/BackgroundPlugin.cpp
+++ b/samples/BrowserPlugin/jni/background/BackgroundPlugin.cpp
@@ -72,6 +72,13 @@ BackgroundPlugin::BackgroundPlugin(NPP inst) : SurfaceSubPlugin(inst) {
test_domAccess();
test_javascript();
test_loadJavaClass();
+
+ //register for touch events
+ ANPEventFlags flags = kTouch_ANPEventFlag;
+ NPError err = browser->setvalue(inst, kAcceptEvents_ANPSetValue, &flags);
+ if (err != NPERR_NO_ERROR) {
+ gLogI.log(kError_ANPLogType, "Error selecting input events.");
+ }
}
BackgroundPlugin::~BackgroundPlugin() {
@@ -170,7 +177,12 @@ int16 BackgroundPlugin::handleEvent(const ANPEvent* evt) {
}
break;
case kTouch_ANPEventType:
- gLogI.log(kError_ANPLogType, " ------ %p the plugin did not request touch events", inst());
+ if (kDown_ANPTouchAction == evt->data.touch.action)
+ return kHandleLongPress_ANPTouchResult | kHandleDoubleTap_ANPTouchResult;
+ else if (kLongPress_ANPTouchAction == evt->data.touch.action)
+ browser->geturl(inst(), "javascript:alert('Detected long press event.')", 0);
+ else if (kDoubleTap_ANPTouchAction == evt->data.touch.action)
+ browser->geturl(inst(), "javascript:alert('Detected double tap event.')", 0);
break;
case kKey_ANPEventType:
gLogI.log(kError_ANPLogType, " ------ %p the plugin did not request key events", inst());
From 807dc15b095139d806eab5c9d4de4fbf692ed447 Mon Sep 17 00:00:00 2001
From: Trevor Johns
Date: Mon, 7 Dec 2009 17:04:53 -0800
Subject: [PATCH 08/17] Importing Wikitionary/SimpleWiktionary sample code from
http://code.google.com/p/wiktionary-android/.
Original code by Jeffrey Sharkey .
Modifications:
- Remove .classpath, .project, and default.properties.
- Remove generated files (/gen).
- Create Android.mk and _index.html.
- Cleaned up whitespace and converted to match AOSP style guide.
- Renamed SimpleWiktionary to WiktionarySimple to keep both samples next to each other in the
directory listing.
- Removed the android:text attribute in the BulletPoint due to localization issues.
---
samples/Wiktionary/Android.mk | 16 +
samples/Wiktionary/AndroidManifest.xml | 70 ++++
samples/Wiktionary/_index.html | 10 +
samples/Wiktionary/res/anim/slide_in.xml | 22 ++
samples/Wiktionary/res/anim/slide_out.xml | 22 ++
samples/Wiktionary/res/drawable/app_icon.png | Bin 0 -> 1985 bytes
.../res/drawable/ic_menu_shuffle.png | Bin 0 -> 2384 bytes
.../res/drawable/logo_overlay.9.png | Bin 0 -> 16911 bytes
samples/Wiktionary/res/drawable/lookup_bg.xml | 20 +
.../Wiktionary/res/drawable/progress_spin.xml | 43 +++
samples/Wiktionary/res/drawable/star_logo.png | Bin 0 -> 2747 bytes
samples/Wiktionary/res/drawable/widget_bg.xml | 22 ++
.../res/drawable/widget_bg_normal.9.png | Bin 0 -> 268 bytes
.../res/drawable/widget_bg_pressed.9.png | Bin 0 -> 275 bytes
.../res/drawable/widget_bg_selected.9.png | Bin 0 -> 276 bytes
samples/Wiktionary/res/layout/about.xml | 40 ++
samples/Wiktionary/res/layout/lookup.xml | 56 +++
.../Wiktionary/res/layout/widget_message.xml | 34 ++
samples/Wiktionary/res/layout/widget_word.xml | 79 ++++
samples/Wiktionary/res/menu/lookup.xml | 34 ++
samples/Wiktionary/res/values/strings.xml | 56 +++
samples/Wiktionary/res/values/styles.xml | 66 ++++
samples/Wiktionary/res/values/themes.xml | 21 ++
samples/Wiktionary/res/xml/searchable.xml | 19 +
samples/Wiktionary/res/xml/widget_word.xml | 21 ++
.../wiktionary/ExtendedWikiHelper.java | 278 ++++++++++++++
.../android/wiktionary/LookupActivity.java | 344 ++++++++++++++++++
.../android/wiktionary/SimpleWikiHelper.java | 208 +++++++++++
.../android/wiktionary/WordWidget.java | 129 +++++++
samples/WiktionarySimple/Android.mk | 16 +
samples/WiktionarySimple/AndroidManifest.xml | 41 +++
samples/WiktionarySimple/_index.html | 10 +
.../res/drawable/app_icon.png | Bin 0 -> 1985 bytes
.../res/drawable/star_logo.png | Bin 0 -> 2747 bytes
.../res/drawable/widget_bg.xml | 23 ++
.../res/drawable/widget_bg_normal.9.png | Bin 0 -> 268 bytes
.../res/drawable/widget_bg_pressed.9.png | Bin 0 -> 275 bytes
.../res/drawable/widget_bg_selected.9.png | Bin 0 -> 276 bytes
.../res/layout/widget_message.xml | 34 ++
.../res/layout/widget_word.xml | 79 ++++
.../WiktionarySimple/res/values/strings.xml | 44 +++
.../WiktionarySimple/res/values/styles.xml | 52 +++
.../WiktionarySimple/res/xml/widget_word.xml | 21 ++
.../simplewiktionary/SimpleWikiHelper.java | 214 +++++++++++
.../android/simplewiktionary/WordWidget.java | 127 +++++++
45 files changed, 2271 insertions(+)
create mode 100644 samples/Wiktionary/Android.mk
create mode 100644 samples/Wiktionary/AndroidManifest.xml
create mode 100644 samples/Wiktionary/_index.html
create mode 100644 samples/Wiktionary/res/anim/slide_in.xml
create mode 100644 samples/Wiktionary/res/anim/slide_out.xml
create mode 100644 samples/Wiktionary/res/drawable/app_icon.png
create mode 100755 samples/Wiktionary/res/drawable/ic_menu_shuffle.png
create mode 100644 samples/Wiktionary/res/drawable/logo_overlay.9.png
create mode 100644 samples/Wiktionary/res/drawable/lookup_bg.xml
create mode 100644 samples/Wiktionary/res/drawable/progress_spin.xml
create mode 100644 samples/Wiktionary/res/drawable/star_logo.png
create mode 100644 samples/Wiktionary/res/drawable/widget_bg.xml
create mode 100644 samples/Wiktionary/res/drawable/widget_bg_normal.9.png
create mode 100644 samples/Wiktionary/res/drawable/widget_bg_pressed.9.png
create mode 100644 samples/Wiktionary/res/drawable/widget_bg_selected.9.png
create mode 100644 samples/Wiktionary/res/layout/about.xml
create mode 100644 samples/Wiktionary/res/layout/lookup.xml
create mode 100644 samples/Wiktionary/res/layout/widget_message.xml
create mode 100644 samples/Wiktionary/res/layout/widget_word.xml
create mode 100644 samples/Wiktionary/res/menu/lookup.xml
create mode 100644 samples/Wiktionary/res/values/strings.xml
create mode 100644 samples/Wiktionary/res/values/styles.xml
create mode 100644 samples/Wiktionary/res/values/themes.xml
create mode 100644 samples/Wiktionary/res/xml/searchable.xml
create mode 100644 samples/Wiktionary/res/xml/widget_word.xml
create mode 100644 samples/Wiktionary/src/com/example/android/wiktionary/ExtendedWikiHelper.java
create mode 100644 samples/Wiktionary/src/com/example/android/wiktionary/LookupActivity.java
create mode 100644 samples/Wiktionary/src/com/example/android/wiktionary/SimpleWikiHelper.java
create mode 100644 samples/Wiktionary/src/com/example/android/wiktionary/WordWidget.java
create mode 100644 samples/WiktionarySimple/Android.mk
create mode 100644 samples/WiktionarySimple/AndroidManifest.xml
create mode 100644 samples/WiktionarySimple/_index.html
create mode 100644 samples/WiktionarySimple/res/drawable/app_icon.png
create mode 100644 samples/WiktionarySimple/res/drawable/star_logo.png
create mode 100644 samples/WiktionarySimple/res/drawable/widget_bg.xml
create mode 100644 samples/WiktionarySimple/res/drawable/widget_bg_normal.9.png
create mode 100644 samples/WiktionarySimple/res/drawable/widget_bg_pressed.9.png
create mode 100644 samples/WiktionarySimple/res/drawable/widget_bg_selected.9.png
create mode 100644 samples/WiktionarySimple/res/layout/widget_message.xml
create mode 100644 samples/WiktionarySimple/res/layout/widget_word.xml
create mode 100644 samples/WiktionarySimple/res/values/strings.xml
create mode 100644 samples/WiktionarySimple/res/values/styles.xml
create mode 100644 samples/WiktionarySimple/res/xml/widget_word.xml
create mode 100644 samples/WiktionarySimple/src/com/example/android/simplewiktionary/SimpleWikiHelper.java
create mode 100644 samples/WiktionarySimple/src/com/example/android/simplewiktionary/WordWidget.java
diff --git a/samples/Wiktionary/Android.mk b/samples/Wiktionary/Android.mk
new file mode 100644
index 000000000..d6ce1f16c
--- /dev/null
+++ b/samples/Wiktionary/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := Wiktionary
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/Wiktionary/AndroidManifest.xml b/samples/Wiktionary/AndroidManifest.xml
new file mode 100644
index 000000000..1641a8b0a
--- /dev/null
+++ b/samples/Wiktionary/AndroidManifest.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/Wiktionary/_index.html b/samples/Wiktionary/_index.html
new file mode 100644
index 000000000..eb7cb96bb
--- /dev/null
+++ b/samples/Wiktionary/_index.html
@@ -0,0 +1,10 @@
+
A sample application that demonstrates how to create an interactive widget
+for display on the Android home screen.
+
+
When installed, this adds a "Wiktionary" option to the widget installation
+menu. The word of the day is downloaded from Wiktionary and displayed in a
+frame. Touching the widget will open a custom WebView to render the
+definition.
",
+ Pattern.MULTILINE));
+
+ // Add internal links
+ sFormatRules.add(new FormatRule("\\[\\[([^:\\|\\]]+)\\]\\]",
+ String.format("$1", WIKI_AUTHORITY, WIKI_LOOKUP_HOST)));
+ sFormatRules.add(new FormatRule("\\[\\[([^:\\|\\]]+)\\|([^\\]]+)\\]\\]",
+ String.format("$2", WIKI_AUTHORITY, WIKI_LOOKUP_HOST)));
+
+ // Add bold and italic formatting
+ sFormatRules.add(new FormatRule("'''(.+?)'''", "$1"));
+ sFormatRules.add(new FormatRule("([^'])''([^'].*?[^'])''([^'])", "$1$2$3"));
+
+ // Remove odd category links and convert remaining links into flat text
+ sFormatRules.add(new FormatRule("(\\{+.+?\\}+|\\[\\[[^:]+:[^\\\\|\\]]+\\]\\]|" +
+ "\\[http.+?\\]|\\[\\[Category:.+?\\]\\])", "", Pattern.MULTILINE | Pattern.DOTALL));
+ sFormatRules.add(new FormatRule("\\[\\[([^\\|\\]]+\\|)?(.+?)\\]\\]", "$2",
+ Pattern.MULTILINE));
+
+ }
+
+ /**
+ * Query the Wiktionary API to pick a random dictionary word. Will try
+ * multiple times to find a valid word before giving up.
+ *
+ * @return Random dictionary word, or null if no valid word was found.
+ * @throws ApiException If any connection or server error occurs.
+ * @throws ParseException If there are problems parsing the response.
+ */
+ public static String getRandomWord() throws ApiException, ParseException {
+ // Keep trying a few times until we find a valid word
+ int tries = 0;
+ while (tries++ < RANDOM_TRIES) {
+ // Query the API for a random word
+ String content = getUrlContent(WIKTIONARY_RANDOM);
+ try {
+ // Drill into the JSON response to find the returned word
+ JSONObject response = new JSONObject(content);
+ JSONObject query = response.getJSONObject("query");
+ JSONArray random = query.getJSONArray("random");
+ JSONObject word = random.getJSONObject(0);
+ String foundWord = word.getString("title");
+
+ // If we found an actual word, and it wasn't rejected by our invalid
+ // filter, then accept and return it.
+ if (foundWord != null &&
+ !sInvalidWord.matcher(foundWord).find()) {
+ return foundWord;
+ }
+ } catch (JSONException e) {
+ throw new ParseException("Problem parsing API response", e);
+ }
+ }
+
+ // No valid word found in number of tries, so return null
+ return null;
+ }
+
+ /**
+ * Format the given wiki-style text into formatted HTML content. This will
+ * create headers, lists, internal links, and style formatting for any wiki
+ * markup found.
+ *
+ * @param wikiText The raw text to format, with wiki-markup included.
+ * @return HTML formatted content, ready for display in {@link WebView}.
+ */
+ public static String formatWikiText(String wikiText) {
+ if (wikiText == null) {
+ return null;
+ }
+
+ // Insert a fake last section into the document so our section splitter
+ // can correctly catch the last section.
+ wikiText = wikiText.concat(STUB_SECTION);
+
+ // Read through all sections, keeping only those matching our filter,
+ // and only including the first entry for each title.
+ HashSet foundSections = new HashSet();
+ StringBuilder builder = new StringBuilder();
+
+ Matcher sectionMatcher = sSectionSplit.matcher(wikiText);
+ while (sectionMatcher.find()) {
+ String title = sectionMatcher.group(1);
+ if (!foundSections.contains(title) &&
+ sValidSections.matcher(title).matches()) {
+ String sectionContent = sectionMatcher.group();
+ foundSections.add(title);
+ builder.append(sectionContent);
+ }
+ }
+
+ // Our new wiki text is the selected sections only
+ wikiText = builder.toString();
+
+ // Apply all formatting rules, in order, to the wiki text
+ for (FormatRule rule : sFormatRules) {
+ wikiText = rule.apply(wikiText);
+ }
+
+ // Return the resulting HTML with style sheet, if we have content left
+ if (!TextUtils.isEmpty(wikiText)) {
+ return STYLE_SHEET + wikiText;
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/samples/Wiktionary/src/com/example/android/wiktionary/LookupActivity.java b/samples/Wiktionary/src/com/example/android/wiktionary/LookupActivity.java
new file mode 100644
index 000000000..6cc231b8c
--- /dev/null
+++ b/samples/Wiktionary/src/com/example/android/wiktionary/LookupActivity.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2009 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.wiktionary;
+
+import com.example.android.wiktionary.SimpleWikiHelper.ApiException;
+import com.example.android.wiktionary.SimpleWikiHelper.ParseException;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.SearchManager;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Animation.AnimationListener;
+import android.webkit.WebView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import java.util.Stack;
+
+/**
+ * Activity that lets users browse through Wiktionary content. This is just the
+ * user interface, and all API communication and parsing is handled in
+ * {@link ExtendedWikiHelper}.
+ */
+public class LookupActivity extends Activity implements AnimationListener {
+ private static final String TAG = "LookupActivity";
+
+ private View mTitleBar;
+ private TextView mTitle;
+ private ProgressBar mProgress;
+ private WebView mWebView;
+
+ private Animation mSlideIn;
+ private Animation mSlideOut;
+
+ /**
+ * History stack of previous words browsed in this session. This is
+ * referenced when the user taps the "back" key, to possibly intercept and
+ * show the last-visited entry, instead of closing the activity.
+ */
+ private Stack mHistory = new Stack();
+
+ private String mEntryTitle;
+
+ /**
+ * Keep track of last time user tapped "back" hard key. When pressed more
+ * than once within {@link #BACK_THRESHOLD}, we treat let the back key fall
+ * through and close the app.
+ */
+ private long mLastPress = -1;
+
+ private static final long BACK_THRESHOLD = DateUtils.SECOND_IN_MILLIS / 2;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.lookup);
+
+ // Load animations used to show/hide progress bar
+ mSlideIn = AnimationUtils.loadAnimation(this, R.anim.slide_in);
+ mSlideOut = AnimationUtils.loadAnimation(this, R.anim.slide_out);
+
+ // Listen for the "in" animation so we make the progress bar visible
+ // only after the sliding has finished.
+ mSlideIn.setAnimationListener(this);
+
+ mTitleBar = findViewById(R.id.title_bar);
+ mTitle = (TextView) findViewById(R.id.title);
+ mProgress = (ProgressBar) findViewById(R.id.progress);
+ mWebView = (WebView) findViewById(R.id.webview);
+
+ // Make the view transparent to show background
+ mWebView.setBackgroundColor(0);
+
+ // Prepare User-Agent string for wiki actions
+ ExtendedWikiHelper.prepareUserAgent(this);
+
+ // Handle incoming intents as possible searches or links
+ onNewIntent(getIntent());
+ }
+
+ /**
+ * Intercept the back-key to try walking backwards along our word history
+ * stack. If we don't have any remaining history, the key behaves normally
+ * and closes this activity.
+ */
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ // Handle back key as long we have a history stack
+ if (keyCode == KeyEvent.KEYCODE_BACK && !mHistory.empty()) {
+
+ // Compare against last pressed time, and if user hit multiple times
+ // in quick succession, we should consider bailing out early.
+ long currentPress = SystemClock.uptimeMillis();
+ if (currentPress - mLastPress < BACK_THRESHOLD) {
+ return super.onKeyDown(keyCode, event);
+ }
+ mLastPress = currentPress;
+
+ // Pop last entry off stack and start loading
+ String lastEntry = mHistory.pop();
+ startNavigating(lastEntry, false);
+
+ return true;
+ }
+
+ // Otherwise fall through to parent
+ return super.onKeyDown(keyCode, event);
+ }
+
+ /**
+ * Start navigating to the given word, pushing any current word onto the
+ * history stack if requested. The navigation happens on a background thread
+ * and updates the GUI when finished.
+ *
+ * @param word The dictionary word to navigate to.
+ * @param pushHistory If true, push the current word onto history stack.
+ */
+ private void startNavigating(String word, boolean pushHistory) {
+ // Push any current word onto the history stack
+ if (!TextUtils.isEmpty(mEntryTitle) && pushHistory) {
+ mHistory.add(mEntryTitle);
+ }
+
+ // Start lookup for new word in background
+ new LookupTask().execute(word);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.lookup, menu);
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.lookup_search: {
+ onSearchRequested();
+ return true;
+ }
+ case R.id.lookup_random: {
+ startNavigating(null, true);
+ return true;
+ }
+ case R.id.lookup_about: {
+ showAbout();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Show an about dialog that cites data sources.
+ */
+ protected void showAbout() {
+ // Inflate the about message contents
+ View messageView = getLayoutInflater().inflate(R.layout.about, null, false);
+
+ // When linking text, force to always use default color. This works
+ // around a pressed color state bug.
+ TextView textView = (TextView) messageView.findViewById(R.id.about_credits);
+ int defaultColor = textView.getTextColors().getDefaultColor();
+ textView.setTextColor(defaultColor);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setIcon(R.drawable.app_icon);
+ builder.setTitle(R.string.app_name);
+ builder.setView(messageView);
+ builder.create();
+ builder.show();
+ }
+
+ /**
+ * Because we're singleTop, we handle our own new intents. These usually
+ * come from the {@link SearchManager} when a search is requested, or from
+ * internal links the user clicks on.
+ */
+ @Override
+ public void onNewIntent(Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_SEARCH.equals(action)) {
+ // Start query for incoming search request
+ String query = intent.getStringExtra(SearchManager.QUERY);
+ startNavigating(query, true);
+
+ } else if (Intent.ACTION_VIEW.equals(action)) {
+ // Treat as internal link only if valid Uri and host matches
+ Uri data = intent.getData();
+ if (data != null && ExtendedWikiHelper.WIKI_LOOKUP_HOST
+ .equals(data.getHost())) {
+ String query = data.getPathSegments().get(0);
+ startNavigating(query, true);
+ }
+
+ } else {
+ // If not recognized, then start showing random word
+ startNavigating(null, true);
+ }
+ }
+
+ /**
+ * Set the title for the current entry.
+ */
+ protected void setEntryTitle(String entryText) {
+ mEntryTitle = entryText;
+ mTitle.setText(mEntryTitle);
+ }
+
+ /**
+ * Set the content for the current entry. This will update our
+ * {@link WebView} to show the requested content.
+ */
+ protected void setEntryContent(String entryContent) {
+ mWebView.loadDataWithBaseURL(ExtendedWikiHelper.WIKI_AUTHORITY, entryContent,
+ ExtendedWikiHelper.MIME_TYPE, ExtendedWikiHelper.ENCODING, null);
+ }
+
+ /**
+ * Background task to handle Wiktionary lookups. This correctly shows and
+ * hides the loading animation from the GUI thread before starting a
+ * background query to the Wiktionary API. When finished, it transitions
+ * back to the GUI thread where it updates with the newly-found entry.
+ */
+ private class LookupTask extends AsyncTask {
+ /**
+ * Before jumping into background thread, start sliding in the
+ * {@link ProgressBar}. We'll only show it once the animation finishes.
+ */
+ @Override
+ protected void onPreExecute() {
+ mTitleBar.startAnimation(mSlideIn);
+ }
+
+ /**
+ * Perform the background query using {@link ExtendedWikiHelper}, which
+ * may return an error message as the result.
+ */
+ @Override
+ protected String doInBackground(String... args) {
+ String query = args[0];
+ String parsedText = null;
+
+ try {
+ // If query word is null, assume request for random word
+ if (query == null) {
+ query = ExtendedWikiHelper.getRandomWord();
+ }
+
+ if (query != null) {
+ // Push our requested word to the title bar
+ publishProgress(query);
+ String wikiText = ExtendedWikiHelper.getPageContent(query, true);
+ parsedText = ExtendedWikiHelper.formatWikiText(wikiText);
+ }
+ } catch (ApiException e) {
+ Log.e(TAG, "Problem making wiktionary request", e);
+ } catch (ParseException e) {
+ Log.e(TAG, "Problem making wiktionary request", e);
+ }
+
+ if (parsedText == null) {
+ parsedText = getString(R.string.empty_result);
+ }
+
+ return parsedText;
+ }
+
+ /**
+ * Our progress update pushes a title bar update.
+ */
+ @Override
+ protected void onProgressUpdate(String... args) {
+ String searchWord = args[0];
+ setEntryTitle(searchWord);
+ }
+
+ /**
+ * When finished, push the newly-found entry content into our
+ * {@link WebView} and hide the {@link ProgressBar}.
+ */
+ @Override
+ protected void onPostExecute(String parsedText) {
+ mTitleBar.startAnimation(mSlideOut);
+ mProgress.setVisibility(View.INVISIBLE);
+
+ setEntryContent(parsedText);
+ }
+ }
+
+ /**
+ * Make the {@link ProgressBar} visible when our in-animation finishes.
+ */
+ public void onAnimationEnd(Animation animation) {
+ mProgress.setVisibility(View.VISIBLE);
+ }
+
+ public void onAnimationRepeat(Animation animation) {
+ // Not interested if the animation repeats
+ }
+
+ public void onAnimationStart(Animation animation) {
+ // Not interested when the animation starts
+ }
+}
diff --git a/samples/Wiktionary/src/com/example/android/wiktionary/SimpleWikiHelper.java b/samples/Wiktionary/src/com/example/android/wiktionary/SimpleWikiHelper.java
new file mode 100644
index 000000000..1c71d7e8b
--- /dev/null
+++ b/samples/Wiktionary/src/com/example/android/wiktionary/SimpleWikiHelper.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2009 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.wiktionary;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Helper methods to simplify talking with and parsing responses from a
+ * lightweight Wiktionary API. Before making any requests, you should call
+ * {@link #prepareUserAgent(Context)} to generate a User-Agent string based on
+ * your application package name and version.
+ */
+public class SimpleWikiHelper {
+ private static final String TAG = "SimpleWikiHelper";
+
+ /**
+ * Partial URL to use when requesting the detailed entry for a specific
+ * Wiktionary page. Use {@link String#format(String, Object...)} to insert
+ * the desired page title after escaping it as needed.
+ */
+ private static final String WIKTIONARY_PAGE =
+ "http://en.wiktionary.org/w/api.php?action=query&prop=revisions&titles=%s&" +
+ "rvprop=content&format=json%s";
+
+ /**
+ * Partial URL to append to {@link #WIKTIONARY_PAGE} when you want to expand
+ * any templates found on the requested page. This is useful when browsing
+ * full entries, but may use more network bandwidth.
+ */
+ private static final String WIKTIONARY_EXPAND_TEMPLATES =
+ "&rvexpandtemplates=true";
+
+ /**
+ * {@link StatusLine} HTTP status code when no server error has occurred.
+ */
+ private static final int HTTP_STATUS_OK = 200;
+
+ /**
+ * Shared buffer used by {@link #getUrlContent(String)} when reading results
+ * from an API request.
+ */
+ private static byte[] sBuffer = new byte[512];
+
+ /**
+ * User-agent string to use when making requests. Should be filled using
+ * {@link #prepareUserAgent(Context)} before making any other calls.
+ */
+ private static String sUserAgent = null;
+
+ /**
+ * Thrown when there were problems contacting the remote API server, either
+ * because of a network error, or the server returned a bad status code.
+ */
+ public static class ApiException extends Exception {
+ public ApiException(String detailMessage, Throwable throwable) {
+ super(detailMessage, throwable);
+ }
+
+ public ApiException(String detailMessage) {
+ super(detailMessage);
+ }
+ }
+
+ /**
+ * Thrown when there were problems parsing the response to an API call,
+ * either because the response was empty, or it was malformed.
+ */
+ public static class ParseException extends Exception {
+ public ParseException(String detailMessage, Throwable throwable) {
+ super(detailMessage, throwable);
+ }
+ }
+
+ /**
+ * Prepare the internal User-Agent string for use. This requires a
+ * {@link Context} to pull the package name and version number for this
+ * application.
+ */
+ public static void prepareUserAgent(Context context) {
+ try {
+ // Read package name and version number from manifest
+ PackageManager manager = context.getPackageManager();
+ PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
+ sUserAgent = String.format(context.getString(R.string.template_user_agent),
+ info.packageName, info.versionName);
+
+ } catch(NameNotFoundException e) {
+ Log.e(TAG, "Couldn't find package information in PackageManager", e);
+ }
+ }
+
+ /**
+ * Read and return the content for a specific Wiktionary page. This makes a
+ * lightweight API call, and trims out just the page content returned.
+ * Because this call blocks until results are available, it should not be
+ * run from a UI thread.
+ *
+ * @param title The exact title of the Wiktionary page requested.
+ * @param expandTemplates If true, expand any wiki templates found.
+ * @return Exact content of page.
+ * @throws ApiException If any connection or server error occurs.
+ * @throws ParseException If there are problems parsing the response.
+ */
+ public static String getPageContent(String title, boolean expandTemplates)
+ throws ApiException, ParseException {
+ // Encode page title and expand templates if requested
+ String encodedTitle = Uri.encode(title);
+ String expandClause = expandTemplates ? WIKTIONARY_EXPAND_TEMPLATES : "";
+
+ // Query the API for content
+ String content = getUrlContent(String.format(WIKTIONARY_PAGE,
+ encodedTitle, expandClause));
+ try {
+ // Drill into the JSON response to find the content body
+ JSONObject response = new JSONObject(content);
+ JSONObject query = response.getJSONObject("query");
+ JSONObject pages = query.getJSONObject("pages");
+ JSONObject page = pages.getJSONObject((String) pages.keys().next());
+ JSONArray revisions = page.getJSONArray("revisions");
+ JSONObject revision = revisions.getJSONObject(0);
+ return revision.getString("*");
+ } catch (JSONException e) {
+ throw new ParseException("Problem parsing API response", e);
+ }
+ }
+
+ /**
+ * Pull the raw text content of the given URL. This call blocks until the
+ * operation has completed, and is synchronized because it uses a shared
+ * buffer {@link #sBuffer}.
+ *
+ * @param url The exact URL to request.
+ * @return The raw content returned by the server.
+ * @throws ApiException If any connection or server error occurs.
+ */
+ protected static synchronized String getUrlContent(String url) throws ApiException {
+ if (sUserAgent == null) {
+ throw new ApiException("User-Agent string must be prepared");
+ }
+
+ // Create client and set our specific user-agent string
+ HttpClient client = new DefaultHttpClient();
+ HttpGet request = new HttpGet(url);
+ request.setHeader("User-Agent", sUserAgent);
+
+ try {
+ HttpResponse response = client.execute(request);
+
+ // Check if server response is valid
+ StatusLine status = response.getStatusLine();
+ if (status.getStatusCode() != HTTP_STATUS_OK) {
+ throw new ApiException("Invalid response from server: " +
+ status.toString());
+ }
+
+ // Pull content stream from response
+ HttpEntity entity = response.getEntity();
+ InputStream inputStream = entity.getContent();
+
+ ByteArrayOutputStream content = new ByteArrayOutputStream();
+
+ // Read response into a buffered stream
+ int readBytes = 0;
+ while ((readBytes = inputStream.read(sBuffer)) != -1) {
+ content.write(sBuffer, 0, readBytes);
+ }
+
+ // Return result from buffered stream
+ return new String(content.toByteArray());
+ } catch (IOException e) {
+ throw new ApiException("Problem communicating with API", e);
+ }
+ }
+
+}
diff --git a/samples/Wiktionary/src/com/example/android/wiktionary/WordWidget.java b/samples/Wiktionary/src/com/example/android/wiktionary/WordWidget.java
new file mode 100644
index 000000000..e80eaf92a
--- /dev/null
+++ b/samples/Wiktionary/src/com/example/android/wiktionary/WordWidget.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2009 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.wiktionary;
+
+import com.example.android.wiktionary.SimpleWikiHelper.ApiException;
+import com.example.android.wiktionary.SimpleWikiHelper.ParseException;
+
+import android.app.PendingIntent;
+import android.app.Service;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.IBinder;
+import android.text.format.Time;
+import android.util.Log;
+import android.widget.RemoteViews;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Define a simple widget that shows the Wiktionary "Word of the day." To build
+ * an update we spawn a background {@link Service} to perform the API queries.
+ */
+public class WordWidget extends AppWidgetProvider {
+ @Override
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+ // To prevent any ANR timeouts, we perform the update in a service
+ context.startService(new Intent(context, UpdateService.class));
+ }
+
+ public static class UpdateService extends Service {
+ @Override
+ public void onStart(Intent intent, int startId) {
+ // Build the widget update for today
+ RemoteViews updateViews = buildUpdate(this);
+
+ // Push update for this widget to the home screen
+ ComponentName thisWidget = new ComponentName(this, WordWidget.class);
+ AppWidgetManager manager = AppWidgetManager.getInstance(this);
+ manager.updateAppWidget(thisWidget, updateViews);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ /**
+ * Regular expression that splits "Word of the day" entry into word
+ * name, word type, and the first description bullet point.
+ */
+ private static final String WOTD_PATTERN =
+ "(?s)\\{\\{wotd\\|(.+?)\\|(.+?)\\|([^#\\|]+).*?\\}\\}";
+
+ /**
+ * Build a widget update to show the current Wiktionary
+ * "Word of the day." Will block until the online API returns.
+ */
+ public RemoteViews buildUpdate(Context context) {
+ // Pick out month names from resources
+ Resources res = context.getResources();
+ String[] monthNames = res.getStringArray(R.array.month_names);
+
+ // Find current month and day
+ Time today = new Time();
+ today.setToNow();
+
+ // Build the page title for today, such as "March 21"
+ String pageName = res.getString(R.string.template_wotd_title,
+ monthNames[today.month], today.monthDay);
+ String pageContent = null;
+
+ try {
+ // Try querying the Wiktionary API for today's word
+ SimpleWikiHelper.prepareUserAgent(context);
+ pageContent = SimpleWikiHelper.getPageContent(pageName, false);
+ } catch (ApiException e) {
+ Log.e("WordWidget", "Couldn't contact API", e);
+ } catch (ParseException e) {
+ Log.e("WordWidget", "Couldn't parse API response", e);
+ }
+
+ RemoteViews views = null;
+ Matcher matcher = Pattern.compile(WOTD_PATTERN).matcher(pageContent);
+ if (matcher.find()) {
+ // Build an update that holds the updated widget contents
+ views = new RemoteViews(context.getPackageName(), R.layout.widget_word);
+
+ String wordTitle = matcher.group(1);
+ views.setTextViewText(R.id.word_title, wordTitle);
+ views.setTextViewText(R.id.word_type, matcher.group(2));
+ views.setTextViewText(R.id.definition, matcher.group(3).trim());
+
+ // When user clicks on widget, launch to Wiktionary definition page
+ String definePage = String.format("%s://%s/%s", ExtendedWikiHelper.WIKI_AUTHORITY,
+ ExtendedWikiHelper.WIKI_LOOKUP_HOST, wordTitle);
+ Intent defineIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(definePage));
+ PendingIntent pendingIntent = PendingIntent.getActivity(context,
+ 0 /* no requestCode */, defineIntent, 0 /* no flags */);
+ views.setOnClickPendingIntent(R.id.widget, pendingIntent);
+
+ } else {
+ // Didn't find word of day, so show error message
+ views = new RemoteViews(context.getPackageName(), R.layout.widget_message);
+ views.setTextViewText(R.id.message, context.getString(R.string.widget_error));
+ }
+ return views;
+ }
+ }
+}
diff --git a/samples/WiktionarySimple/Android.mk b/samples/WiktionarySimple/Android.mk
new file mode 100644
index 000000000..a5a1423e0
--- /dev/null
+++ b/samples/WiktionarySimple/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := WiktionarySimple
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/WiktionarySimple/AndroidManifest.xml b/samples/WiktionarySimple/AndroidManifest.xml
new file mode 100644
index 000000000..c6b872486
--- /dev/null
+++ b/samples/WiktionarySimple/AndroidManifest.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/WiktionarySimple/_index.html b/samples/WiktionarySimple/_index.html
new file mode 100644
index 000000000..cf8142e89
--- /dev/null
+++ b/samples/WiktionarySimple/_index.html
@@ -0,0 +1,10 @@
+
A sample application that demonstrates how to create an interactive widget for display on the Android home screen.
+
+
When installed, this adds a "Wiktionary simple" option to the widget
+installation menu. The word of the day is downloaded from Wiktionary and
+displayed in a frame. Touching the widget will open a new browser session and
+load the word's Wiktionary entry.
+
+
A more advanced version of this sample is available in the Wiktionary directory.
ftGJW}NT+??iN)p7baw)}
zp`vqYWMI1i5n;2LbN$8|)`lMeLLiz%z*<8ii9!%Kefk2w`Td(byfnj;k1w$}pWUqo
z+_Rj2_e0)Zxyt3sYpmW_%ZKjH>#P1Q6SZlbF>TH~HNk{?0@D>pb`L$HzXX
zyBl*iveTSPAFT80YySpdV`Ed-*Vo=&y>#X$fTdiw1{g^|j8G2)%R4IzF
z)>>>7b~_W@iM^P(oq&4vYpXYIvbe8o2An>7i7$Tck$YC=UgkXWB0{tXL6S#8zPgk><#c%)e*RTET(~o^LN|Pwq
zY_@xVG9zp6_@Kpm1r^P*MK(6B=0LN*tQjDns$N9$fBo$juP+{X;!A~V{U`|Hrx5YR
zm;&|E7r_97_V??`eQ(ii1LbLV0hVK180me{+c@uHML;QB^OcQ_tLs3k^Vyocy+{)g
z3sixWGIfWCjy<&$MAf>8YEg(Hg^GAz6rxb5c&{K@NTDDqMd3vR=L$s%FNjh&EyOE1
zia2$M;tDU~6u`N{mo#(DbLT}wRomk3Y&Ki1^-ap`yRNG3i3B<%A|UA!7y?XBgwp48
z;Et+p4gtoM$s!U`60l6QvA(53=U4zORc(y~O{()d|FsdYI|QIyDn+;M872P%4!Y{-
TZnFlg00000NkvXXu0mjfX5y*S
literal 0
HcmV?d00001
diff --git a/samples/WiktionarySimple/res/drawable/star_logo.png b/samples/WiktionarySimple/res/drawable/star_logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..b32d1756ccde3f84a26d989b45a8af70a7b60d04
GIT binary patch
literal 2747
zcmV;s3PkmZP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXH1
z2q6+}11H7+017)vL_t(o!_}C3a8%VD$G_*CyL)%|Jprs?zx})yWijU_d5r;fLGI|HIth*ts(dyT=YUM)Dmqk
zmc;+CBdF(+0?%-D180FdC{h)d4EVulo0iDat1O{aD9!(~O9tE$stiOfmWm+lhUUEg
z&d+_91bEV-Wn7T?lrp$doAHrleC3h=*QmPPw|18^D=i-ZoK)XX{LL6dUkHyxUv7dgEdj;M<4sujopV1R8Fm`
z!nJh^7$F3Nka+~;a{weEBe79RLDMv7ng-jp`FroZw;>*nuWW8^PW)fMODbya4tNIq
zy4M3JARG=u2tjSzHV7eLj6nziDJ6=EicnNk1OP}R61RrK;q?G^ooW45t5%g(RaMO{
zC@7e8{P=OFr>Cc_x3{-zLg0t{`a*|#;+4CjuTrnagN}|4EL*k=wr#_49C*E62q8uy
zq-hXBKnQ_gFo>$EsuE4pri}-_V#NwJZ{ECXtE;PTD=RCTbMoZL>3w~Dtt~Ar?{s!{
zW{(^>5}OcsI%nSC)qQo-=6wlm|9S^q$B(JSix-njCWA~SgHR|0pU(%^b&<>Ez&S@S
z7=)&2C@wDchr{970DJ%}0BCG%H2VAdCof#M@QJBYr!EQv0)h7S_Slv!TV8BxYWhb{
zPtRc~WdeXWZ}5ZN2U%}I&T=IwiTe+L`Pq6
zLK^2nk5Ec(cIAkr>V5;0tIA|w{|Q!6UV%&|1I8FArQn>y4;MXgTW90
zF94L5madsTefo07m?ot}G#c&Nym|9;O-)U20_XzZoR*vqth{fIgYeq4TU0BkP9-!Y
zCB)gl|Iqj@z3~fkBjIr2={-v>3(h&Dl&2n|X>eT^UauF!!^5DI!Y~ZjihORGj#VHgq1vY4*x=<7Q^(768jhNh-XuiLiW34EGIbBFe{q-E`+-eCSAp_kpE^e_YS
zDZxU-9SO$xWgfu(XiUw&LRSI5j{ty@idfu6DwRSqnFQw?hG9S{1R8NPdX;4amF$P`N!5D*5
zYD^|dDGU$CR9U2mcr^{BrKMnuiRR|!rstk*c)>KyqX77S3mhB$F|+o{3}teMu!i4P
z#5+w&RB_I!uv5gxV@gL>5kd)wQiKqqKmiaWvYdwejE6%8Q_i5wr!urWG6{)90)!A)
zmIc>!VVWi!$AN9z;GBaohT@_Kk**8B-;ch&-i{AH-2KK=Pd&B1zrTMs0Q*c*9RqJa
zas;jW52&^Shr4QLPTfv4RzN7b3`Emi3BnzRP$vaVq(Gz%4{<;!prDa>+G3HAPkKpG
z4xZ?z<&jCmbzLM9r;4KMx{y-Bah$On&~+WUt}D*@(Z#^M(LGDnJNH6t{@tIRYmOU8X@`tDG&ApTy;fV=*?fs+x=z
zn^9X-N{uu}gp8Jb3=Iv5;o;%tb?eqWy<^9Y?XK%~128X0lXb`UmsJwI^cmudTo0-R
zPrI1_exFYj78W8>ph;m4NH4eP+*u2lX_^p1fH5}OSi$4*jA_5Ow-=E}B)59?>XjWG
z9UB0|0H|}dV|KO)t^1E;F2ACB8zD4K2(1J47?coHl$W|SH8U||#!SqfRg0kCMzs4Q
zl1rJSvUC#3=kst~7fPuyR}n&hbB^KRVGu%4SXd~#y1L%)?(W`oHcg*bleMw&^?@aK
zFR3potKE0m<$)@?4wnG%T(s_R(@UG2Z3o*sO^#5;Ikf>1PD-W|N0lI620*!rAel-b
zU>IY54FFO~xULH!1nTPQpp*jVoP|Q6DTEMxGzTuq4oG8aL5`zfY(QAbN_QxC1o~s~
zd5}z{A%uX}>xHIi812b*U58;9a2yAYYisM{4Gj(P3nG<$UG0@Ai_AV1?3l=9jzPjhKlyq>Dx`8YPWBx@
zQBe`X;V>-A!lFfsFm2kj@s{=jm^=C>o6UZC?b@|508gH&a-K7|>5!#DA_4$JS}LE3
zLcnvHD^VgjG>?|dW|2=M5s5^`s^`9a`{4C@&&lbOQgZ-=0SrzE{7`qdw=2%;B;beS
z$S02~n{zgrB8&*jNAg>%OWengAB#U?Q{NYfFA$8!SY)~kQp{
zEcd67C;Q;?C6DyHy#53ESsv1MxD7Wh(BfG;_?EPj2Tj*AH>8`@#E@HGd*$8i1wYB!
zvFpY1RK|>JD!GEP)RsGM{iA!n5n8uy4}d-HO-pWi1B7-Chl_szuAleW
+
+
+
+
+
+
+
+
+
diff --git a/samples/WiktionarySimple/res/drawable/widget_bg_normal.9.png b/samples/WiktionarySimple/res/drawable/widget_bg_normal.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..314eb8ef96947d191ce4920c3d312da92fc65767
GIT binary patch
literal 268
zcmeAS@N?(olHy`uVBq!ia0vp^LO?9a!3HF~Pw#sVq!^2X+?^QKos)S90RNH+H(seOwv7tK_w>@#H4kzg|Ch3)w>#|ZKJJHmFDQ8$`^3*!ZN$uN
z^50;Q_OFM(-!cd*yZpHS|38mUPY=)kfB(}T+&J^p!JsD~sd3`O#!k+xfA(w~zvbs9
z^$N~rxvl2ME_5bQ<_Txf`Xi>t7|R8f_qDX~9g3OyfBmi6>4M!B%nU!cm2Iu7dYysJ
OWAJqKb6Mw<&;$T6n`1}-
literal 0
HcmV?d00001
diff --git a/samples/WiktionarySimple/res/drawable/widget_bg_selected.9.png b/samples/WiktionarySimple/res/drawable/widget_bg_selected.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..ef0cdc066a526eeeb7ba760d907115a2d1c0c26a
GIT binary patch
literal 276
zcmeAS@N?(olHy`uVBq!ia0vp^LO?9a!3HF~Pw#sVq!^2X+?^QKos)S9q?zevUW;JPv
PbRUDKtDnm{r-UW|1UqF4
literal 0
HcmV?d00001
diff --git a/samples/WiktionarySimple/res/layout/widget_message.xml b/samples/WiktionarySimple/res/layout/widget_message.xml
new file mode 100644
index 000000000..ba9471447
--- /dev/null
+++ b/samples/WiktionarySimple/res/layout/widget_message.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
diff --git a/samples/WiktionarySimple/res/layout/widget_word.xml b/samples/WiktionarySimple/res/layout/widget_word.xml
new file mode 100644
index 000000000..0e76f0b80
--- /dev/null
+++ b/samples/WiktionarySimple/res/layout/widget_word.xml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/WiktionarySimple/res/values/strings.xml b/samples/WiktionarySimple/res/values/strings.xml
new file mode 100644
index 000000000..65e44cb86
--- /dev/null
+++ b/samples/WiktionarySimple/res/values/strings.xml
@@ -0,0 +1,44 @@
+
+
+
+
+ Wiktionary simple example
+
+ "%s/%s (Linux; Android)"
+ "Wiktionary:Word of the day/%s %s"
+ "http://en.wiktionary.org/wiki/%s"
+
+ Wiktionary simple
+
+ Loading word\nof day\u2026
+ No word of\nday found
+
+
+ January
+ February
+ March
+ April
+ May
+ June
+ July
+ August
+ September
+ October
+ November
+ December
+
+
+
diff --git a/samples/WiktionarySimple/res/values/styles.xml b/samples/WiktionarySimple/res/values/styles.xml
new file mode 100644
index 000000000..42d679c56
--- /dev/null
+++ b/samples/WiktionarySimple/res/values/styles.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/WiktionarySimple/res/xml/widget_word.xml b/samples/WiktionarySimple/res/xml/widget_word.xml
new file mode 100644
index 000000000..46d31c321
--- /dev/null
+++ b/samples/WiktionarySimple/res/xml/widget_word.xml
@@ -0,0 +1,21 @@
+
+
+
+
diff --git a/samples/WiktionarySimple/src/com/example/android/simplewiktionary/SimpleWikiHelper.java b/samples/WiktionarySimple/src/com/example/android/simplewiktionary/SimpleWikiHelper.java
new file mode 100644
index 000000000..bb39d7bd7
--- /dev/null
+++ b/samples/WiktionarySimple/src/com/example/android/simplewiktionary/SimpleWikiHelper.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2009 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.simplewiktionary;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Helper methods to simplify talking with and parsing responses from a
+ * lightweight Wiktionary API. Before making any requests, you should call
+ * {@link #prepareUserAgent(Context)} to generate a User-Agent string based on
+ * your application package name and version.
+ */
+public class SimpleWikiHelper {
+ private static final String TAG = "SimpleWikiHelper";
+
+ /**
+ * Regular expression that splits "Word of the day" entry into word
+ * name, word type, and the first description bullet point.
+ */
+ public static final String WORD_OF_DAY_REGEX =
+ "(?s)\\{\\{wotd\\|(.+?)\\|(.+?)\\|([^#\\|]+).*?\\}\\}";
+
+ /**
+ * Partial URL to use when requesting the detailed entry for a specific
+ * Wiktionary page. Use {@link String#format(String, Object...)} to insert
+ * the desired page title after escaping it as needed.
+ */
+ private static final String WIKTIONARY_PAGE =
+ "http://en.wiktionary.org/w/api.php?action=query&prop=revisions&titles=%s&" +
+ "rvprop=content&format=json%s";
+
+ /**
+ * Partial URL to append to {@link #WIKTIONARY_PAGE} when you want to expand
+ * any templates found on the requested page. This is useful when browsing
+ * full entries, but may use more network bandwidth.
+ */
+ private static final String WIKTIONARY_EXPAND_TEMPLATES =
+ "&rvexpandtemplates=true";
+
+ /**
+ * {@link StatusLine} HTTP status code when no server error has occurred.
+ */
+ private static final int HTTP_STATUS_OK = 200;
+
+ /**
+ * Shared buffer used by {@link #getUrlContent(String)} when reading results
+ * from an API request.
+ */
+ private static byte[] sBuffer = new byte[512];
+
+ /**
+ * User-agent string to use when making requests. Should be filled using
+ * {@link #prepareUserAgent(Context)} before making any other calls.
+ */
+ private static String sUserAgent = null;
+
+ /**
+ * Thrown when there were problems contacting the remote API server, either
+ * because of a network error, or the server returned a bad status code.
+ */
+ public static class ApiException extends Exception {
+ public ApiException(String detailMessage, Throwable throwable) {
+ super(detailMessage, throwable);
+ }
+
+ public ApiException(String detailMessage) {
+ super(detailMessage);
+ }
+ }
+
+ /**
+ * Thrown when there were problems parsing the response to an API call,
+ * either because the response was empty, or it was malformed.
+ */
+ public static class ParseException extends Exception {
+ public ParseException(String detailMessage, Throwable throwable) {
+ super(detailMessage, throwable);
+ }
+ }
+
+ /**
+ * Prepare the internal User-Agent string for use. This requires a
+ * {@link Context} to pull the package name and version number for this
+ * application.
+ */
+ public static void prepareUserAgent(Context context) {
+ try {
+ // Read package name and version number from manifest
+ PackageManager manager = context.getPackageManager();
+ PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
+ sUserAgent = String.format(context.getString(R.string.template_user_agent),
+ info.packageName, info.versionName);
+
+ } catch(NameNotFoundException e) {
+ Log.e(TAG, "Couldn't find package information in PackageManager", e);
+ }
+ }
+
+ /**
+ * Read and return the content for a specific Wiktionary page. This makes a
+ * lightweight API call, and trims out just the page content returned.
+ * Because this call blocks until results are available, it should not be
+ * run from a UI thread.
+ *
+ * @param title The exact title of the Wiktionary page requested.
+ * @param expandTemplates If true, expand any wiki templates found.
+ * @return Exact content of page.
+ * @throws ApiException If any connection or server error occurs.
+ * @throws ParseException If there are problems parsing the response.
+ */
+ public static String getPageContent(String title, boolean expandTemplates)
+ throws ApiException, ParseException {
+ // Encode page title and expand templates if requested
+ String encodedTitle = Uri.encode(title);
+ String expandClause = expandTemplates ? WIKTIONARY_EXPAND_TEMPLATES : "";
+
+ // Query the API for content
+ String content = getUrlContent(String.format(WIKTIONARY_PAGE,
+ encodedTitle, expandClause));
+ try {
+ // Drill into the JSON response to find the content body
+ JSONObject response = new JSONObject(content);
+ JSONObject query = response.getJSONObject("query");
+ JSONObject pages = query.getJSONObject("pages");
+ JSONObject page = pages.getJSONObject((String) pages.keys().next());
+ JSONArray revisions = page.getJSONArray("revisions");
+ JSONObject revision = revisions.getJSONObject(0);
+ return revision.getString("*");
+ } catch (JSONException e) {
+ throw new ParseException("Problem parsing API response", e);
+ }
+ }
+
+ /**
+ * Pull the raw text content of the given URL. This call blocks until the
+ * operation has completed, and is synchronized because it uses a shared
+ * buffer {@link #sBuffer}.
+ *
+ * @param url The exact URL to request.
+ * @return The raw content returned by the server.
+ * @throws ApiException If any connection or server error occurs.
+ */
+ protected static synchronized String getUrlContent(String url) throws ApiException {
+ if (sUserAgent == null) {
+ throw new ApiException("User-Agent string must be prepared");
+ }
+
+ // Create client and set our specific user-agent string
+ HttpClient client = new DefaultHttpClient();
+ HttpGet request = new HttpGet(url);
+ request.setHeader("User-Agent", sUserAgent);
+
+ try {
+ HttpResponse response = client.execute(request);
+
+ // Check if server response is valid
+ StatusLine status = response.getStatusLine();
+ if (status.getStatusCode() != HTTP_STATUS_OK) {
+ throw new ApiException("Invalid response from server: " +
+ status.toString());
+ }
+
+ // Pull content stream from response
+ HttpEntity entity = response.getEntity();
+ InputStream inputStream = entity.getContent();
+
+ ByteArrayOutputStream content = new ByteArrayOutputStream();
+
+ // Read response into a buffered stream
+ int readBytes = 0;
+ while ((readBytes = inputStream.read(sBuffer)) != -1) {
+ content.write(sBuffer, 0, readBytes);
+ }
+
+ // Return result from buffered stream
+ return new String(content.toByteArray());
+ } catch (IOException e) {
+ throw new ApiException("Problem communicating with API", e);
+ }
+ }
+}
diff --git a/samples/WiktionarySimple/src/com/example/android/simplewiktionary/WordWidget.java b/samples/WiktionarySimple/src/com/example/android/simplewiktionary/WordWidget.java
new file mode 100644
index 000000000..d005faa4d
--- /dev/null
+++ b/samples/WiktionarySimple/src/com/example/android/simplewiktionary/WordWidget.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2009 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.simplewiktionary;
+
+import com.example.android.simplewiktionary.SimpleWikiHelper.ApiException;
+import com.example.android.simplewiktionary.SimpleWikiHelper.ParseException;
+
+import android.app.PendingIntent;
+import android.app.Service;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.IBinder;
+import android.text.format.Time;
+import android.util.Log;
+import android.widget.RemoteViews;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Define a simple widget that shows the Wiktionary "Word of the day." To build
+ * an update we spawn a background {@link Service} to perform the API queries.
+ */
+public class WordWidget extends AppWidgetProvider {
+ @Override
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager,
+ int[] appWidgetIds) {
+ // To prevent any ANR timeouts, we perform the update in a service
+ context.startService(new Intent(context, UpdateService.class));
+ }
+
+ public static class UpdateService extends Service {
+ @Override
+ public void onStart(Intent intent, int startId) {
+ // Build the widget update for today
+ RemoteViews updateViews = buildUpdate(this);
+
+ // Push update for this widget to the home screen
+ ComponentName thisWidget = new ComponentName(this, WordWidget.class);
+ AppWidgetManager manager = AppWidgetManager.getInstance(this);
+ manager.updateAppWidget(thisWidget, updateViews);
+ }
+
+ /**
+ * Build a widget update to show the current Wiktionary
+ * "Word of the day." Will block until the online API returns.
+ */
+ public RemoteViews buildUpdate(Context context) {
+ // Pick out month names from resources
+ Resources res = context.getResources();
+ String[] monthNames = res.getStringArray(R.array.month_names);
+
+ // Find current month and day
+ Time today = new Time();
+ today.setToNow();
+
+ // Build today's page title, like "Wiktionary:Word of the day/March 21"
+ String pageName = res.getString(R.string.template_wotd_title,
+ monthNames[today.month], today.monthDay);
+ RemoteViews updateViews = null;
+ String pageContent = "";
+
+ try {
+ // Try querying the Wiktionary API for today's word
+ SimpleWikiHelper.prepareUserAgent(context);
+ pageContent = SimpleWikiHelper.getPageContent(pageName, false);
+ } catch (ApiException e) {
+ Log.e("WordWidget", "Couldn't contact API", e);
+ } catch (ParseException e) {
+ Log.e("WordWidget", "Couldn't parse API response", e);
+ }
+
+ // Use a regular expression to parse out the word and its definition
+ Pattern pattern = Pattern.compile(SimpleWikiHelper.WORD_OF_DAY_REGEX);
+ Matcher matcher = pattern.matcher(pageContent);
+ if (matcher.find()) {
+ // Build an update that holds the updated widget contents
+ updateViews = new RemoteViews(context.getPackageName(), R.layout.widget_word);
+
+ String wordTitle = matcher.group(1);
+ updateViews.setTextViewText(R.id.word_title, wordTitle);
+ updateViews.setTextViewText(R.id.word_type, matcher.group(2));
+ updateViews.setTextViewText(R.id.definition, matcher.group(3).trim());
+
+ // When user clicks on widget, launch to Wiktionary definition page
+ String definePage = res.getString(R.string.template_define_url,
+ Uri.encode(wordTitle));
+ Intent defineIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(definePage));
+ PendingIntent pendingIntent = PendingIntent.getActivity(context,
+ 0 /* no requestCode */, defineIntent, 0 /* no flags */);
+ updateViews.setOnClickPendingIntent(R.id.widget, pendingIntent);
+
+ } else {
+ // Didn't find word of day, so show error message
+ updateViews = new RemoteViews(context.getPackageName(), R.layout.widget_message);
+ CharSequence errorMessage = context.getText(R.string.widget_error);
+ updateViews.setTextViewText(R.id.message, errorMessage);
+ }
+ return updateViews;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ // We don't need to bind to this service
+ return null;
+ }
+ }
+}
From a13e890e57951b0cd78146fc2a04de15ff35cb33 Mon Sep 17 00:00:00 2001
From: David 'Digit' Turner
Date: Wed, 9 Dec 2009 15:46:07 -0800
Subject: [PATCH 09/17] Update documentation to indicate EGL ES 2.0 isn't
supported in the emulator.
---
ndk/docs/CHANGES.TXT | 4 ++++
ndk/docs/STABLE-APIS.TXT | 8 ++++++--
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/ndk/docs/CHANGES.TXT b/ndk/docs/CHANGES.TXT
index 145933c0b..c6db521e8 100644
--- a/ndk/docs/CHANGES.TXT
+++ b/ndk/docs/CHANGES.TXT
@@ -56,6 +56,10 @@ IMPORTANT CHANGES:
OpenGL ES 2.0 is currently *not* available from Java, and must be used
through native code exclusively.
+ IMPORTANT: OpenGL ES 2.0 is not supported in the Android emulator at this
+ time. Running/testing any native code that depends on it thus
+ requires a real device.
+
- The NDK build script will now remove installed binaries from the application
project's path before starting the build. This ensures that:
diff --git a/ndk/docs/STABLE-APIS.TXT b/ndk/docs/STABLE-APIS.TXT
index 700bb8843..48e6b5aeb 100644
--- a/ndk/docs/STABLE-APIS.TXT
+++ b/ndk/docs/STABLE-APIS.TXT
@@ -157,8 +157,7 @@ IV. Android-5 Stable Native APIs:
----------------------------------
All the APIs listed below are available for developing native code that runs
-on the Eclair experimental branch, which will be used to make the next official
-platform system images.
+on Android 2.0 system images and above.
The OpenGL ES 2.0 Library:
@@ -183,3 +182,8 @@ http://android-developers.blogspot.com/2009/04/introducing-glsurfaceview.html
The "hello-gl2" sample application demonstrate this. It is used to draw a very
simple triangle with the help of a vertex and fragment shaders.
+
+IMPORTANT NOTE:
+ The Android emulator does not support OpenGL ES 2.0 hardware emulation
+ at this time. Running and testing code that uses this API requires a
+ real device with such capabilities.
From e7e7a6b821367c5c2ce7a0eac66f92a2689c22e9 Mon Sep 17 00:00:00 2001
From: Roman Nurik
Date: Thu, 10 Dec 2009 09:43:24 -0800
Subject: [PATCH 10/17] Modify index pages of MultiResolution, Wiktionary, and
WiktionarySimple samples, to point to new screenshots. Also some minor
cleanup.
---
samples/MultiResolution/_index.html | 2 +-
samples/Wiktionary/_index.html | 12 ++++++------
samples/WiktionarySimple/_index.html | 2 +-
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/samples/MultiResolution/_index.html b/samples/MultiResolution/_index.html
index 73089ae64..825ea4da4 100644
--- a/samples/MultiResolution/_index.html
+++ b/samples/MultiResolution/_index.html
@@ -17,4 +17,4 @@ It demonstrates using:
A sample application that demonstrates how to create an interactive widget
-for display on the Android home screen.
+
A sample application that demonstrates how to create an interactive Android
+home screen widget that launches an application activity.
When installed, this adds a "Wiktionary" option to the widget installation
menu. The word of the day is downloaded from Wiktionary and displayed in a
-frame. Touching the widget will open a custom WebView to render the
-definition.
+frame. Touching the widget will launch a custom application activity showing
+more details.
-
-
+
+
diff --git a/samples/WiktionarySimple/_index.html b/samples/WiktionarySimple/_index.html
index cf8142e89..3980e3326 100644
--- a/samples/WiktionarySimple/_index.html
+++ b/samples/WiktionarySimple/_index.html
@@ -7,4 +7,4 @@ load the word's Wiktionary entry.
A more advanced version of this sample is available in the Wiktionary directory.
-
+
From 94b6d06899ddb3f97cd0171bce5de1cb60602d01 Mon Sep 17 00:00:00 2001
From: Tom O'Neill
Date: Wed, 9 Dec 2009 15:41:05 -0800
Subject: [PATCH 11/17] Extract and check in NotepadCodeLab.zip for change
tracking
The 3-part Notepad tutorial is currently only available in the zip archive
frameworks/base/docs/html/guide/tutorials/notepad/codelab/NotepadCodeLab.zip.
This CL adds the uncompressed code to the repository so that there is a
paper trail of future changes to the code.
---
.../Notepadv1/AndroidManifest.xml | 11 +
.../Notepadv1/res/drawable/icon.png | Bin 0 -> 6094 bytes
.../Notepadv1/res/layout/notepad_list.xml | 6 +
.../Notepadv1/res/values/strings.xml | 5 +
.../com/android/demo/notepad1/Notepadv1.java | 44 ++++
.../android/demo/notepad1/NotesDbAdapter.java | 188 ++++++++++++++++++
.../Notepadv1Solution/AndroidManifest.xml | 12 ++
.../Notepadv1Solution/res/drawable/icon.png | Bin 0 -> 6094 bytes
.../res/layout/notepad_list.xml | 14 ++
.../res/layout/notes_row.xml | 5 +
.../Notepadv1Solution/res/values/strings.xml | 6 +
.../com/android/demo/notepad1/Notepadv1.java | 78 ++++++++
.../android/demo/notepad1/NotesDbAdapter.java | 188 ++++++++++++++++++
.../Notepadv2/AndroidManifest.xml | 12 ++
.../Notepadv2/res/drawable/icon.png | Bin 0 -> 6094 bytes
.../Notepadv2/res/layout/note_edit.xml | 33 +++
.../Notepadv2/res/layout/notes_list.xml | 13 ++
.../Notepadv2/res/layout/notes_row.xml | 4 +
.../Notepadv2/res/values/strings.xml | 11 +
.../com/android/demo/notepad2/Notepadv2.java | 120 +++++++++++
.../android/demo/notepad2/NotesDbAdapter.java | 188 ++++++++++++++++++
.../Notepadv2Solution/AndroidManifest.xml | 12 ++
.../Notepadv2Solution/res/drawable/icon.png | Bin 0 -> 6094 bytes
.../res/layout/note_edit.xml | 33 +++
.../res/layout/notes_list.xml | 13 ++
.../res/layout/notes_row.xml | 4 +
.../Notepadv2Solution/res/values/strings.xml | 11 +
.../com/android/demo/notepad2/NoteEdit.java | 76 +++++++
.../com/android/demo/notepad2/Notepadv2.java | 147 ++++++++++++++
.../android/demo/notepad2/NotesDbAdapter.java | 188 ++++++++++++++++++
.../Notepadv3/AndroidManifest.xml | 12 ++
.../Notepadv3/res/drawable/icon.png | Bin 0 -> 6094 bytes
.../Notepadv3/res/layout/note_edit.xml | 33 +++
.../Notepadv3/res/layout/notes_list.xml | 13 ++
.../Notepadv3/res/layout/notes_row.xml | 4 +
.../Notepadv3/res/values/strings.xml | 11 +
.../com/android/demo/notepad3/NoteEdit.java | 76 +++++++
.../com/android/demo/notepad3/Notepadv3.java | 148 ++++++++++++++
.../android/demo/notepad3/NotesDbAdapter.java | 188 ++++++++++++++++++
.../Notepadv3Solution/AndroidManifest.xml | 12 ++
.../Notepadv3Solution/res/drawable/icon.png | Bin 0 -> 6094 bytes
.../res/layout/note_edit.xml | 33 +++
.../res/layout/notes_list.xml | 13 ++
.../res/layout/notes_row.xml | 4 +
.../Notepadv3Solution/res/values/strings.xml | 11 +
.../com/android/demo/notepad3/NoteEdit.java | 109 ++++++++++
.../com/android/demo/notepad3/Notepadv3.java | 124 ++++++++++++
.../android/demo/notepad3/NotesDbAdapter.java | 188 ++++++++++++++++++
48 files changed, 2401 insertions(+)
create mode 100755 tutorials/NotepadCodeLab/Notepadv1/AndroidManifest.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv1/res/drawable/icon.png
create mode 100755 tutorials/NotepadCodeLab/Notepadv1/res/layout/notepad_list.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv1/res/values/strings.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv1/src/com/android/demo/notepad1/Notepadv1.java
create mode 100755 tutorials/NotepadCodeLab/Notepadv1/src/com/android/demo/notepad1/NotesDbAdapter.java
create mode 100755 tutorials/NotepadCodeLab/Notepadv1Solution/AndroidManifest.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv1Solution/res/drawable/icon.png
create mode 100755 tutorials/NotepadCodeLab/Notepadv1Solution/res/layout/notepad_list.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv1Solution/res/layout/notes_row.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv1Solution/res/values/strings.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv1Solution/src/com/android/demo/notepad1/Notepadv1.java
create mode 100755 tutorials/NotepadCodeLab/Notepadv1Solution/src/com/android/demo/notepad1/NotesDbAdapter.java
create mode 100755 tutorials/NotepadCodeLab/Notepadv2/AndroidManifest.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv2/res/drawable/icon.png
create mode 100755 tutorials/NotepadCodeLab/Notepadv2/res/layout/note_edit.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv2/res/layout/notes_list.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv2/res/layout/notes_row.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv2/res/values/strings.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv2/src/com/android/demo/notepad2/Notepadv2.java
create mode 100755 tutorials/NotepadCodeLab/Notepadv2/src/com/android/demo/notepad2/NotesDbAdapter.java
create mode 100755 tutorials/NotepadCodeLab/Notepadv2Solution/AndroidManifest.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv2Solution/res/drawable/icon.png
create mode 100755 tutorials/NotepadCodeLab/Notepadv2Solution/res/layout/note_edit.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv2Solution/res/layout/notes_list.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv2Solution/res/layout/notes_row.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv2Solution/res/values/strings.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv2Solution/src/com/android/demo/notepad2/NoteEdit.java
create mode 100755 tutorials/NotepadCodeLab/Notepadv2Solution/src/com/android/demo/notepad2/Notepadv2.java
create mode 100755 tutorials/NotepadCodeLab/Notepadv2Solution/src/com/android/demo/notepad2/NotesDbAdapter.java
create mode 100755 tutorials/NotepadCodeLab/Notepadv3/AndroidManifest.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv3/res/drawable/icon.png
create mode 100755 tutorials/NotepadCodeLab/Notepadv3/res/layout/note_edit.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv3/res/layout/notes_list.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv3/res/layout/notes_row.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv3/res/values/strings.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv3/src/com/android/demo/notepad3/NoteEdit.java
create mode 100755 tutorials/NotepadCodeLab/Notepadv3/src/com/android/demo/notepad3/Notepadv3.java
create mode 100755 tutorials/NotepadCodeLab/Notepadv3/src/com/android/demo/notepad3/NotesDbAdapter.java
create mode 100755 tutorials/NotepadCodeLab/Notepadv3Solution/AndroidManifest.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv3Solution/res/drawable/icon.png
create mode 100755 tutorials/NotepadCodeLab/Notepadv3Solution/res/layout/note_edit.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv3Solution/res/layout/notes_list.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv3Solution/res/layout/notes_row.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv3Solution/res/values/strings.xml
create mode 100755 tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NoteEdit.java
create mode 100755 tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/Notepadv3.java
create mode 100755 tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NotesDbAdapter.java
diff --git a/tutorials/NotepadCodeLab/Notepadv1/AndroidManifest.xml b/tutorials/NotepadCodeLab/Notepadv1/AndroidManifest.xml
new file mode 100755
index 000000000..33f30da3c
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv1/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tutorials/NotepadCodeLab/Notepadv1/res/drawable/icon.png b/tutorials/NotepadCodeLab/Notepadv1/res/drawable/icon.png
new file mode 100755
index 0000000000000000000000000000000000000000..64e3601c234d3818409fb5daad54b099b76e9a04
GIT binary patch
literal 6094
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
U$@ynO6#xJL07*qoM6N<$f@Zv%aR2}S
literal 0
HcmV?d00001
diff --git a/tutorials/NotepadCodeLab/Notepadv1/res/layout/notepad_list.xml b/tutorials/NotepadCodeLab/Notepadv1/res/layout/notepad_list.xml
new file mode 100755
index 000000000..154888dd3
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv1/res/layout/notepad_list.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/tutorials/NotepadCodeLab/Notepadv1/res/values/strings.xml b/tutorials/NotepadCodeLab/Notepadv1/res/values/strings.xml
new file mode 100755
index 000000000..5698961a6
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv1/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+
+ Notepad v1
+ No Notes Yet
+
diff --git a/tutorials/NotepadCodeLab/Notepadv1/src/com/android/demo/notepad1/Notepadv1.java b/tutorials/NotepadCodeLab/Notepadv1/src/com/android/demo/notepad1/Notepadv1.java
new file mode 100755
index 000000000..43e7a7733
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv1/src/com/android/demo/notepad1/Notepadv1.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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.android.demo.notepad1;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+
+public class Notepadv1 extends Activity {
+ private int mNoteNumber = 1;
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // TODO Auto-generated method stub
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // TODO Auto-generated method stub
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/tutorials/NotepadCodeLab/Notepadv1/src/com/android/demo/notepad1/NotesDbAdapter.java b/tutorials/NotepadCodeLab/Notepadv1/src/com/android/demo/notepad1/NotesDbAdapter.java
new file mode 100755
index 000000000..6f85bbd20
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv1/src/com/android/demo/notepad1/NotesDbAdapter.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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.android.demo.notepad1;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+/**
+ * Simple notes database access helper class. Defines the basic CRUD operations
+ * for the notepad example, and gives the ability to list all notes as well as
+ * retrieve or modify a specific note.
+ *
+ * This has been improved from the first version of this tutorial through the
+ * addition of better error handling and also using returning a Cursor instead
+ * of using a collection of inner classes (which is less scalable and not
+ * recommended).
+ */
+public class NotesDbAdapter {
+
+ public static final String KEY_TITLE = "title";
+ public static final String KEY_BODY = "body";
+ public static final String KEY_ROWID = "_id";
+
+ private static final String TAG = "NotesDbAdapter";
+ private DatabaseHelper mDbHelper;
+ private SQLiteDatabase mDb;
+
+ /**
+ * Database creation sql statement
+ */
+ private static final String DATABASE_CREATE =
+ "create table notes (_id integer primary key autoincrement, "
+ + "title text not null, body text not null);";
+
+ private static final String DATABASE_NAME = "data";
+ private static final String DATABASE_TABLE = "notes";
+ private static final int DATABASE_VERSION = 2;
+
+ private final Context mCtx;
+
+ private static class DatabaseHelper extends SQLiteOpenHelper {
+
+ DatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+
+ db.execSQL(DATABASE_CREATE);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ + newVersion + ", which will destroy all old data");
+ db.execSQL("DROP TABLE IF EXISTS notes");
+ onCreate(db);
+ }
+ }
+
+ /**
+ * Constructor - takes the context to allow the database to be
+ * opened/created
+ *
+ * @param ctx the Context within which to work
+ */
+ public NotesDbAdapter(Context ctx) {
+ this.mCtx = ctx;
+ }
+
+ /**
+ * Open the notes database. If it cannot be opened, try to create a new
+ * instance of the database. If it cannot be created, throw an exception to
+ * signal the failure
+ *
+ * @return this (self reference, allowing this to be chained in an
+ * initialization call)
+ * @throws SQLException if the database could be neither opened or created
+ */
+ public NotesDbAdapter open() throws SQLException {
+ mDbHelper = new DatabaseHelper(mCtx);
+ mDb = mDbHelper.getWritableDatabase();
+ return this;
+ }
+
+ public void close() {
+ mDbHelper.close();
+ }
+
+
+ /**
+ * Create a new note using the title and body provided. If the note is
+ * successfully created return the new rowId for that note, otherwise return
+ * a -1 to indicate failure.
+ *
+ * @param title the title of the note
+ * @param body the body of the note
+ * @return rowId or -1 if failed
+ */
+ public long createNote(String title, String body) {
+ ContentValues initialValues = new ContentValues();
+ initialValues.put(KEY_TITLE, title);
+ initialValues.put(KEY_BODY, body);
+
+ return mDb.insert(DATABASE_TABLE, null, initialValues);
+ }
+
+ /**
+ * Delete the note with the given rowId
+ *
+ * @param rowId id of note to delete
+ * @return true if deleted, false otherwise
+ */
+ public boolean deleteNote(long rowId) {
+
+ return mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0;
+ }
+
+ /**
+ * Return a Cursor over the list of all notes in the database
+ *
+ * @return Cursor over all notes
+ */
+ public Cursor fetchAllNotes() {
+
+ return mDb.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_TITLE,
+ KEY_BODY}, null, null, null, null, null);
+ }
+
+ /**
+ * Return a Cursor positioned at the note that matches the given rowId
+ *
+ * @param rowId id of note to retrieve
+ * @return Cursor positioned to matching note, if found
+ * @throws SQLException if note could not be found/retrieved
+ */
+ public Cursor fetchNote(long rowId) throws SQLException {
+
+ Cursor mCursor =
+
+ mDb.query(true, DATABASE_TABLE, new String[] {KEY_ROWID,
+ KEY_TITLE, KEY_BODY}, KEY_ROWID + "=" + rowId, null,
+ null, null, null, null);
+ if (mCursor != null) {
+ mCursor.moveToFirst();
+ }
+ return mCursor;
+
+ }
+
+ /**
+ * Update the note using the details provided. The note to be updated is
+ * specified using the rowId, and it is altered to use the title and body
+ * values passed in
+ *
+ * @param rowId id of note to update
+ * @param title value to set note title to
+ * @param body value to set note body to
+ * @return true if the note was successfully updated, false otherwise
+ */
+ public boolean updateNote(long rowId, String title, String body) {
+ ContentValues args = new ContentValues();
+ args.put(KEY_TITLE, title);
+ args.put(KEY_BODY, body);
+
+ return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0;
+ }
+}
diff --git a/tutorials/NotepadCodeLab/Notepadv1Solution/AndroidManifest.xml b/tutorials/NotepadCodeLab/Notepadv1Solution/AndroidManifest.xml
new file mode 100755
index 000000000..99023fe78
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv1Solution/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tutorials/NotepadCodeLab/Notepadv1Solution/res/drawable/icon.png b/tutorials/NotepadCodeLab/Notepadv1Solution/res/drawable/icon.png
new file mode 100755
index 0000000000000000000000000000000000000000..64e3601c234d3818409fb5daad54b099b76e9a04
GIT binary patch
literal 6094
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
U$@ynO6#xJL07*qoM6N<$f@Zv%aR2}S
literal 0
HcmV?d00001
diff --git a/tutorials/NotepadCodeLab/Notepadv1Solution/res/layout/notepad_list.xml b/tutorials/NotepadCodeLab/Notepadv1Solution/res/layout/notepad_list.xml
new file mode 100755
index 000000000..0c8dbabee
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv1Solution/res/layout/notepad_list.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tutorials/NotepadCodeLab/Notepadv1Solution/res/layout/notes_row.xml b/tutorials/NotepadCodeLab/Notepadv1Solution/res/layout/notes_row.xml
new file mode 100755
index 000000000..6b1a65b05
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv1Solution/res/layout/notes_row.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/tutorials/NotepadCodeLab/Notepadv1Solution/res/values/strings.xml b/tutorials/NotepadCodeLab/Notepadv1Solution/res/values/strings.xml
new file mode 100755
index 000000000..265bc62e3
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv1Solution/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+ Notepad v1
+ No Notes Yet
+ Add Item
+
diff --git a/tutorials/NotepadCodeLab/Notepadv1Solution/src/com/android/demo/notepad1/Notepadv1.java b/tutorials/NotepadCodeLab/Notepadv1Solution/src/com/android/demo/notepad1/Notepadv1.java
new file mode 100755
index 000000000..a1819ed0f
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv1Solution/src/com/android/demo/notepad1/Notepadv1.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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.android.demo.notepad1;
+
+import android.app.ListActivity;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.SimpleCursorAdapter;
+
+public class Notepadv1 extends ListActivity {
+ public static final int INSERT_ID = Menu.FIRST;
+
+ private int mNoteNumber = 1;
+ private NotesDbAdapter mDbHelper;
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.notepad_list);
+ mDbHelper = new NotesDbAdapter(this);
+ mDbHelper.open();
+ fillData();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ boolean result = super.onCreateOptionsMenu(menu);
+ menu.add(0, INSERT_ID, 0, R.string.menu_insert);
+ return result;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case INSERT_ID:
+ createNote();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void createNote() {
+ String noteName = "Note " + mNoteNumber++;
+ mDbHelper.createNote(noteName, "");
+ fillData();
+ }
+
+ private void fillData() {
+ // Get all of the notes from the database and create the item list
+ Cursor c = mDbHelper.fetchAllNotes();
+ startManagingCursor(c);
+
+ String[] from = new String[] { NotesDbAdapter.KEY_TITLE };
+ int[] to = new int[] { R.id.text1 };
+
+ // Now create an array adapter and set it to display using our row
+ SimpleCursorAdapter notes =
+ new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
+ setListAdapter(notes);
+ }
+}
diff --git a/tutorials/NotepadCodeLab/Notepadv1Solution/src/com/android/demo/notepad1/NotesDbAdapter.java b/tutorials/NotepadCodeLab/Notepadv1Solution/src/com/android/demo/notepad1/NotesDbAdapter.java
new file mode 100755
index 000000000..6f85bbd20
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv1Solution/src/com/android/demo/notepad1/NotesDbAdapter.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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.android.demo.notepad1;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+/**
+ * Simple notes database access helper class. Defines the basic CRUD operations
+ * for the notepad example, and gives the ability to list all notes as well as
+ * retrieve or modify a specific note.
+ *
+ * This has been improved from the first version of this tutorial through the
+ * addition of better error handling and also using returning a Cursor instead
+ * of using a collection of inner classes (which is less scalable and not
+ * recommended).
+ */
+public class NotesDbAdapter {
+
+ public static final String KEY_TITLE = "title";
+ public static final String KEY_BODY = "body";
+ public static final String KEY_ROWID = "_id";
+
+ private static final String TAG = "NotesDbAdapter";
+ private DatabaseHelper mDbHelper;
+ private SQLiteDatabase mDb;
+
+ /**
+ * Database creation sql statement
+ */
+ private static final String DATABASE_CREATE =
+ "create table notes (_id integer primary key autoincrement, "
+ + "title text not null, body text not null);";
+
+ private static final String DATABASE_NAME = "data";
+ private static final String DATABASE_TABLE = "notes";
+ private static final int DATABASE_VERSION = 2;
+
+ private final Context mCtx;
+
+ private static class DatabaseHelper extends SQLiteOpenHelper {
+
+ DatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+
+ db.execSQL(DATABASE_CREATE);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ + newVersion + ", which will destroy all old data");
+ db.execSQL("DROP TABLE IF EXISTS notes");
+ onCreate(db);
+ }
+ }
+
+ /**
+ * Constructor - takes the context to allow the database to be
+ * opened/created
+ *
+ * @param ctx the Context within which to work
+ */
+ public NotesDbAdapter(Context ctx) {
+ this.mCtx = ctx;
+ }
+
+ /**
+ * Open the notes database. If it cannot be opened, try to create a new
+ * instance of the database. If it cannot be created, throw an exception to
+ * signal the failure
+ *
+ * @return this (self reference, allowing this to be chained in an
+ * initialization call)
+ * @throws SQLException if the database could be neither opened or created
+ */
+ public NotesDbAdapter open() throws SQLException {
+ mDbHelper = new DatabaseHelper(mCtx);
+ mDb = mDbHelper.getWritableDatabase();
+ return this;
+ }
+
+ public void close() {
+ mDbHelper.close();
+ }
+
+
+ /**
+ * Create a new note using the title and body provided. If the note is
+ * successfully created return the new rowId for that note, otherwise return
+ * a -1 to indicate failure.
+ *
+ * @param title the title of the note
+ * @param body the body of the note
+ * @return rowId or -1 if failed
+ */
+ public long createNote(String title, String body) {
+ ContentValues initialValues = new ContentValues();
+ initialValues.put(KEY_TITLE, title);
+ initialValues.put(KEY_BODY, body);
+
+ return mDb.insert(DATABASE_TABLE, null, initialValues);
+ }
+
+ /**
+ * Delete the note with the given rowId
+ *
+ * @param rowId id of note to delete
+ * @return true if deleted, false otherwise
+ */
+ public boolean deleteNote(long rowId) {
+
+ return mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0;
+ }
+
+ /**
+ * Return a Cursor over the list of all notes in the database
+ *
+ * @return Cursor over all notes
+ */
+ public Cursor fetchAllNotes() {
+
+ return mDb.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_TITLE,
+ KEY_BODY}, null, null, null, null, null);
+ }
+
+ /**
+ * Return a Cursor positioned at the note that matches the given rowId
+ *
+ * @param rowId id of note to retrieve
+ * @return Cursor positioned to matching note, if found
+ * @throws SQLException if note could not be found/retrieved
+ */
+ public Cursor fetchNote(long rowId) throws SQLException {
+
+ Cursor mCursor =
+
+ mDb.query(true, DATABASE_TABLE, new String[] {KEY_ROWID,
+ KEY_TITLE, KEY_BODY}, KEY_ROWID + "=" + rowId, null,
+ null, null, null, null);
+ if (mCursor != null) {
+ mCursor.moveToFirst();
+ }
+ return mCursor;
+
+ }
+
+ /**
+ * Update the note using the details provided. The note to be updated is
+ * specified using the rowId, and it is altered to use the title and body
+ * values passed in
+ *
+ * @param rowId id of note to update
+ * @param title value to set note title to
+ * @param body value to set note body to
+ * @return true if the note was successfully updated, false otherwise
+ */
+ public boolean updateNote(long rowId, String title, String body) {
+ ContentValues args = new ContentValues();
+ args.put(KEY_TITLE, title);
+ args.put(KEY_BODY, body);
+
+ return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0;
+ }
+}
diff --git a/tutorials/NotepadCodeLab/Notepadv2/AndroidManifest.xml b/tutorials/NotepadCodeLab/Notepadv2/AndroidManifest.xml
new file mode 100755
index 000000000..738fb2afc
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv2/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tutorials/NotepadCodeLab/Notepadv2/res/drawable/icon.png b/tutorials/NotepadCodeLab/Notepadv2/res/drawable/icon.png
new file mode 100755
index 0000000000000000000000000000000000000000..64e3601c234d3818409fb5daad54b099b76e9a04
GIT binary patch
literal 6094
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
U$@ynO6#xJL07*qoM6N<$f@Zv%aR2}S
literal 0
HcmV?d00001
diff --git a/tutorials/NotepadCodeLab/Notepadv2/res/layout/note_edit.xml b/tutorials/NotepadCodeLab/Notepadv2/res/layout/note_edit.xml
new file mode 100755
index 000000000..b254552c3
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv2/res/layout/note_edit.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tutorials/NotepadCodeLab/Notepadv2/res/layout/notes_list.xml b/tutorials/NotepadCodeLab/Notepadv2/res/layout/notes_list.xml
new file mode 100755
index 000000000..29ae88631
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv2/res/layout/notes_list.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/tutorials/NotepadCodeLab/Notepadv2/res/layout/notes_row.xml b/tutorials/NotepadCodeLab/Notepadv2/res/layout/notes_row.xml
new file mode 100755
index 000000000..f28a41bea
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv2/res/layout/notes_row.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/tutorials/NotepadCodeLab/Notepadv2/res/values/strings.xml b/tutorials/NotepadCodeLab/Notepadv2/res/values/strings.xml
new file mode 100755
index 000000000..b70c3f843
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv2/res/values/strings.xml
@@ -0,0 +1,11 @@
+
+
+ Notepad v2
+ No Notes Yet
+ Add Note
+ Delete Note
+ Title
+ Body
+ Confirm
+ Edit Note
+
diff --git a/tutorials/NotepadCodeLab/Notepadv2/src/com/android/demo/notepad2/Notepadv2.java b/tutorials/NotepadCodeLab/Notepadv2/src/com/android/demo/notepad2/Notepadv2.java
new file mode 100755
index 000000000..abfc53864
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv2/src/com/android/demo/notepad2/Notepadv2.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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.android.demo.notepad2;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+
+public class Notepadv2 extends ListActivity {
+ private static final int ACTIVITY_CREATE=0;
+ private static final int ACTIVITY_EDIT=1;
+
+ private static final int INSERT_ID = Menu.FIRST;
+ private static final int DELETE_ID = Menu.FIRST + 1;
+
+ private NotesDbAdapter mDbHelper;
+ private Cursor mNotesCursor;
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.notes_list);
+ mDbHelper = new NotesDbAdapter(this);
+ mDbHelper.open();
+ fillData();
+ }
+
+ private void fillData() {
+ // Get all of the rows from the database and create the item list
+ mNotesCursor = mDbHelper.fetchAllNotes();
+ startManagingCursor(mNotesCursor);
+
+ // Create an array to specify the fields we want to display in the list (only TITLE)
+ String[] from = new String[]{NotesDbAdapter.KEY_TITLE};
+
+ // and an array of the fields we want to bind those fields to (in this case just text1)
+ int[] to = new int[]{R.id.text1};
+
+ // Now create a simple cursor adapter and set it to display
+ SimpleCursorAdapter notes =
+ new SimpleCursorAdapter(this, R.layout.notes_row, mNotesCursor, from, to);
+ setListAdapter(notes);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ menu.add(0, INSERT_ID,0, R.string.menu_insert);
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ switch(item.getItemId()) {
+ case INSERT_ID:
+ createNote();
+ return true;
+ }
+
+ return super.onMenuItemSelected(featureId, item);
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+
+ // TODO: fill in rest of method
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ return super.onContextItemSelected(item);
+
+ // TODO: fill in rest of method
+ }
+
+ private void createNote() {
+ // TODO: fill in implementation
+
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ super.onListItemClick(l, v, position, id);
+
+ // TODO: fill in rest of method
+
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ super.onActivityResult(requestCode, resultCode, intent);
+
+ // TODO: fill in rest of method
+
+ }
+
+}
diff --git a/tutorials/NotepadCodeLab/Notepadv2/src/com/android/demo/notepad2/NotesDbAdapter.java b/tutorials/NotepadCodeLab/Notepadv2/src/com/android/demo/notepad2/NotesDbAdapter.java
new file mode 100755
index 000000000..5bf51b1e5
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv2/src/com/android/demo/notepad2/NotesDbAdapter.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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.android.demo.notepad2;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+/**
+ * Simple notes database access helper class. Defines the basic CRUD operations
+ * for the notepad example, and gives the ability to list all notes as well as
+ * retrieve or modify a specific note.
+ *
+ * This has been improved from the first version of this tutorial through the
+ * addition of better error handling and also using returning a Cursor instead
+ * of using a collection of inner classes (which is less scalable and not
+ * recommended).
+ */
+public class NotesDbAdapter {
+
+ public static final String KEY_TITLE = "title";
+ public static final String KEY_BODY = "body";
+ public static final String KEY_ROWID = "_id";
+
+ private static final String TAG = "NotesDbAdapter";
+ private DatabaseHelper mDbHelper;
+ private SQLiteDatabase mDb;
+
+ /**
+ * Database creation sql statement
+ */
+ private static final String DATABASE_CREATE =
+ "create table notes (_id integer primary key autoincrement, "
+ + "title text not null, body text not null);";
+
+ private static final String DATABASE_NAME = "data";
+ private static final String DATABASE_TABLE = "notes";
+ private static final int DATABASE_VERSION = 2;
+
+ private final Context mCtx;
+
+ private static class DatabaseHelper extends SQLiteOpenHelper {
+
+ DatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+
+ db.execSQL(DATABASE_CREATE);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ + newVersion + ", which will destroy all old data");
+ db.execSQL("DROP TABLE IF EXISTS notes");
+ onCreate(db);
+ }
+ }
+
+ /**
+ * Constructor - takes the context to allow the database to be
+ * opened/created
+ *
+ * @param ctx the Context within which to work
+ */
+ public NotesDbAdapter(Context ctx) {
+ this.mCtx = ctx;
+ }
+
+ /**
+ * Open the notes database. If it cannot be opened, try to create a new
+ * instance of the database. If it cannot be created, throw an exception to
+ * signal the failure
+ *
+ * @return this (self reference, allowing this to be chained in an
+ * initialization call)
+ * @throws SQLException if the database could be neither opened or created
+ */
+ public NotesDbAdapter open() throws SQLException {
+ mDbHelper = new DatabaseHelper(mCtx);
+ mDb = mDbHelper.getWritableDatabase();
+ return this;
+ }
+
+ public void close() {
+ mDbHelper.close();
+ }
+
+
+ /**
+ * Create a new note using the title and body provided. If the note is
+ * successfully created return the new rowId for that note, otherwise return
+ * a -1 to indicate failure.
+ *
+ * @param title the title of the note
+ * @param body the body of the note
+ * @return rowId or -1 if failed
+ */
+ public long createNote(String title, String body) {
+ ContentValues initialValues = new ContentValues();
+ initialValues.put(KEY_TITLE, title);
+ initialValues.put(KEY_BODY, body);
+
+ return mDb.insert(DATABASE_TABLE, null, initialValues);
+ }
+
+ /**
+ * Delete the note with the given rowId
+ *
+ * @param rowId id of note to delete
+ * @return true if deleted, false otherwise
+ */
+ public boolean deleteNote(long rowId) {
+
+ return mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0;
+ }
+
+ /**
+ * Return a Cursor over the list of all notes in the database
+ *
+ * @return Cursor over all notes
+ */
+ public Cursor fetchAllNotes() {
+
+ return mDb.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_TITLE,
+ KEY_BODY}, null, null, null, null, null);
+ }
+
+ /**
+ * Return a Cursor positioned at the note that matches the given rowId
+ *
+ * @param rowId id of note to retrieve
+ * @return Cursor positioned to matching note, if found
+ * @throws SQLException if note could not be found/retrieved
+ */
+ public Cursor fetchNote(long rowId) throws SQLException {
+
+ Cursor mCursor =
+
+ mDb.query(true, DATABASE_TABLE, new String[] {KEY_ROWID,
+ KEY_TITLE, KEY_BODY}, KEY_ROWID + "=" + rowId, null,
+ null, null, null, null);
+ if (mCursor != null) {
+ mCursor.moveToFirst();
+ }
+ return mCursor;
+
+ }
+
+ /**
+ * Update the note using the details provided. The note to be updated is
+ * specified using the rowId, and it is altered to use the title and body
+ * values passed in
+ *
+ * @param rowId id of note to update
+ * @param title value to set note title to
+ * @param body value to set note body to
+ * @return true if the note was successfully updated, false otherwise
+ */
+ public boolean updateNote(long rowId, String title, String body) {
+ ContentValues args = new ContentValues();
+ args.put(KEY_TITLE, title);
+ args.put(KEY_BODY, body);
+
+ return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0;
+ }
+}
diff --git a/tutorials/NotepadCodeLab/Notepadv2Solution/AndroidManifest.xml b/tutorials/NotepadCodeLab/Notepadv2Solution/AndroidManifest.xml
new file mode 100755
index 000000000..dbfc9d0f7
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv2Solution/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tutorials/NotepadCodeLab/Notepadv2Solution/res/drawable/icon.png b/tutorials/NotepadCodeLab/Notepadv2Solution/res/drawable/icon.png
new file mode 100755
index 0000000000000000000000000000000000000000..64e3601c234d3818409fb5daad54b099b76e9a04
GIT binary patch
literal 6094
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
U$@ynO6#xJL07*qoM6N<$f@Zv%aR2}S
literal 0
HcmV?d00001
diff --git a/tutorials/NotepadCodeLab/Notepadv2Solution/res/layout/note_edit.xml b/tutorials/NotepadCodeLab/Notepadv2Solution/res/layout/note_edit.xml
new file mode 100755
index 000000000..b254552c3
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv2Solution/res/layout/note_edit.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tutorials/NotepadCodeLab/Notepadv2Solution/res/layout/notes_list.xml b/tutorials/NotepadCodeLab/Notepadv2Solution/res/layout/notes_list.xml
new file mode 100755
index 000000000..29ae88631
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv2Solution/res/layout/notes_list.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/tutorials/NotepadCodeLab/Notepadv2Solution/res/layout/notes_row.xml b/tutorials/NotepadCodeLab/Notepadv2Solution/res/layout/notes_row.xml
new file mode 100755
index 000000000..f28a41bea
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv2Solution/res/layout/notes_row.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/tutorials/NotepadCodeLab/Notepadv2Solution/res/values/strings.xml b/tutorials/NotepadCodeLab/Notepadv2Solution/res/values/strings.xml
new file mode 100755
index 000000000..b70c3f843
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv2Solution/res/values/strings.xml
@@ -0,0 +1,11 @@
+
+
+ Notepad v2
+ No Notes Yet
+ Add Note
+ Delete Note
+ Title
+ Body
+ Confirm
+ Edit Note
+
diff --git a/tutorials/NotepadCodeLab/Notepadv2Solution/src/com/android/demo/notepad2/NoteEdit.java b/tutorials/NotepadCodeLab/Notepadv2Solution/src/com/android/demo/notepad2/NoteEdit.java
new file mode 100755
index 000000000..6c5e664b5
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv2Solution/src/com/android/demo/notepad2/NoteEdit.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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.savedInstanceState
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.demo.notepad2;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+
+public class NoteEdit extends Activity {
+
+ private EditText mTitleText;
+ private EditText mBodyText;
+ private Long mRowId;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.note_edit);
+
+ mTitleText = (EditText) findViewById(R.id.title);
+ mBodyText = (EditText) findViewById(R.id.body);
+
+ Button confirmButton = (Button) findViewById(R.id.confirm);
+
+ mRowId = null;
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ String title = extras.getString(NotesDbAdapter.KEY_TITLE);
+ String body = extras.getString(NotesDbAdapter.KEY_BODY);
+ mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
+
+ if (title != null) {
+ mTitleText.setText(title);
+ }
+ if (body != null) {
+ mBodyText.setText(body);
+ }
+ }
+
+ confirmButton.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View view) {
+ Bundle bundle = new Bundle();
+
+ bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
+ bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
+ if (mRowId != null) {
+ bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
+ }
+
+ Intent mIntent = new Intent();
+ mIntent.putExtras(bundle);
+ setResult(RESULT_OK, mIntent);
+ finish();
+ }
+
+ });
+ }
+}
diff --git a/tutorials/NotepadCodeLab/Notepadv2Solution/src/com/android/demo/notepad2/Notepadv2.java b/tutorials/NotepadCodeLab/Notepadv2Solution/src/com/android/demo/notepad2/Notepadv2.java
new file mode 100755
index 000000000..1b3398291
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv2Solution/src/com/android/demo/notepad2/Notepadv2.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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.android.demo.notepad2;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+
+public class Notepadv2 extends ListActivity {
+ private static final int ACTIVITY_CREATE=0;
+ private static final int ACTIVITY_EDIT=1;
+
+ private static final int INSERT_ID = Menu.FIRST;
+ private static final int DELETE_ID = Menu.FIRST + 1;
+
+ private NotesDbAdapter mDbHelper;
+ private Cursor mNotesCursor;
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.notes_list);
+ mDbHelper = new NotesDbAdapter(this);
+ mDbHelper.open();
+ fillData();
+ registerForContextMenu(getListView());
+ }
+
+ private void fillData() {
+ // Get all of the rows from the database and create the item list
+ mNotesCursor = mDbHelper.fetchAllNotes();
+ startManagingCursor(mNotesCursor);
+
+ // Create an array to specify the fields we want to display in the list (only TITLE)
+ String[] from = new String[]{NotesDbAdapter.KEY_TITLE};
+
+ // and an array of the fields we want to bind those fields to (in this case just text1)
+ int[] to = new int[]{R.id.text1};
+
+ // Now create a simple cursor adapter and set it to display
+ SimpleCursorAdapter notes =
+ new SimpleCursorAdapter(this, R.layout.notes_row, mNotesCursor, from, to);
+ setListAdapter(notes);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ menu.add(0, INSERT_ID,0, R.string.menu_insert);
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ switch(item.getItemId()) {
+ case INSERT_ID:
+ createNote();
+ return true;
+ }
+ return super.onMenuItemSelected(featureId, item);
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ menu.add(0, DELETE_ID, 0, R.string.menu_delete);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ switch(item.getItemId()) {
+ case DELETE_ID:
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
+ mDbHelper.deleteNote(info.id);
+ fillData();
+ return true;
+ }
+ return super.onContextItemSelected(item);
+ }
+
+ private void createNote() {
+ Intent i = new Intent(this, NoteEdit.class);
+ startActivityForResult(i, ACTIVITY_CREATE);
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ super.onListItemClick(l, v, position, id);
+ Cursor c = mNotesCursor;
+ c.moveToPosition(position);
+ Intent i = new Intent(this, NoteEdit.class);
+ i.putExtra(NotesDbAdapter.KEY_ROWID, id);
+ i.putExtra(NotesDbAdapter.KEY_TITLE, c.getString(
+ c.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
+ i.putExtra(NotesDbAdapter.KEY_BODY, c.getString(
+ c.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
+ startActivityForResult(i, ACTIVITY_EDIT);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ super.onActivityResult(requestCode, resultCode, intent);
+ Bundle extras = intent.getExtras();
+ switch(requestCode) {
+ case ACTIVITY_CREATE:
+ String title = extras.getString(NotesDbAdapter.KEY_TITLE);
+ String body = extras.getString(NotesDbAdapter.KEY_BODY);
+ mDbHelper.createNote(title, body);
+ fillData();
+ break;
+ case ACTIVITY_EDIT:
+ Long rowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
+ if (rowId != null) {
+ String editTitle = extras.getString(NotesDbAdapter.KEY_TITLE);
+ String editBody = extras.getString(NotesDbAdapter.KEY_BODY);
+ mDbHelper.updateNote(rowId, editTitle, editBody);
+ }
+ fillData();
+ break;
+ }
+ }
+}
diff --git a/tutorials/NotepadCodeLab/Notepadv2Solution/src/com/android/demo/notepad2/NotesDbAdapter.java b/tutorials/NotepadCodeLab/Notepadv2Solution/src/com/android/demo/notepad2/NotesDbAdapter.java
new file mode 100755
index 000000000..5bf51b1e5
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv2Solution/src/com/android/demo/notepad2/NotesDbAdapter.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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.android.demo.notepad2;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+/**
+ * Simple notes database access helper class. Defines the basic CRUD operations
+ * for the notepad example, and gives the ability to list all notes as well as
+ * retrieve or modify a specific note.
+ *
+ * This has been improved from the first version of this tutorial through the
+ * addition of better error handling and also using returning a Cursor instead
+ * of using a collection of inner classes (which is less scalable and not
+ * recommended).
+ */
+public class NotesDbAdapter {
+
+ public static final String KEY_TITLE = "title";
+ public static final String KEY_BODY = "body";
+ public static final String KEY_ROWID = "_id";
+
+ private static final String TAG = "NotesDbAdapter";
+ private DatabaseHelper mDbHelper;
+ private SQLiteDatabase mDb;
+
+ /**
+ * Database creation sql statement
+ */
+ private static final String DATABASE_CREATE =
+ "create table notes (_id integer primary key autoincrement, "
+ + "title text not null, body text not null);";
+
+ private static final String DATABASE_NAME = "data";
+ private static final String DATABASE_TABLE = "notes";
+ private static final int DATABASE_VERSION = 2;
+
+ private final Context mCtx;
+
+ private static class DatabaseHelper extends SQLiteOpenHelper {
+
+ DatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+
+ db.execSQL(DATABASE_CREATE);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ + newVersion + ", which will destroy all old data");
+ db.execSQL("DROP TABLE IF EXISTS notes");
+ onCreate(db);
+ }
+ }
+
+ /**
+ * Constructor - takes the context to allow the database to be
+ * opened/created
+ *
+ * @param ctx the Context within which to work
+ */
+ public NotesDbAdapter(Context ctx) {
+ this.mCtx = ctx;
+ }
+
+ /**
+ * Open the notes database. If it cannot be opened, try to create a new
+ * instance of the database. If it cannot be created, throw an exception to
+ * signal the failure
+ *
+ * @return this (self reference, allowing this to be chained in an
+ * initialization call)
+ * @throws SQLException if the database could be neither opened or created
+ */
+ public NotesDbAdapter open() throws SQLException {
+ mDbHelper = new DatabaseHelper(mCtx);
+ mDb = mDbHelper.getWritableDatabase();
+ return this;
+ }
+
+ public void close() {
+ mDbHelper.close();
+ }
+
+
+ /**
+ * Create a new note using the title and body provided. If the note is
+ * successfully created return the new rowId for that note, otherwise return
+ * a -1 to indicate failure.
+ *
+ * @param title the title of the note
+ * @param body the body of the note
+ * @return rowId or -1 if failed
+ */
+ public long createNote(String title, String body) {
+ ContentValues initialValues = new ContentValues();
+ initialValues.put(KEY_TITLE, title);
+ initialValues.put(KEY_BODY, body);
+
+ return mDb.insert(DATABASE_TABLE, null, initialValues);
+ }
+
+ /**
+ * Delete the note with the given rowId
+ *
+ * @param rowId id of note to delete
+ * @return true if deleted, false otherwise
+ */
+ public boolean deleteNote(long rowId) {
+
+ return mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0;
+ }
+
+ /**
+ * Return a Cursor over the list of all notes in the database
+ *
+ * @return Cursor over all notes
+ */
+ public Cursor fetchAllNotes() {
+
+ return mDb.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_TITLE,
+ KEY_BODY}, null, null, null, null, null);
+ }
+
+ /**
+ * Return a Cursor positioned at the note that matches the given rowId
+ *
+ * @param rowId id of note to retrieve
+ * @return Cursor positioned to matching note, if found
+ * @throws SQLException if note could not be found/retrieved
+ */
+ public Cursor fetchNote(long rowId) throws SQLException {
+
+ Cursor mCursor =
+
+ mDb.query(true, DATABASE_TABLE, new String[] {KEY_ROWID,
+ KEY_TITLE, KEY_BODY}, KEY_ROWID + "=" + rowId, null,
+ null, null, null, null);
+ if (mCursor != null) {
+ mCursor.moveToFirst();
+ }
+ return mCursor;
+
+ }
+
+ /**
+ * Update the note using the details provided. The note to be updated is
+ * specified using the rowId, and it is altered to use the title and body
+ * values passed in
+ *
+ * @param rowId id of note to update
+ * @param title value to set note title to
+ * @param body value to set note body to
+ * @return true if the note was successfully updated, false otherwise
+ */
+ public boolean updateNote(long rowId, String title, String body) {
+ ContentValues args = new ContentValues();
+ args.put(KEY_TITLE, title);
+ args.put(KEY_BODY, body);
+
+ return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0;
+ }
+}
diff --git a/tutorials/NotepadCodeLab/Notepadv3/AndroidManifest.xml b/tutorials/NotepadCodeLab/Notepadv3/AndroidManifest.xml
new file mode 100755
index 000000000..adb07ffdb
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv3/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tutorials/NotepadCodeLab/Notepadv3/res/drawable/icon.png b/tutorials/NotepadCodeLab/Notepadv3/res/drawable/icon.png
new file mode 100755
index 0000000000000000000000000000000000000000..64e3601c234d3818409fb5daad54b099b76e9a04
GIT binary patch
literal 6094
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
U$@ynO6#xJL07*qoM6N<$f@Zv%aR2}S
literal 0
HcmV?d00001
diff --git a/tutorials/NotepadCodeLab/Notepadv3/res/layout/note_edit.xml b/tutorials/NotepadCodeLab/Notepadv3/res/layout/note_edit.xml
new file mode 100755
index 000000000..b254552c3
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv3/res/layout/note_edit.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tutorials/NotepadCodeLab/Notepadv3/res/layout/notes_list.xml b/tutorials/NotepadCodeLab/Notepadv3/res/layout/notes_list.xml
new file mode 100755
index 000000000..29ae88631
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv3/res/layout/notes_list.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/tutorials/NotepadCodeLab/Notepadv3/res/layout/notes_row.xml b/tutorials/NotepadCodeLab/Notepadv3/res/layout/notes_row.xml
new file mode 100755
index 000000000..f28a41bea
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv3/res/layout/notes_row.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/tutorials/NotepadCodeLab/Notepadv3/res/values/strings.xml b/tutorials/NotepadCodeLab/Notepadv3/res/values/strings.xml
new file mode 100755
index 000000000..ae63b837d
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv3/res/values/strings.xml
@@ -0,0 +1,11 @@
+
+
+ Notepad v3
+ No Notes Yet
+ Add Note
+ Delete Note
+ Title
+ Body
+ Confirm
+ Edit Note
+
diff --git a/tutorials/NotepadCodeLab/Notepadv3/src/com/android/demo/notepad3/NoteEdit.java b/tutorials/NotepadCodeLab/Notepadv3/src/com/android/demo/notepad3/NoteEdit.java
new file mode 100755
index 000000000..af3a2340d
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv3/src/com/android/demo/notepad3/NoteEdit.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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.android.demo.notepad3;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+
+public class NoteEdit extends Activity {
+
+ private EditText mTitleText;
+ private EditText mBodyText;
+ private Long mRowId;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.note_edit);
+
+ mTitleText = (EditText) findViewById(R.id.title);
+ mBodyText = (EditText) findViewById(R.id.body);
+
+ Button confirmButton = (Button) findViewById(R.id.confirm);
+
+ mRowId = null;
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ String title = extras.getString(NotesDbAdapter.KEY_TITLE);
+ String body = extras.getString(NotesDbAdapter.KEY_BODY);
+ mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
+
+ if (title != null) {
+ mTitleText.setText(title);
+ }
+ if (body != null) {
+ mBodyText.setText(body);
+ }
+ }
+
+ confirmButton.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View view) {
+ Bundle bundle = new Bundle();
+
+ bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
+ bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
+ if (mRowId != null) {
+ bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
+ }
+
+ Intent mIntent = new Intent();
+ mIntent.putExtras(bundle);
+ setResult(RESULT_OK, mIntent);
+ finish();
+ }
+
+ });
+ }
+}
diff --git a/tutorials/NotepadCodeLab/Notepadv3/src/com/android/demo/notepad3/Notepadv3.java b/tutorials/NotepadCodeLab/Notepadv3/src/com/android/demo/notepad3/Notepadv3.java
new file mode 100755
index 000000000..f50f371d8
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv3/src/com/android/demo/notepad3/Notepadv3.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")savedInstanceState;
+ * 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.android.demo.notepad3;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+
+public class Notepadv3 extends ListActivity {
+ private static final int ACTIVITY_CREATE=0;
+ private static final int ACTIVITY_EDIT=1;
+
+ private static final int INSERT_ID = Menu.FIRST;
+ private static final int DELETE_ID = Menu.FIRST + 1;
+
+ private NotesDbAdapter mDbHelper;
+ private Cursor mNotesCursor;
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.notes_list);
+ mDbHelper = new NotesDbAdapter(this);
+ mDbHelper.open();
+ fillData();
+ registerForContextMenu(getListView());
+ }
+
+ private void fillData() {
+ // Get all of the rows from the database and create the item list
+ mNotesCursor = mDbHelper.fetchAllNotes();
+ startManagingCursor(mNotesCursor);
+
+ // Create an array to specify the fields we want to display in the list (only TITLE)
+ String[] from = new String[]{NotesDbAdapter.KEY_TITLE};
+
+ // and an array of the fields we want to bind those fields to (in this case just text1)
+ int[] to = new int[]{R.id.text1};
+
+ // Now create a simple cursor adapter and set it to display
+ SimpleCursorAdapter notes =
+ new SimpleCursorAdapter(this, R.layout.notes_row, mNotesCursor, from, to);
+ setListAdapter(notes);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ menu.add(0, INSERT_ID, 0, R.string.menu_insert);
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ switch(item.getItemId()) {
+ case INSERT_ID:
+ createNote();
+ return true;
+ }
+
+ return super.onMenuItemSelected(featureId, item);
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ menu.add(0, DELETE_ID, 0, R.string.menu_delete);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ switch(item.getItemId()) {
+ case DELETE_ID:
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
+ mDbHelper.deleteNote(info.id);
+ fillData();
+ return true;
+ }
+ return super.onContextItemSelected(item);
+ }
+
+ private void createNote() {
+ Intent i = new Intent(this, NoteEdit.class);
+ startActivityForResult(i, ACTIVITY_CREATE);
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ super.onListItemClick(l, v, position, id);
+ Cursor c = mNotesCursor;
+ c.moveToPosition(position);
+ Intent i = new Intent(this, NoteEdit.class);
+ i.putExtra(NotesDbAdapter.KEY_ROWID, id);
+ i.putExtra(NotesDbAdapter.KEY_TITLE, c.getString(
+ c.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
+ i.putExtra(NotesDbAdapter.KEY_BODY, c.getString(
+ c.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
+ startActivityForResult(i, ACTIVITY_EDIT);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ super.onActivityResult(requestCode, resultCode, intent);
+ Bundle extras = intent.getExtras();
+ switch(requestCode) {
+ case ACTIVITY_CREATE:
+ String title = extras.getString(NotesDbAdapter.KEY_TITLE);
+ String body = extras.getString(NotesDbAdapter.KEY_BODY);
+ mDbHelper.createNote(title, body);
+ fillData();
+ break;
+ case ACTIVITY_EDIT:
+ Long rowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
+ if (rowId != null) {
+ String editTitle = extras.getString(NotesDbAdapter.KEY_TITLE);
+ String editBody = extras.getString(NotesDbAdapter.KEY_BODY);
+ mDbHelper.updateNote(rowId, editTitle, editBody);
+ }
+ fillData();
+ break;
+ }
+ }
+}
diff --git a/tutorials/NotepadCodeLab/Notepadv3/src/com/android/demo/notepad3/NotesDbAdapter.java b/tutorials/NotepadCodeLab/Notepadv3/src/com/android/demo/notepad3/NotesDbAdapter.java
new file mode 100755
index 000000000..61ad04623
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv3/src/com/android/demo/notepad3/NotesDbAdapter.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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.android.demo.notepad3;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+/**
+ * Simple notes database access helper class. Defines the basic CRUD operations
+ * for the notepad example, and gives the ability to list all notes as well as
+ * retrieve or modify a specific note.
+ *
+ * This has been improved from the first version of this tutorial through the
+ * addition of better error handling and also using returning a Cursor instead
+ * of using a collection of inner classes (which is less scalable and not
+ * recommended).
+ */
+public class NotesDbAdapter {
+
+ public static final String KEY_TITLE = "title";
+ public static final String KEY_BODY = "body";
+ public static final String KEY_ROWID = "_id";
+
+ private static final String TAG = "NotesDbAdapter";
+ private DatabaseHelper mDbHelper;
+ private SQLiteDatabase mDb;
+
+ /**
+ * Database creation sql statement
+ */
+ private static final String DATABASE_CREATE =
+ "create table notes (_id integer primary key autoincrement, "
+ + "title text not null, body text not null);";
+
+ private static final String DATABASE_NAME = "data";
+ private static final String DATABASE_TABLE = "notes";
+ private static final int DATABASE_VERSION = 2;
+
+ private final Context mCtx;
+
+ private static class DatabaseHelper extends SQLiteOpenHelper {
+
+ DatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+
+ db.execSQL(DATABASE_CREATE);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ + newVersion + ", which will destroy all old data");
+ db.execSQL("DROP TABLE IF EXISTS notes");
+ onCreate(db);
+ }
+ }
+
+ /**
+ * Constructor - takes the context to allow the database to be
+ * opened/created
+ *
+ * @param ctx the Context within which to work
+ */
+ public NotesDbAdapter(Context ctx) {
+ this.mCtx = ctx;
+ }
+
+ /**
+ * Open the notes database. If it cannot be opened, try to create a new
+ * instance of the database. If it cannot be created, throw an exception to
+ * signal the failure
+ *
+ * @return this (self reference, allowing this to be chained in an
+ * initialization call)
+ * @throws SQLException if the database could be neither opened or created
+ */
+ public NotesDbAdapter open() throws SQLException {
+ mDbHelper = new DatabaseHelper(mCtx);
+ mDb = mDbHelper.getWritableDatabase();
+ return this;
+ }
+
+ public void close() {
+ mDbHelper.close();
+ }
+
+
+ /**
+ * Create a new note using the title and body provided. If the note is
+ * successfully created return the new rowId for that note, otherwise return
+ * a -1 to indicate failure.
+ *
+ * @param title the title of the note
+ * @param body the body of the note
+ * @return rowId or -1 if failed
+ */
+ public long createNote(String title, String body) {
+ ContentValues initialValues = new ContentValues();
+ initialValues.put(KEY_TITLE, title);
+ initialValues.put(KEY_BODY, body);
+
+ return mDb.insert(DATABASE_TABLE, null, initialValues);
+ }
+
+ /**
+ * Delete the note with the given rowId
+ *
+ * @param rowId id of note to delete
+ * @return true if deleted, false otherwise
+ */
+ public boolean deleteNote(long rowId) {
+
+ return mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0;
+ }
+
+ /**
+ * Return a Cursor over the list of all notes in the database
+ *
+ * @return Cursor over all notes
+ */
+ public Cursor fetchAllNotes() {
+
+ return mDb.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_TITLE,
+ KEY_BODY}, null, null, null, null, null);
+ }
+
+ /**
+ * Return a Cursor positioned at the note that matches the given rowId
+ *
+ * @param rowId id of note to retrieve
+ * @return Cursor positioned to matching note, if found
+ * @throws SQLException if note could not be found/retrieved
+ */
+ public Cursor fetchNote(long rowId) throws SQLException {
+
+ Cursor mCursor =
+
+ mDb.query(true, DATABASE_TABLE, new String[] {KEY_ROWID,
+ KEY_TITLE, KEY_BODY}, KEY_ROWID + "=" + rowId, null,
+ null, null, null, null);
+ if (mCursor != null) {
+ mCursor.moveToFirst();
+ }
+ return mCursor;
+
+ }
+
+ /**
+ * Update the note using the details provided. The note to be updated is
+ * specified using the rowId, and it is altered to use the title and body
+ * values passed in
+ *
+ * @param rowId id of note to update
+ * @param title value to set note title to
+ * @param body value to set note body to
+ * @return true if the note was successfully updated, false otherwise
+ */
+ public boolean updateNote(long rowId, String title, String body) {
+ ContentValues args = new ContentValues();
+ args.put(KEY_TITLE, title);
+ args.put(KEY_BODY, body);
+
+ return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0;
+ }
+}
diff --git a/tutorials/NotepadCodeLab/Notepadv3Solution/AndroidManifest.xml b/tutorials/NotepadCodeLab/Notepadv3Solution/AndroidManifest.xml
new file mode 100755
index 000000000..adb07ffdb
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv3Solution/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tutorials/NotepadCodeLab/Notepadv3Solution/res/drawable/icon.png b/tutorials/NotepadCodeLab/Notepadv3Solution/res/drawable/icon.png
new file mode 100755
index 0000000000000000000000000000000000000000..64e3601c234d3818409fb5daad54b099b76e9a04
GIT binary patch
literal 6094
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
U$@ynO6#xJL07*qoM6N<$f@Zv%aR2}S
literal 0
HcmV?d00001
diff --git a/tutorials/NotepadCodeLab/Notepadv3Solution/res/layout/note_edit.xml b/tutorials/NotepadCodeLab/Notepadv3Solution/res/layout/note_edit.xml
new file mode 100755
index 000000000..b254552c3
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv3Solution/res/layout/note_edit.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tutorials/NotepadCodeLab/Notepadv3Solution/res/layout/notes_list.xml b/tutorials/NotepadCodeLab/Notepadv3Solution/res/layout/notes_list.xml
new file mode 100755
index 000000000..29ae88631
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv3Solution/res/layout/notes_list.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/tutorials/NotepadCodeLab/Notepadv3Solution/res/layout/notes_row.xml b/tutorials/NotepadCodeLab/Notepadv3Solution/res/layout/notes_row.xml
new file mode 100755
index 000000000..f28a41bea
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv3Solution/res/layout/notes_row.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/tutorials/NotepadCodeLab/Notepadv3Solution/res/values/strings.xml b/tutorials/NotepadCodeLab/Notepadv3Solution/res/values/strings.xml
new file mode 100755
index 000000000..ae63b837d
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv3Solution/res/values/strings.xml
@@ -0,0 +1,11 @@
+
+
+ Notepad v3
+ No Notes Yet
+ Add Note
+ Delete Note
+ Title
+ Body
+ Confirm
+ Edit Note
+
diff --git a/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NoteEdit.java b/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NoteEdit.java
new file mode 100755
index 000000000..710ea339d
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NoteEdit.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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.android.demo.notepad3;
+
+import android.app.Activity;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+
+public class NoteEdit extends Activity {
+
+ private EditText mTitleText;
+ private EditText mBodyText;
+ private Long mRowId;
+ private NotesDbAdapter mDbHelper;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mDbHelper = new NotesDbAdapter(this);
+ mDbHelper.open();
+ setContentView(R.layout.note_edit);
+
+
+ mTitleText = (EditText) findViewById(R.id.title);
+ mBodyText = (EditText) findViewById(R.id.body);
+
+ Button confirmButton = (Button) findViewById(R.id.confirm);
+
+ mRowId = savedInstanceState != null ? savedInstanceState.getLong(NotesDbAdapter.KEY_ROWID)
+ : null;
+ if (mRowId == null) {
+ Bundle extras = getIntent().getExtras();
+ mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID)
+ : null;
+ }
+
+ populateFields();
+
+ confirmButton.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View view) {
+ setResult(RESULT_OK);
+ finish();
+ }
+
+ });
+ }
+
+ private void populateFields() {
+ if (mRowId != null) {
+ Cursor note = mDbHelper.fetchNote(mRowId);
+ startManagingCursor(note);
+ mTitleText.setText(note.getString(
+ note.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
+ mBodyText.setText(note.getString(
+ note.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ saveState();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ populateFields();
+ }
+
+ private void saveState() {
+ String title = mTitleText.getText().toString();
+ String body = mBodyText.getText().toString();
+
+ if (mRowId == null) {
+ long id = mDbHelper.createNote(title, body);
+ if (id > 0) {
+ mRowId = id;
+ }
+ } else {
+ mDbHelper.updateNote(mRowId, title, body);
+ }
+ }
+
+}
diff --git a/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/Notepadv3.java b/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/Notepadv3.java
new file mode 100755
index 000000000..7f9903750
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/Notepadv3.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")savedInstanceState;
+ * 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.android.demo.notepad3;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+
+public class Notepadv3 extends ListActivity {
+ private static final int ACTIVITY_CREATE=0;
+ private static final int ACTIVITY_EDIT=1;
+
+ private static final int INSERT_ID = Menu.FIRST;
+ private static final int DELETE_ID = Menu.FIRST + 1;
+
+ private NotesDbAdapter mDbHelper;
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.notes_list);
+ mDbHelper = new NotesDbAdapter(this);
+ mDbHelper.open();
+ fillData();
+ registerForContextMenu(getListView());
+ }
+
+ private void fillData() {
+ Cursor notesCursor = mDbHelper.fetchAllNotes();
+ startManagingCursor(notesCursor);
+
+ // Create an array to specify the fields we want to display in the list (only TITLE)
+ String[] from = new String[]{NotesDbAdapter.KEY_TITLE};
+
+ // and an array of the fields we want to bind those fields to (in this case just text1)
+ int[] to = new int[]{R.id.text1};
+
+ // Now create a simple cursor adapter and set it to display
+ SimpleCursorAdapter notes =
+ new SimpleCursorAdapter(this, R.layout.notes_row, notesCursor, from, to);
+ setListAdapter(notes);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ menu.add(0, INSERT_ID, 0, R.string.menu_insert);
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ switch(item.getItemId()) {
+ case INSERT_ID:
+ createNote();
+ return true;
+ }
+
+ return super.onMenuItemSelected(featureId, item);
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ menu.add(0, DELETE_ID, 0, R.string.menu_delete);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ switch(item.getItemId()) {
+ case DELETE_ID:
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
+ mDbHelper.deleteNote(info.id);
+ fillData();
+ return true;
+ }
+ return super.onContextItemSelected(item);
+ }
+
+ private void createNote() {
+ Intent i = new Intent(this, NoteEdit.class);
+ startActivityForResult(i, ACTIVITY_CREATE);
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ super.onListItemClick(l, v, position, id);
+ Intent i = new Intent(this, NoteEdit.class);
+ i.putExtra(NotesDbAdapter.KEY_ROWID, id);
+ startActivityForResult(i, ACTIVITY_EDIT);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode,
+ Intent intent) {
+ super.onActivityResult(requestCode, resultCode, intent);
+ fillData();
+ }
+}
diff --git a/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NotesDbAdapter.java b/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NotesDbAdapter.java
new file mode 100755
index 000000000..61ad04623
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NotesDbAdapter.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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.android.demo.notepad3;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+/**
+ * Simple notes database access helper class. Defines the basic CRUD operations
+ * for the notepad example, and gives the ability to list all notes as well as
+ * retrieve or modify a specific note.
+ *
+ * This has been improved from the first version of this tutorial through the
+ * addition of better error handling and also using returning a Cursor instead
+ * of using a collection of inner classes (which is less scalable and not
+ * recommended).
+ */
+public class NotesDbAdapter {
+
+ public static final String KEY_TITLE = "title";
+ public static final String KEY_BODY = "body";
+ public static final String KEY_ROWID = "_id";
+
+ private static final String TAG = "NotesDbAdapter";
+ private DatabaseHelper mDbHelper;
+ private SQLiteDatabase mDb;
+
+ /**
+ * Database creation sql statement
+ */
+ private static final String DATABASE_CREATE =
+ "create table notes (_id integer primary key autoincrement, "
+ + "title text not null, body text not null);";
+
+ private static final String DATABASE_NAME = "data";
+ private static final String DATABASE_TABLE = "notes";
+ private static final int DATABASE_VERSION = 2;
+
+ private final Context mCtx;
+
+ private static class DatabaseHelper extends SQLiteOpenHelper {
+
+ DatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+
+ db.execSQL(DATABASE_CREATE);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ + newVersion + ", which will destroy all old data");
+ db.execSQL("DROP TABLE IF EXISTS notes");
+ onCreate(db);
+ }
+ }
+
+ /**
+ * Constructor - takes the context to allow the database to be
+ * opened/created
+ *
+ * @param ctx the Context within which to work
+ */
+ public NotesDbAdapter(Context ctx) {
+ this.mCtx = ctx;
+ }
+
+ /**
+ * Open the notes database. If it cannot be opened, try to create a new
+ * instance of the database. If it cannot be created, throw an exception to
+ * signal the failure
+ *
+ * @return this (self reference, allowing this to be chained in an
+ * initialization call)
+ * @throws SQLException if the database could be neither opened or created
+ */
+ public NotesDbAdapter open() throws SQLException {
+ mDbHelper = new DatabaseHelper(mCtx);
+ mDb = mDbHelper.getWritableDatabase();
+ return this;
+ }
+
+ public void close() {
+ mDbHelper.close();
+ }
+
+
+ /**
+ * Create a new note using the title and body provided. If the note is
+ * successfully created return the new rowId for that note, otherwise return
+ * a -1 to indicate failure.
+ *
+ * @param title the title of the note
+ * @param body the body of the note
+ * @return rowId or -1 if failed
+ */
+ public long createNote(String title, String body) {
+ ContentValues initialValues = new ContentValues();
+ initialValues.put(KEY_TITLE, title);
+ initialValues.put(KEY_BODY, body);
+
+ return mDb.insert(DATABASE_TABLE, null, initialValues);
+ }
+
+ /**
+ * Delete the note with the given rowId
+ *
+ * @param rowId id of note to delete
+ * @return true if deleted, false otherwise
+ */
+ public boolean deleteNote(long rowId) {
+
+ return mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0;
+ }
+
+ /**
+ * Return a Cursor over the list of all notes in the database
+ *
+ * @return Cursor over all notes
+ */
+ public Cursor fetchAllNotes() {
+
+ return mDb.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_TITLE,
+ KEY_BODY}, null, null, null, null, null);
+ }
+
+ /**
+ * Return a Cursor positioned at the note that matches the given rowId
+ *
+ * @param rowId id of note to retrieve
+ * @return Cursor positioned to matching note, if found
+ * @throws SQLException if note could not be found/retrieved
+ */
+ public Cursor fetchNote(long rowId) throws SQLException {
+
+ Cursor mCursor =
+
+ mDb.query(true, DATABASE_TABLE, new String[] {KEY_ROWID,
+ KEY_TITLE, KEY_BODY}, KEY_ROWID + "=" + rowId, null,
+ null, null, null, null);
+ if (mCursor != null) {
+ mCursor.moveToFirst();
+ }
+ return mCursor;
+
+ }
+
+ /**
+ * Update the note using the details provided. The note to be updated is
+ * specified using the rowId, and it is altered to use the title and body
+ * values passed in
+ *
+ * @param rowId id of note to update
+ * @param title value to set note title to
+ * @param body value to set note body to
+ * @return true if the note was successfully updated, false otherwise
+ */
+ public boolean updateNote(long rowId, String title, String body) {
+ ContentValues args = new ContentValues();
+ args.put(KEY_TITLE, title);
+ args.put(KEY_BODY, body);
+
+ return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0;
+ }
+}
From f4ff28edda30f72984eeb50a9153879c1e5f0abb Mon Sep 17 00:00:00 2001
From: Romain Guy
Date: Thu, 10 Dec 2009 15:19:03 -0800
Subject: [PATCH 12/17] Cleanup one of the ListView sample demos.
---
samples/ApiDemos/res/layout/list_7.xml | 4 +-
.../com/example/android/apis/view/List7.java | 64 ++++++++++++-------
2 files changed, 44 insertions(+), 24 deletions(-)
diff --git a/samples/ApiDemos/res/layout/list_7.xml b/samples/ApiDemos/res/layout/list_7.xml
index 2dc777707..dfaf093e7 100644
--- a/samples/ApiDemos/res/layout/list_7.xml
+++ b/samples/ApiDemos/res/layout/list_7.xml
@@ -20,7 +20,7 @@
android:layout_height="fill_parent"
android:paddingLeft="8dip"
android:paddingRight="8dip">
-
+
-
+
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/List7.java b/samples/ApiDemos/src/com/example/android/apis/view/List7.java
index d44ed5672..e773db62a 100644
--- a/samples/ApiDemos/src/com/example/android/apis/view/List7.java
+++ b/samples/ApiDemos/src/com/example/android/apis/view/List7.java
@@ -16,15 +16,12 @@
package com.example.android.apis.view;
-// 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.ListActivity;
import android.database.Cursor;
-import android.provider.Contacts.People;
import android.os.Bundle;
+import android.provider.ContactsContract;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
@@ -36,46 +33,69 @@ import android.widget.TextView;
* A list view example where the data comes from a cursor.
*/
public class List7 extends ListActivity implements OnItemSelectedListener {
- private static String[] PROJECTION = new String[] {
- People._ID, People.NAME, People.NUMBER
+ private static final String[] PROJECTION = new String[] {
+ ContactsContract.Contacts._ID,
+ ContactsContract.Contacts.DISPLAY_NAME,
+ ContactsContract.Contacts.HAS_PHONE_NUMBER,
+ ContactsContract.Contacts.LOOKUP_KEY
};
+ private int mIdColumnIndex;
+ private int mHasPhoneColumnIndex;
+
+ private TextView mPhone;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
setContentView(R.layout.list_7);
+
mPhone = (TextView) findViewById(R.id.phone);
getListView().setOnItemSelectedListener(this);
// Get a cursor with all people
- Cursor c = getContentResolver().query(People.CONTENT_URI, PROJECTION, null, null, null);
- startManagingCursor(c);
- mPhoneColumnIndex = c.getColumnIndex(People.NUMBER);
+ Cursor c = managedQuery(ContactsContract.Contacts.CONTENT_URI,
+ PROJECTION, null, null, null);
+ mIdColumnIndex = c.getColumnIndex(ContactsContract.Contacts._ID);
+ mHasPhoneColumnIndex = c.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER);
ListAdapter adapter = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_1, // Use a template
- // that displays a
- // text view
- c, // Give the cursor to the list adatper
- new String[] {People.NAME}, // Map the NAME column in the
- // people database to...
- new int[] {android.R.id.text1}); // The "text1" view defined in
- // the XML template
+ // that displays a
+ // text view
+ c, // Give the cursor to the list adapter
+ new String[] { ContactsContract.Contacts.DISPLAY_NAME }, // Map the NAME column in the
+ // people database to...
+ new int[] { android.R.id.text1 }); // The "text1" view defined in
+ // the XML template
setListAdapter(adapter);
}
public void onItemSelected(AdapterView parent, View v, int position, long id) {
if (position >= 0) {
- Cursor c = (Cursor) parent.getItemAtPosition(position);
- mPhone.setText(c.getString(mPhoneColumnIndex));
+ final Cursor c = (Cursor) parent.getItemAtPosition(position);
+ if (c.getInt(mHasPhoneColumnIndex) > 0) {
+ final long contactId = c.getLong(mIdColumnIndex);
+ final Cursor phones = getContentResolver().query(
+ ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
+ new String[] { ContactsContract.CommonDataKinds.Phone.NUMBER },
+ ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + contactId, null,
+ ContactsContract.CommonDataKinds.Phone.IS_SUPER_PRIMARY + " DESC");
+
+ try {
+ phones.moveToFirst();
+ mPhone.setText(phones.getString(0));
+ } finally {
+ phones.close();
+ }
+ } else {
+ mPhone.setText(R.string.list_7_nothing);
+ }
}
}
public void onNothingSelected(AdapterView parent) {
mPhone.setText(R.string.list_7_nothing);
-
}
-
- private int mPhoneColumnIndex;
- private TextView mPhone;
}
From 5a3963887519fcfe1f7e8b1dfe552cb3977fad84 Mon Sep 17 00:00:00 2001
From: Xavier Ducrohet
Date: Thu, 10 Dec 2009 16:54:52 -0800
Subject: [PATCH 13/17] Add new samples to the SDK.
Change-Id: I9fb303e6b6f6fb00172f4f5bf64a2c56649c2491
---
build/sdk.atree | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/build/sdk.atree b/build/sdk.atree
index 6696deb51..f03d35484 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -83,6 +83,10 @@ development/samples/Snake platforms/${PLATFORM_NAME}/samples/Snake
development/samples/SoftKeyboard platforms/${PLATFORM_NAME}/samples/SoftKeyboard
development/samples/JetBoy platforms/${PLATFORM_NAME}/samples/JetBoy
development/samples/SearchableDictionary platforms/${PLATFORM_NAME}/samples/SearchableDictionary
+development/samples/ContactManager platforms/${PLATFORM_NAME}/samples/ContactManager
+development/samples/MultiResolution platforms/${PLATFORM_NAME}/samples/MultiResolution
+development/samples/Wiktionary platforms/${PLATFORM_NAME}/samples/Wiktionary
+development/samples/WiktionarySimple platforms/${PLATFORM_NAME}/samples/WiktionarySimple
# dx
bin/dx platforms/${PLATFORM_NAME}/tools/dx
From f2519b444f658d2d7af8f85b0b95d12f32cf8bd2 Mon Sep 17 00:00:00 2001
From: Tom O'Neill
Date: Fri, 11 Dec 2009 11:25:36 -0800
Subject: [PATCH 14/17] Add Android.mk files for the Notepad tutorial's
solutions
Now the tutorial is built as part of the standard build, but not installed
as part of the system image. Required localizing an android:text field
that was already localized in Notepadv1 but for some odd reason was no
longer localized in v2 and v3.
Change-Id: I0e1b41a4efa454a503b788b0698593136662a014
---
.../Notepadv1Solution/Android.mk | 30 +++++++++++++++++++
.../Notepadv2/res/layout/notes_list.xml | 2 +-
.../Notepadv2Solution/Android.mk | 30 +++++++++++++++++++
.../res/layout/notes_list.xml | 2 +-
.../Notepadv3/res/layout/notes_list.xml | 2 +-
.../Notepadv3Solution/Android.mk | 30 +++++++++++++++++++
.../res/layout/notes_list.xml | 2 +-
7 files changed, 94 insertions(+), 4 deletions(-)
create mode 100644 tutorials/NotepadCodeLab/Notepadv1Solution/Android.mk
create mode 100644 tutorials/NotepadCodeLab/Notepadv2Solution/Android.mk
create mode 100644 tutorials/NotepadCodeLab/Notepadv3Solution/Android.mk
diff --git a/tutorials/NotepadCodeLab/Notepadv1Solution/Android.mk b/tutorials/NotepadCodeLab/Notepadv1Solution/Android.mk
new file mode 100644
index 000000000..537503105
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv1Solution/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2009 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := Notepadv1Solution
+
+# Make the app build against the current SDK
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/tutorials/NotepadCodeLab/Notepadv2/res/layout/notes_list.xml b/tutorials/NotepadCodeLab/Notepadv2/res/layout/notes_list.xml
index 29ae88631..6ae04729e 100755
--- a/tutorials/NotepadCodeLab/Notepadv2/res/layout/notes_list.xml
+++ b/tutorials/NotepadCodeLab/Notepadv2/res/layout/notes_list.xml
@@ -9,5 +9,5 @@
+ android:text="@string/no_notes"/>
diff --git a/tutorials/NotepadCodeLab/Notepadv2Solution/Android.mk b/tutorials/NotepadCodeLab/Notepadv2Solution/Android.mk
new file mode 100644
index 000000000..8e483519b
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv2Solution/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2009 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := Notepadv2Solution
+
+# Make the app build against the current SDK
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/tutorials/NotepadCodeLab/Notepadv2Solution/res/layout/notes_list.xml b/tutorials/NotepadCodeLab/Notepadv2Solution/res/layout/notes_list.xml
index 29ae88631..6ae04729e 100755
--- a/tutorials/NotepadCodeLab/Notepadv2Solution/res/layout/notes_list.xml
+++ b/tutorials/NotepadCodeLab/Notepadv2Solution/res/layout/notes_list.xml
@@ -9,5 +9,5 @@
+ android:text="@string/no_notes"/>
diff --git a/tutorials/NotepadCodeLab/Notepadv3/res/layout/notes_list.xml b/tutorials/NotepadCodeLab/Notepadv3/res/layout/notes_list.xml
index 29ae88631..6ae04729e 100755
--- a/tutorials/NotepadCodeLab/Notepadv3/res/layout/notes_list.xml
+++ b/tutorials/NotepadCodeLab/Notepadv3/res/layout/notes_list.xml
@@ -9,5 +9,5 @@
+ android:text="@string/no_notes"/>
diff --git a/tutorials/NotepadCodeLab/Notepadv3Solution/Android.mk b/tutorials/NotepadCodeLab/Notepadv3Solution/Android.mk
new file mode 100644
index 000000000..177164ed8
--- /dev/null
+++ b/tutorials/NotepadCodeLab/Notepadv3Solution/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2009 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := Notepadv3Solution
+
+# Make the app build against the current SDK
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/tutorials/NotepadCodeLab/Notepadv3Solution/res/layout/notes_list.xml b/tutorials/NotepadCodeLab/Notepadv3Solution/res/layout/notes_list.xml
index 29ae88631..6ae04729e 100755
--- a/tutorials/NotepadCodeLab/Notepadv3Solution/res/layout/notes_list.xml
+++ b/tutorials/NotepadCodeLab/Notepadv3Solution/res/layout/notes_list.xml
@@ -9,5 +9,5 @@
+ android:text="@string/no_notes"/>
From ecef8139decf8ff101c6bea2c7c89b4e3ff8ca38 Mon Sep 17 00:00:00 2001
From: vchtchetkine
Date: Fri, 11 Dec 2009 07:14:38 -0800
Subject: [PATCH 15/17] Fixed
---
host/windows/usb/android_winusb.inf | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/host/windows/usb/android_winusb.inf b/host/windows/usb/android_winusb.inf
index f88af7e97..54f7aa2e1 100755
--- a/host/windows/usb/android_winusb.inf
+++ b/host/windows/usb/android_winusb.inf
@@ -34,11 +34,14 @@ HKR,,Icon,,-1
%CompositeAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C03&MI_01
;
;Moto Sholes
-%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_D00D
-%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_0002
-%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_0002&MI_01
%SingleAdbInterface% = USB_Install, USB\VID_22B8&PID_41DB
%CompositeAdbInterface% = USB_Install, USB\VID_22B8&PID_41DB&MI_01
+;
+;Google NexusOne
+%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_0D02
+%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_0D02&MI_01
+%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_4E11
+%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E12&MI_01
[Google.NTamd64]
; HTC Dream
@@ -49,11 +52,14 @@ HKR,,Icon,,-1
%CompositeAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C03&MI_01
;
;Moto Sholes
-%SingleBootLoaderInterface% = USB_Install, USB\VID_18D1&PID_D00D
-%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_0002
-%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_0002&MI_01
%SingleAdbInterface% = USB_Install, USB\VID_22B8&PID_41DB
%CompositeAdbInterface% = USB_Install, USB\VID_22B8&PID_41DB&MI_01
+;
+;Google NexusOne
+%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_0D02
+%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_0D02&MI_01
+%SingleAdbInterface% = USB_Install, USB\VID_18D1&PID_4E11
+%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E12&MI_01
[USB_Install]
Include = winusb.inf
From d0225ee63f305ef59e17f8ad0dc16b2ef5e85af0 Mon Sep 17 00:00:00 2001
From: Dan Egnor
Date: Wed, 9 Dec 2009 16:30:23 -0800
Subject: [PATCH 16/17] Remove the old Dev Tools exception browser (since it
relies on now eliminated checkin database crash storage functionality).
Add a new test app to Dev Tools, "Bad Behavior", which has
buttons to crash and generate an ANR on command.
Update the Monkey to follow changed APIs.
---
apps/Development/AndroidManifest.xml | 33 ++--
apps/Development/res/layout/bad_behavior.xml | 37 ++++
apps/Development/res/values/strings.xml | 5 +
.../development/BadBehaviorActivity.java | 63 +++++++
.../android/development/ExceptionBrowser.java | 171 ------------------
.../android/development/StacktraceViewer.java | 50 -----
.../com/android/commands/monkey/Monkey.java | 28 +--
7 files changed, 131 insertions(+), 256 deletions(-)
create mode 100644 apps/Development/res/layout/bad_behavior.xml
create mode 100644 apps/Development/src/com/android/development/BadBehaviorActivity.java
delete mode 100644 apps/Development/src/com/android/development/ExceptionBrowser.java
delete mode 100644 apps/Development/src/com/android/development/StacktraceViewer.java
diff --git a/apps/Development/AndroidManifest.xml b/apps/Development/AndroidManifest.xml
index 31427a59a..07a4213b3 100644
--- a/apps/Development/AndroidManifest.xml
+++ b/apps/Development/AndroidManifest.xml
@@ -58,13 +58,6 @@
-
-
-
-
-
-
-
@@ -128,44 +121,50 @@
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
+
+
+
+
+
+
diff --git a/apps/Development/res/layout/bad_behavior.xml b/apps/Development/res/layout/bad_behavior.xml
new file mode 100644
index 000000000..62a724502
--- /dev/null
+++ b/apps/Development/res/layout/bad_behavior.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/Development/res/values/strings.xml b/apps/Development/res/values/strings.xml
index 350f74db7..cb41087f8 100644
--- a/apps/Development/res/values/strings.xml
+++ b/apps/Development/res/values/strings.xml
@@ -193,4 +193,9 @@
Bind failedWaiting for service to be connected...Select account to sync
+
+
+ Crash the main thread
+ Crash an auxiliary thread
+ Stop responding for 20 seconds (ANR)
diff --git a/apps/Development/src/com/android/development/BadBehaviorActivity.java b/apps/Development/src/com/android/development/BadBehaviorActivity.java
new file mode 100644
index 000000000..b8f47216a
--- /dev/null
+++ b/apps/Development/src/com/android/development/BadBehaviorActivity.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 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.android.development;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.Button;
+import android.view.View;
+
+public class BadBehaviorActivity extends Activity {
+ static class BadBehaviorException extends RuntimeException {
+ BadBehaviorException() {
+ super("Whatcha gonna do, whatcha gonna do",
+ new IllegalStateException("When they come for you"));
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.bad_behavior);
+
+ Button crash_main = (Button) findViewById(R.id.bad_behavior_crash_main);
+ crash_main.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) { throw new BadBehaviorException(); }
+ });
+
+ Button crash_thread = (Button) findViewById(R.id.bad_behavior_crash_thread);
+ crash_thread.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ new Thread() {
+ @Override
+ public void run() { throw new BadBehaviorException(); }
+ }.start();
+ }
+ });
+
+ Button anr = (Button) findViewById(R.id.bad_behavior_anr);
+ anr.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ try {
+ Thread.sleep(20000);
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ });
+ }
+}
diff --git a/apps/Development/src/com/android/development/ExceptionBrowser.java b/apps/Development/src/com/android/development/ExceptionBrowser.java
deleted file mode 100644
index 63270aab5..000000000
--- a/apps/Development/src/com/android/development/ExceptionBrowser.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
-** Copyright 2007, 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.android.development;
-
-import android.app.ListActivity;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.database.ContentObserver;
-import android.database.DataSetObserver;
-import android.graphics.Typeface;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.provider.Checkin;
-import android.server.data.CrashData;
-import android.server.data.ThrowableData;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.KeyEvent;
-import android.widget.*;
-
-import org.apache.commons.codec.binary.Base64;
-
-import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
-import java.io.IOException;
-
-/**
- *
- *
- */
-public class ExceptionBrowser extends ListActivity {
- /** Logging identifier. */
- private static final String TAG = "ExceptionBrowser";
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- Cursor cursor = getContentResolver().query(
- Checkin.Crashes.CONTENT_URI,
- new String[] { Checkin.Crashes._ID, Checkin.Crashes.DATA },
- null, null, null);
-
- if (cursor != null) {
- startManagingCursor(cursor);
-
- setListAdapter(new CursorAdapter(this, cursor, true) {
- public View newView(Context context, Cursor c, ViewGroup v) {
- return new CrashListItem(context);
- }
-
- public void bindView(View view, Context c, Cursor cursor) {
- CrashListItem item = (CrashListItem) view;
- try {
- String data = cursor.getString(1);
- CrashData crash = new CrashData(
- new DataInputStream(
- new ByteArrayInputStream(
- Base64.decodeBase64(data.getBytes()))));
-
- ThrowableData exc = crash.getThrowableData();
- item.setText(exc.getType() + ": " + exc.getMessage());
- item.setCrashData(crash);
- } catch (IOException e) {
- item.setText("Invalid crash: " + e);
- Log.e(TAG, "Invalid crash", e);
- }
- }
- });
- } else {
- // No database, no exceptions, empty list.
- setListAdapter(new BaseAdapter() {
- public int getCount() {
- return 0;
- }
-
- public Object getItem(int position) {
- throw new AssertionError();
- }
-
- public long getItemId(int position) {
- throw new AssertionError();
- }
-
- public View getView(int position, View convertView,
- ViewGroup parent) {
- throw new AssertionError();
- }
- });
- }
- }
-
- private static final int UPLOAD_ID = Menu.FIRST;
- private static final int CLEAR_ID = Menu.FIRST + 1;
-
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
-
- menu.add(0, UPLOAD_ID, 0, R.string.menu_upload_exceptions);
- menu.add(0, CLEAR_ID, 0, R.string.menu_clear_exceptions);
-
- return true;
- }
-
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- // Handle all of the possible menu actions.
- switch (item.getItemId()) {
- case UPLOAD_ID:
- sendBroadcast(new Intent(Checkin.TriggerIntent.ACTION));
- break;
- case CLEAR_ID:
- getContentResolver().delete(
- Checkin.Crashes.CONTENT_URI, null, null);
- break;
- }
- return super.onOptionsItemSelected(item);
- }
-
- static class CrashListItem extends TextView {
- CrashData crashData = null;
-
- public CrashListItem(Context context) {
- super(context);
- setTextSize(10);
- setTypeface(Typeface.MONOSPACE);
- }
-
- public CrashData getCrashData() {
- return crashData;
- }
-
- public void setCrashData(CrashData crashData) {
- this.crashData = crashData;
- }
- }
-
- @Override
- protected void onListItemClick(ListView l, View view, int pos, long id) {
- // TODO: Use a generic VIEW action on the crash's content URI.
- CrashData crash = ((CrashListItem) view).getCrashData();
- if (crash != null) {
- Intent intent = new Intent();
- intent.setClass(this, StacktraceViewer.class);
- intent.putExtra(
- CrashData.class.getName(),
- crash.getThrowableData().toString());
- startActivity(intent);
- }
- }
-}
diff --git a/apps/Development/src/com/android/development/StacktraceViewer.java b/apps/Development/src/com/android/development/StacktraceViewer.java
deleted file mode 100644
index 96b052129..000000000
--- a/apps/Development/src/com/android/development/StacktraceViewer.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
-** Copyright 2007, 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.android.development;
-
-
-import android.app.Activity;
-import android.widget.EditText;
-import android.widget.TextView;
-import android.text.method.ScrollingMovementMethod;
-import android.os.Bundle;
-import android.server.data.CrashData;
-import android.server.data.ThrowableData;
-import android.server.data.StackTraceElementData;
-import android.graphics.Typeface;
-
-/**
- * Views a single stack trace.
- */
-public class StacktraceViewer extends Activity {
-
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setContentView(R.layout.log_viewer);
-
- TextView text = (TextView) findViewById(R.id.text);
- text.setTextSize(10);
- text.setHorizontallyScrolling(true);
- text.setTypeface(Typeface.MONOSPACE);
- text.setMovementMethod(ScrollingMovementMethod.getInstance());
-
- String stacktrace = getIntent().getExtras().getString(
- CrashData.class.getName());
-
- text.setText(stacktrace);
- }
-}
diff --git a/cmds/monkey/src/com/android/commands/monkey/Monkey.java b/cmds/monkey/src/com/android/commands/monkey/Monkey.java
index bb0663fd3..19f6d8be2 100644
--- a/cmds/monkey/src/com/android/commands/monkey/Monkey.java
+++ b/cmds/monkey/src/com/android/commands/monkey/Monkey.java
@@ -23,11 +23,11 @@ import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.ResolveInfo;
+import android.os.Build;
import android.os.Debug;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.server.data.CrashData;
import android.view.IWindowManager;
import java.io.BufferedReader;
@@ -209,26 +209,18 @@ public class Monkey {
}
}
- public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg,
- byte[] crashData) {
+ public boolean appCrashed(String processName, int pid,
+ String tag, String shortMsg, String longMsg,
+ long timeMillis, String stackTrace) {
System.err.println("// CRASH: " + processName + " (pid " + pid + ")");
System.err.println("// Short Msg: " + shortMsg);
System.err.println("// Long Msg: " + longMsg);
- if (crashData != null) {
- try {
- CrashData cd = new CrashData(new DataInputStream(new ByteArrayInputStream(
- crashData)));
- System.err.println("// Build Label: " + cd.getBuildData().getFingerprint());
- System.err.println("// Build Changelist: "
- + cd.getBuildData().getIncrementalVersion());
- System.err.println("// Build Time: " + cd.getBuildData().getTime());
- System.err.println("// ID: " + cd.getId());
- System.err.println("// Tag: " + cd.getActivity());
- System.err.println(cd.getThrowableData().toString("// "));
- } catch (IOException e) {
- System.err.println("// BAD STACK CRAWL");
- }
- }
+ System.err.println("// Build Label: " + Build.FINGERPRINT);
+ System.err.println("// Build Changelist: " + Build.VERSION.INCREMENTAL);
+ System.err.println("// Build Time: " + Build.TIME);
+ System.err.println("// ID: "); // TODO: This was never set -- remove?
+ System.err.println("// Tag: " + tag);
+ System.err.println("// " + stackTrace.replace("\n", "\n// "));
if (!mIgnoreCrashes) {
synchronized (Monkey.this) {
From 7c21d1cd4051a7aaab5c15d63649315708c6aaa4 Mon Sep 17 00:00:00 2001
From: Tom O'Neill
Date: Fri, 11 Dec 2009 16:01:04 -0800
Subject: [PATCH 17/17] Fix Notepadv3Solution orientation changes during
NoteEdit
The current NoteEdit.java in Notepadv3Solution crashes on NPE when the
screen orientation changes. This happens because a null Long is
auto-unboxed to a long when used as an input to
Bundle.putLong(String, long). The easiest solution is to use
Bundle.putSerializable() instead.
In addition duplicate notepad entries could result because mRowId was not
necessarily defined when onSaveInstanceState(Bundle) was called. The
solution to that is to call saveState() in that method.
Fixes these buganizer bugs:
Change-Id: Ice325f3b089867e4716deb48aefe8ec03f30ad55
http://b/issue?id=2266994
http://b/issue?id=2266962
---
.../src/com/android/demo/notepad3/NoteEdit.java | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NoteEdit.java b/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NoteEdit.java
index 710ea339d..f5eb6c433 100755
--- a/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NoteEdit.java
+++ b/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NoteEdit.java
@@ -43,8 +43,8 @@ public class NoteEdit extends Activity {
Button confirmButton = (Button) findViewById(R.id.confirm);
- mRowId = savedInstanceState != null ? savedInstanceState.getLong(NotesDbAdapter.KEY_ROWID)
- : null;
+ mRowId = (savedInstanceState == null) ? null :
+ (Long) savedInstanceState.getSerializable(NotesDbAdapter.KEY_ROWID);
if (mRowId == null) {
Bundle extras = getIntent().getExtras();
mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID)
@@ -77,7 +77,8 @@ public class NoteEdit extends Activity {
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- outState.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
+ saveState();
+ outState.putSerializable(NotesDbAdapter.KEY_ROWID, mRowId);
}
@Override