Merge commit 'goog/eclair-mr2' into play-with-monkey

Conflicts:

	cmds/monkey/src/com/android/commands/monkey/Monkey.java
This commit is contained in:
Ying Wang
2009-12-14 16:23:32 -08:00
134 changed files with 6783 additions and 317 deletions

View File

@@ -58,13 +58,6 @@
<category android:name="android.intent.category.TEST" />
</intent-filter>
</activity>
<activity android:name="ExceptionBrowser" android:label="Exception Browser">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.TEST" />
</intent-filter>
</activity>
<activity android:name="StacktraceViewer" android:label="Stacktrace Viewer"/>
<activity android:name="PackageSummary" android:label="Package Summary">
</activity>
<activity android:name="ShowActivity" android:label="Activity">
@@ -167,5 +160,11 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name="BadBehaviorActivity" android:label="Bad Behavior">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.TEST" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button android:id="@+id/bad_behavior_crash_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bad_behavior_crash_main_label" />
<Button android:id="@+id/bad_behavior_crash_thread"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bad_behavior_crash_thread_label" />
<Button android:id="@+id/bad_behavior_anr"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bad_behavior_anr_label" />
</LinearLayout>

View File

@@ -193,4 +193,9 @@
<string name="binding_bind_failed">Bind failed</string>
<string name="binding_waiting_for_connection">Waiting for service to be connected...</string>
<string name="select_account_to_sync">Select account to sync</string>
<!-- BadBehaviorActivity -->
<string name="bad_behavior_crash_main_label">Crash the main thread</string>
<string name="bad_behavior_crash_thread_label">Crash an auxiliary thread</string>
<string name="bad_behavior_anr_label">Stop responding for 20 seconds (ANR)</string>
</resources>

View File

@@ -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);
}
}
});
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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;
@@ -225,26 +225,18 @@ public class Monkey {
return allow;
}
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) {

View File

@@ -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

View File

@@ -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:

View File

@@ -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.

View File

@@ -553,6 +553,15 @@
</intent-filter>
</activity>
<!-- Text-To-Speech Samples -->
<activity android:name=".app.TextToSpeechActivity" android:label="@string/text_to_speech">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
<!-- ************************************* -->
<!-- CONTENT PACKAGE SAMPLES -->
<!-- ************************************* -->
@@ -1407,6 +1416,25 @@
</intent-filter>
</activity>
<activity android:name=".graphics.GLES20Activity"
android:label="Graphics/OpenGL ES/OpenGL ES 2.0"
android:theme="@android:style/Theme.NoTitleBar"
android:configChanges="orientation|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
<activity android:name=".graphics.MatrixPaletteActivity"
android:label="Graphics/OpenGL ES/Matrix Palette Skinning"
android:configChanges="orientation|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
<activity android:name=".graphics.TranslucentGLSurfaceViewActivity"
android:label="Graphics/OpenGL ES/Translucent GLSurfaceView"
android:theme="@style/Theme.Translucent"

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<Button android:id="@+id/again_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:enabled="false"
android:text="@string/again" />
</LinearLayout>

View File

@@ -221,6 +221,9 @@
<string name="voice_recognition">App/Voice Recognition</string>
<string name="text_to_speech">App/Text-To-Speech</string>
<string name="again">Again</string>
<!-- ============================== -->
<!-- app/content examples strings -->
<!-- ============================== -->

View File

@@ -0,0 +1,136 @@
/*
* 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.app;
import android.app.Activity;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.example.android.apis.R;
import java.util.Locale;
import java.util.Random;
/**
* <p>Demonstrates text-to-speech (TTS). Please note the following steps:</p>
*
* <ol>
* <li>Construct the TextToSpeech object.</li>
* <li>Handle initialization callback in the onInit method.
* The activity implements TextToSpeech.OnInitListener for this purpose.</li>
* <li>Call TextToSpeech.speak to synthesize speech.</li>
* <li>Shutdown TextToSpeech in onDestroy.</li>
* </ol>
*
* <p>Documentation:
* http://developer.android.com/reference/android/speech/tts/package-summary.html
* </p>
* <ul>
*/
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);
}
}

View File

@@ -0,0 +1,72 @@
/*
* 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 android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ConfigurationInfo;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
/**
* This sample shows how to check for OpenGL ES 2.0 support at runtime, and then
* use either OpenGL ES 1.0 or OpenGL ES 2.0, as appropriate.
*/
public class GLES20Activity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLSurfaceView = new GLSurfaceView(this);
if (detectOpenGLES20()) {
// Tell the surface view we want to create an OpenGL ES 2.0-compatible
// context, and set an OpenGL ES 2.0-compatible renderer.
mGLSurfaceView.setEGLContextClientVersion(2);
mGLSurfaceView.setRenderer(new GLES20TriangleRenderer(this));
} else {
// Set an OpenGL ES 1.x-compatible renderer. In a real application
// this renderer might approximate the same output as the 2.0 renderer.
mGLSurfaceView.setRenderer(new TriangleRenderer(this));
}
setContentView(mGLSurfaceView);
}
private boolean detectOpenGLES20() {
ActivityManager am =
(ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ConfigurationInfo info = am.getDeviceConfigurationInfo();
return (info.reqGlEsVersion >= 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;
}

View File

@@ -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";
}

View File

@@ -0,0 +1,52 @@
/*
* 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 android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
/**
* This sample shows how to implement a Matrix Palette
*/
public class MatrixPaletteActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLSurfaceView = new GLSurfaceView(this);
mGLSurfaceView.setRenderer(new MatrixPaletteRenderer(this));
setContentView(mGLSurfaceView);
}
@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;
}

View File

@@ -0,0 +1,408 @@
/*
* 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.CharBuffer;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11Ext;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import android.opengl.GLUtils;
import android.os.SystemClock;
import com.example.android.apis.R;
public class MatrixPaletteRenderer implements GLSurfaceView.Renderer{
private Context mContext;
private Grid mGrid;
private int mTextureID;
/** A grid is a topologically rectangular array of vertices.
*
* This grid class is customized for the vertex data required for this
* example.
*
* The vertex and index data are held in VBO objects because on most
* GPUs VBO objects are the fastest way of rendering static vertex
* and index data.
*
*/
private static class Grid {
// Size of vertex data elements in bytes:
final static int FLOAT_SIZE = 4;
final static int CHAR_SIZE = 2;
// Vertex structure:
// float x, y, z;
// float u, v;
// float weight0, weight1;
// byte palette0, palette1, pad0, pad1;
final static int VERTEX_SIZE = 8 * FLOAT_SIZE;
final static int VERTEX_TEXTURE_BUFFER_INDEX_OFFSET = 3;
final static int VERTEX_WEIGHT_BUFFER_INDEX_OFFSET = 5;
final static int VERTEX_PALETTE_INDEX_OFFSET = 7 * FLOAT_SIZE;
private int mVertexBufferObjectId;
private int mElementBufferObjectId;
// These buffers are used to hold the vertex and index data while
// constructing the grid. Once createBufferObjects() is called
// the buffers are nulled out to save memory.
private ByteBuffer mVertexByteBuffer;
private FloatBuffer mVertexBuffer;
private CharBuffer mIndexBuffer;
private int mW;
private int mH;
private int mIndexCount;
public Grid(int w, int h) {
if (w < 0 || w >= 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;
}
}

View File

@@ -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,28 +33,39 @@ 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
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
@@ -66,16 +74,28 @@ public class List7 extends ListActivity implements OnItemSelectedListener {
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;
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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());

View File

@@ -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))

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.example.android.contactmanager"
android:versionCode="1" android:versionName="1.0">
<application android:label="@string/app_name" android:icon="@drawable/icon">
<activity android:name=".ContactManager" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="ContactAdder" android:label="@string/addContactTitle">
</activity>
</application>
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="5" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
</manifest>

View File

@@ -0,0 +1,10 @@
<p>A sample application that demonstrates how to manually query the system contacts provider using
the new <code><a
href="../../../reference/android/provider/ContactsContract.html">ContactsContract</a></code> API, as
well as manually insert contacts into a specific account.</p>
<p>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.</p>
<img alt="Screenshot 1" src="../images/ContactManager1.png" />
<img alt="Screenshot 2" src="../images/ContactManager2.png" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:padding="6dip">
<ImageView
android:id="@+id/accountIcon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_marginRight="6dip" />
<TextView
android:id="@+id/secondAccountLine"
android:layout_width="fill_parent"
android:layout_height="26dip"
android:layout_toRightOf="@id/accountIcon"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:singleLine="true"
android:ellipsize="marquee"
android:textColor="@android:color/secondary_text_light" />
<TextView
android:id="@+id/firstAccountLine"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/accountIcon"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_above="@id/secondAccountLine"
android:layout_alignWithParentIfMissing="true"
android:gravity="center_vertical"
android:textColor="@android:color/primary_text_light"/>
</RelativeLayout>

View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TableLayout android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TableRow>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/targetAccountLabel"/>
</TableRow>
<TableRow>
<Spinner android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="1"
android:id="@+id/accountSpinner"/>
</TableRow>
<TableRow>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/contactNameLabel"/>
</TableRow>
<TableRow>
<EditText android:id="@+id/contactNameEditText"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_weight="1"/>
</TableRow>
<TableRow>
<TextView android:text="@string/contactPhoneLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</TableRow>
<TableRow>
<EditText android:id="@+id/contactPhoneEditText"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_weight="1"/>
<Spinner android:id="@+id/contactPhoneTypeSpinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</TableRow>
<TableRow>
<TextView android:text="@string/contactEmailLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</TableRow>
<TableRow>
<EditText android:id="@+id/contactEmailEditText"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_weight="1"/>
<Spinner android:id="@+id/contactEmailTypeSpinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</TableRow>
<TableRow>
<Button android:layout_height="wrap_content"
android:text="@string/save"
android:id="@+id/contactSaveButton"
android:layout_width="fill_parent"
android:layout_weight="1"/>
</TableRow>
</TableLayout>
</ScrollView>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView android:text="@+id/contactEntryText"
android:id="@+id/contactEntryText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView android:layout_width="fill_parent"
android:id="@+id/contactList"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<CheckBox android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/showInvisible"
android:text="@string/showInvisible"/>
<Button android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/addContactButton"
android:text="@string/addContactButtonLabel"/>
</LinearLayout>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<resources>
<string name="accountSpinnerLabel">Account</string>
<string name="addContactButtonLabel">Add Contact</string>
<string name="addContactTitle">Add Contact</string>
<string name="allAccounts">All Accounts</string>
<string name="app_name">Contact Manager</string>
<string name="contactCreationFailure">Contact creation failed, check logs.</string>
<string name="contactEmailLabel">Contact Email</string>
<string name="contactNameLabel">Contact Name</string>
<string name="contactPhoneLabel">Contact Phone</string>
<string name="save">Save</string>
<string name="selectAccountLabel">Select</string>
<string name="selectLabel">Select label</string>
<string name="showInvisible">Show Invisible Contacts (Only)</string>
<string name="targetAccountLabel">Target Account</string>
<string name="undefinedTypeLabel">(Undefined)</string>
</resources>

View File

@@ -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<AccountData> mAccounts;
private AccountAdapter mAccountAdapter;
private Spinner mAccountSpinner;
private EditText mContactEmailEditText;
private ArrayList<Integer> mContactEmailTypes;
private Spinner mContactEmailTypeSpinner;
private EditText mContactNameEditText;
private EditText mContactPhoneEditText;
private ArrayList<Integer> 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<Integer>();
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<Integer>();
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<AccountData>();
mAccountAdapter = new AccountAdapter(this, mAccounts);
mAccountSpinner.setAdapter(mAccountAdapter);
// Populate list of account types for phone
ArrayAdapter<String> adapter;
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Iterator<Integer> 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<String>(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<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
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<AccountData> {
public AccountAdapter(Context context, ArrayList<AccountData> 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;
}
}
}

View File

@@ -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._ID,
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);
}
}

View File

@@ -17,4 +17,4 @@ It demonstrates using:
</ul>
</p>
<img alt="" src="../images/sample_multires.png"/>
<img alt="" src="../images/MultiResolution.png"/>

View File

@@ -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))

View File

@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.wiktionary"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/app_icon" android:label="@string/app_name"
android:description="@string/app_descrip">
<!-- Browser-like Activity to navigate dictionary definitions -->
<activity
android:name=".LookupActivity"
android:theme="@style/LookupTheme"
android:launchMode="singleTop"
android:configChanges="orientation|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="wiktionary" android:host="lookup" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="android.app.searchable" android:resource="@xml/searchable" />
</activity>
<!-- Broadcast Receiver that will process AppWidget updates -->
<receiver android:name=".WordWidget" android:label="@string/widget_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/widget_word" />
</receiver>
<!-- Service to perform web API queries -->
<service android:name=".WordWidget$UpdateService" />
</application>
<meta-data android:name="android.app.default_searchable" android:value=".LookupActivity" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="4" />
</manifest>

View File

@@ -0,0 +1,10 @@
<p>A sample application that demonstrates how to create an interactive Android
home screen widget that launches an application activity.</p>
<p>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 launch a custom application activity showing
more details.</p>
<img alt="" src="../images/WiktionarySimple.png"/>
<img alt="" src="../images/Wiktionary.png"/>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true">
<translate
android:fromXDelta="-26"
android:toXDelta="0"
android:duration="400" />
</set>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true">
<translate
android:fromXDelta="0"
android:toXDelta="-26"
android:duration="400" />
</set>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<item android:drawable="@drawable/logo_overlay" />
</layer-list>

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:pivotX="50%"
android:pivotY="50%"
android:fromDegrees="0"
android:toDegrees="360">
<shape
android:shape="ring"
android:innerRadiusRatio="4"
android:thicknessRatio="5.333"
android:useLevel="false">
<size
android:width="18dip"
android:height="18dip" />
<gradient
android:type="sweep"
android:useLevel="false"
android:startColor="#006688cc"
android:centerColor="#886688cc"
android:endColor="#ff6688cc"
android:centerY="0.50" />
</shape>
</rotate>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false" android:drawable="@drawable/widget_bg_normal" />
<item android:state_pressed="true" android:drawable="@drawable/widget_bg_pressed" />
<item android:state_focused="true" android:drawable="@drawable/widget_bg_selected" />
<item android:drawable="@drawable/widget_bg_normal" />
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:padding="20dip">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:text="@string/app_descrip"
android:textColor="?android:attr/textColorPrimaryInverse" />
<TextView
android:id="@+id/about_credits"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="20dip"
android:textSize="16sp"
android:text="@string/app_credits"
android:autoLink="web"
android:textColor="?android:attr/textColorPrimaryInverse" />
</LinearLayout>

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/title_bar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<ProgressBar
android:id="@+id/progress"
android:layout_width="18dip"
android:layout_height="18dip"
android:layout_marginLeft="10dip"
android:visibility="invisible"
android:indeterminateOnly="true"
android:indeterminateDrawable="@drawable/progress_spin"
android:indeterminateBehavior="repeat"
android:indeterminateDuration="3500" />
<TextView
android:id="@+id/title"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="10dip"
style="@style/LookupTitle" />
</LinearLayout>
<WebView
android:id="@+id/webview"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1" />
</LinearLayout>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
style="@style/WidgetBackground">
<TextView
android:id="@+id/message"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dip"
android:padding="10dip"
android:gravity="center"
android:text="@string/widget_loading"
style="@style/Text.Loading" />
</LinearLayout>

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:focusable="true"
style="@style/WidgetBackground">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:src="@drawable/star_logo" />
<TextView
android:id="@+id/word_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="14dip"
android:layout_marginBottom="1dip"
android:includeFontPadding="false"
android:singleLine="true"
android:ellipsize="end"
style="@style/Text.WordTitle" />
<TextView
android:id="@+id/word_type"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/word_title"
android:layout_toLeftOf="@id/icon"
android:layout_alignBaseline="@id/word_title"
android:paddingLeft="4dip"
android:includeFontPadding="false"
android:singleLine="true"
android:ellipsize="end"
style="@style/Text.WordType" />
<TextView
android:id="@+id/bullet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/word_title"
android:paddingRight="4dip"
android:includeFontPadding="false"
android:singleLine="true"
style="@style/BulletPoint" />
<TextView
android:id="@+id/definition"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/word_title"
android:layout_toRightOf="@id/bullet"
android:paddingRight="5dip"
android:paddingBottom="4dip"
android:includeFontPadding="false"
android:lineSpacingMultiplier="0.9"
android:maxLines="4"
android:fadingEdge="vertical"
style="@style/Text.Definition" />
</RelativeLayout>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/lookup_search"
android:title="@string/lookup_search"
android:icon="@android:drawable/ic_menu_search" />
<item
android:id="@+id/lookup_random"
android:title="@string/lookup_random"
android:icon="@drawable/ic_menu_shuffle" />
<item
android:id="@+id/lookup_about"
android:title="@string/lookup_about"
android:icon="@android:drawable/ic_menu_help" />
</menu>

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<resources>
<string name="app_name">Wiktionary example</string>
<string name="app_descrip">Example of a fast Wiktionary browser and Word-of-day widget</string>
<string name="app_credits">"All dictionary content provided by Wiktionary under a GFDL license. http://en.wiktionary.org\n\nIcon derived from Tango Desktop Project under a public domain license. http://tango.freedesktop.org"</string>
<string name="template_user_agent">"%s/%s (Linux; Android)"</string>
<string name="template_wotd_title">"Wiktionary:Word of the day/%s %s"</string>
<string name="template_define_url">"http://en.wiktionary.org/wiki/%s"</string>
<string name="widget_name">Wiktionary</string>
<string name="widget_loading">"Loading word\nof day\u2026"</string>
<string name="widget_error">No word of day found</string>
<string-array name="month_names">
<item>January</item>
<item>February</item>
<item>March</item>
<item>April</item>
<item>May</item>
<item>June</item>
<item>July</item>
<item>August</item>
<item>September</item>
<item>October</item>
<item>November</item>
<item>December</item>
</string-array>
<string name="search_label">Wiktionary search</string>
<string name="search_hint">Define word</string>
<string name="lookup_search">Search</string>
<string name="lookup_random">Random</string>
<string name="lookup_about">About</string>
<string name="empty_result">No entry found for this word, or problem reading data.</string>
</resources>

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<resources>
<style name="WidgetBackground">
<item name="android:background">@drawable/widget_bg</item>
</style>
<style name="BulletPoint">
<item name="android:textSize">13sp</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="Text" />
<style name="Text.Loading">
<item name="android:textSize">14sp</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="Text.WordTitle">
<item name="android:textSize">16sp</item>
<item name="android:textStyle">bold</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="Text.WordType">
<item name="android:textSize">14sp</item>
<item name="android:textStyle">italic</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="Text.Definition">
<item name="android:textSize">13sp</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="LookupProgress">
<item name="android:indeterminateOnly">true</item>
<item name="android:indeterminateDrawable">@drawable/progress_spin</item>
<item name="android:indeterminateBehavior">repeat</item>
<item name="android:indeterminateDuration">3500</item>
</style>
<style name="LookupTitle">
<item name="android:textSize">30sp</item>
<item name="android:textStyle">bold</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
</resources>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<resources>
<style name="LookupTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">@drawable/lookup_bg</item>
</style>
</resources>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
android:hint="@string/search_hint" />

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="146dip"
android:minHeight="72dip"
android:updatePeriodMillis="86400000"
android:initialLayout="@layout/widget_message" />

View File

@@ -0,0 +1,278 @@
/*
* 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.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.net.Uri;
import android.text.TextUtils;
import android.webkit.WebView;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Extended version of {@link SimpleWikiHelper}. This version adds methods to
* pick a random word, and to format generic wiki-style text into HTML.
*/
public class ExtendedWikiHelper extends SimpleWikiHelper {
/**
* HTML style sheet to include with any {@link #formatWikiText(String)} HTML
* results. It formats nicely for a mobile screen, and hides some content
* boxes to keep things tidy.
*/
private static final String STYLE_SHEET = "<style>h2 {font-size:1.2em;font-weight:normal;} " +
"a {color:#6688cc;} ol {padding-left:1.5em;} blockquote {margin-left:0em;} " +
".interProject, .noprint {display:none;} " +
"li, blockquote {margin-top:0.5em;margin-bottom:0.5em;}</style>";
/**
* Pattern of section titles we're interested in showing. This trims out
* extra sections that can clutter things up on a mobile screen.
*/
private static final Pattern sValidSections =
Pattern.compile("(verb|noun|adjective|pronoun|interjection)", Pattern.CASE_INSENSITIVE);
/**
* Pattern that can be used to split a returned wiki page into its various
* sections. Doesn't treat children sections differently.
*/
private static final Pattern sSectionSplit =
Pattern.compile("^=+(.+?)=+.+?(?=^=)", Pattern.MULTILINE | Pattern.DOTALL);
/**
* When picking random words in {@link #getRandomWord()}, we sometimes
* encounter special articles or templates. This pattern ignores any words
* like those, usually because they have ":" or other punctuation.
*/
private static final Pattern sInvalidWord = Pattern.compile("[^A-Za-z0-9 ]");
/**
* {@link Uri} authority to use when creating internal links.
*/
public static final String WIKI_AUTHORITY = "wiktionary";
/**
* {@link Uri} host to use when creating internal links.
*/
public static final String WIKI_LOOKUP_HOST = "lookup";
/**
* Mime-type to use when showing parsed results in a {@link WebView}.
*/
public static final String MIME_TYPE = "text/html";
/**
* Encoding to use when showing parsed results in a {@link WebView}.
*/
public static final String ENCODING = "utf-8";
/**
* {@link Uri} to use when requesting a random page.
*/
private static final String WIKTIONARY_RANDOM =
"http://en.wiktionary.org/w/api.php?action=query&list=random&format=json";
/**
* Fake section to insert at the bottom of a wiki response before parsing.
* This ensures that {@link #sSectionSplit} will always catch the last
* section, as it uses section headers in its searching.
*/
private static final String STUB_SECTION = "\n=Stub section=";
/**
* Number of times to try finding a random word in {@link #getRandomWord()}.
* These failures are usually when the found word fails the
* {@link #sInvalidWord} test, or when a network error happens.
*/
private static final int RANDOM_TRIES = 3;
/**
* Internal class to hold a wiki formatting rule. It's mostly a wrapper to
* simplify {@link Matcher#replaceAll(String)}.
*/
private static class FormatRule {
private Pattern mPattern;
private String mReplaceWith;
/**
* Create a wiki formatting rule.
*
* @param pattern Search string to be compiled into a {@link Pattern}.
* @param replaceWith String to replace any found occurances with. This
* string can also include back-references into the given
* pattern.
* @param flags Any flags to compile the {@link Pattern} with.
*/
public FormatRule(String pattern, String replaceWith, int flags) {
mPattern = Pattern.compile(pattern, flags);
mReplaceWith = replaceWith;
}
/**
* Create a wiki formatting rule.
*
* @param pattern Search string to be compiled into a {@link Pattern}.
* @param replaceWith String to replace any found occurances with. This
* string can also include back-references into the given
* pattern.
*/
public FormatRule(String pattern, String replaceWith) {
this(pattern, replaceWith, 0);
}
/**
* Apply this formatting rule to the given input string, and return the
* resulting new string.
*/
public String apply(String input) {
Matcher m = mPattern.matcher(input);
return m.replaceAll(mReplaceWith);
}
}
/**
* List of internal formatting rules to apply when parsing wiki text. These
* include indenting various bullets, apply italic and bold styles, and
* adding internal linking.
*/
private static final List<FormatRule> sFormatRules = new ArrayList<FormatRule>();
static {
// Format header blocks and wrap outside content in ordered list
sFormatRules.add(new FormatRule("^=+(.+?)=+", "</ol><h2>$1</h2><ol>",
Pattern.MULTILINE));
// Indent quoted blocks, handle ordered and bullet lists
sFormatRules.add(new FormatRule("^#+\\*?:(.+?)$", "<blockquote>$1</blockquote>",
Pattern.MULTILINE));
sFormatRules.add(new FormatRule("^#+:?\\*(.+?)$", "<ul><li>$1</li></ul>",
Pattern.MULTILINE));
sFormatRules.add(new FormatRule("^#+(.+?)$", "<li>$1</li>",
Pattern.MULTILINE));
// Add internal links
sFormatRules.add(new FormatRule("\\[\\[([^:\\|\\]]+)\\]\\]",
String.format("<a href=\"%s://%s/$1\">$1</a>", WIKI_AUTHORITY, WIKI_LOOKUP_HOST)));
sFormatRules.add(new FormatRule("\\[\\[([^:\\|\\]]+)\\|([^\\]]+)\\]\\]",
String.format("<a href=\"%s://%s/$1\">$2</a>", WIKI_AUTHORITY, WIKI_LOOKUP_HOST)));
// Add bold and italic formatting
sFormatRules.add(new FormatRule("'''(.+?)'''", "<b>$1</b>"));
sFormatRules.add(new FormatRule("([^'])''([^'].*?[^'])''([^'])", "$1<i>$2</i>$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<String> foundSections = new HashSet<String>();
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;
}
}
}

View File

@@ -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<String> mHistory = new Stack<String>();
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<String, String, String> {
/**
* 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
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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))

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.simplewiktionary"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/app_icon" android:label="@string/app_name">
<!-- Broadcast Receiver that will process AppWidget updates -->
<receiver android:name=".WordWidget" android:label="@string/widget_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/widget_word" />
</receiver>
<!-- Service to perform web API queries -->
<service android:name=".WordWidget$UpdateService" />
</application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="4" />
</manifest>

View File

@@ -0,0 +1,10 @@
<p>A sample application that demonstrates how to create an interactive widget for display on the Android home screen.</p>
<p>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.</p>
<p>A more advanced version of this sample is available in the Wiktionary directory.</p>
<img alt="" src="../images/WiktionarySimple.png"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The stateful background drawable for a widget -->
<item android:state_window_focused="false" android:drawable="@drawable/widget_bg_normal" />
<item android:state_pressed="true" android:drawable="@drawable/widget_bg_pressed" />
<item android:state_focused="true" android:drawable="@drawable/widget_bg_selected" />
<item android:drawable="@drawable/widget_bg_normal" />
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
style="@style/WidgetBackground">
<TextView
android:id="@+id/message"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dip"
android:padding="10dip"
android:gravity="center"
android:text="@string/widget_loading"
style="@style/Text.Loading" />
</LinearLayout>

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:focusable="true"
style="@style/WidgetBackground">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:src="@drawable/star_logo" />
<TextView
android:id="@+id/word_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="14dip"
android:layout_marginBottom="1dip"
android:includeFontPadding="false"
android:singleLine="true"
android:ellipsize="end"
style="@style/Text.WordTitle" />
<TextView
android:id="@+id/word_type"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/word_title"
android:layout_toLeftOf="@id/icon"
android:layout_alignBaseline="@id/word_title"
android:paddingLeft="4dip"
android:includeFontPadding="false"
android:singleLine="true"
android:ellipsize="end"
style="@style/Text.WordType" />
<TextView
android:id="@+id/bullet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/word_title"
android:paddingRight="4dip"
android:includeFontPadding="false"
android:singleLine="true"
style="@style/BulletPoint" />
<TextView
android:id="@+id/definition"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/word_title"
android:layout_toRightOf="@id/bullet"
android:paddingRight="5dip"
android:paddingBottom="4dip"
android:includeFontPadding="false"
android:lineSpacingMultiplier="0.9"
android:maxLines="4"
android:fadingEdge="vertical"
style="@style/Text.Definition" />
</RelativeLayout>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<resources>
<string name="app_name">Wiktionary simple example</string>
<string name="template_user_agent">"%s/%s (Linux; Android)"</string>
<string name="template_wotd_title">"Wiktionary:Word of the day/%s %s"</string>
<string name="template_define_url">"http://en.wiktionary.org/wiki/%s"</string>
<string name="widget_name">Wiktionary simple</string>
<string name="widget_loading">Loading word\nof day\u2026</string>
<string name="widget_error">No word of\nday found</string>
<string-array name="month_names">
<item>January</item>
<item>February</item>
<item>March</item>
<item>April</item>
<item>May</item>
<item>June</item>
<item>July</item>
<item>August</item>
<item>September</item>
<item>October</item>
<item>November</item>
<item>December</item>
</string-array>
</resources>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<resources>
<style name="WidgetBackground">
<item name="android:background">@drawable/widget_bg</item>
</style>
<style name="BulletPoint">
<item name="android:textSize">13sp</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="Text" />
<style name="Text.Loading">
<item name="android:textSize">14sp</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="Text.WordTitle">
<item name="android:textSize">16sp</item>
<item name="android:textStyle">bold</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="Text.WordType">
<item name="android:textSize">14sp</item>
<item name="android:textStyle">italic</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="Text.Definition">
<item name="android:textSize">13sp</item>
<item name="android:textColor">@android:color/black</item>
</style>
</resources>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="146dip"
android:minHeight="72dip"
android:updatePeriodMillis="86400000"
android:initialLayout="@layout/widget_message" />

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.demo.notepad1">
<application android:icon="@drawable/icon">
<activity android:name=".Notepadv1" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</LinearLayout>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Notepad v1</string>
<string name="no_notes">No Notes Yet</string>
</resources>

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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)

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.demo.notepad1">
<application android:icon="@drawable/icon">
<activity android:name=".Notepadv1" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ListView android:id="@id/android:list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView android:id="@id/android:empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/no_notes"/>
</LinearLayout>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView android:id="@+id/text1"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Notepad v1</string>
<string name="no_notes">No Notes Yet</string>
<string name="menu_insert">Add Item</string>
</resources>

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.demo.notepad2">
<application android:icon="@drawable/icon">
<activity android:name=".Notepadv2" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title" />
<EditText android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"/>
</LinearLayout>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/body" />
<EditText android:id="@+id/body" android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:scrollbars="vertical" />
<Button android:id="@+id/confirm"
android:text="@string/confirm"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ListView android:id="@+id/android:list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView android:id="@+id/android:empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/no_notes"/>
</LinearLayout>

Some files were not shown because too many files have changed in this diff Show More