Merge commit 'goog/eclair-mr2' into play-with-monkey
Conflicts: cmds/monkey/src/com/android/commands/monkey/Monkey.java
@@ -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">
|
||||
@@ -128,44 +121,50 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="GLSTester" android:label="Google Login Service">
|
||||
<activity android:name="GLSTester" android:label="Google Login Service">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.TEST" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="RunningProcesses" android:label="Running processes">
|
||||
<activity android:name="RunningProcesses" android:label="Running processes">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.TEST" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="ProcessInfo" android:label="Process Information">
|
||||
<activity android:name="ProcessInfo" android:label="Process Information">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</activity>
|
||||
<!--
|
||||
<activity android:name="AppHwConfigList" android:label="Applications Hw Configuration">
|
||||
<activity android:name="AppHwConfigList" android:label="Applications Hw Configuration">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.TEST" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</activity>
|
||||
-->
|
||||
<activity android:name="AppHwPref" android:label="Applications Hardware Preferences">
|
||||
<activity android:name="AppHwPref" android:label="Applications Hardware Preferences">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="PermissionDetails" android:label="Permission Info">
|
||||
</activity>
|
||||
<activity android:name="PermissionDetails" android:label="Permission Info">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.development.VIEW_PERMISSION" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</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>
|
||||
|
||||
37
apps/Development/res/layout/bad_behavior.xml
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
android:layout_height="fill_parent"
|
||||
android:paddingLeft="8dip"
|
||||
android:paddingRight="8dip">
|
||||
|
||||
|
||||
<ListView android:id="@android:id/list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dip"
|
||||
@@ -31,5 +31,5 @@
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/blue"/>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
26
samples/ApiDemos/res/layout/text_to_speech.xml
Normal 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>
|
||||
@@ -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 -->
|
||||
<!-- ============================== -->
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -16,15 +16,12 @@
|
||||
|
||||
package com.example.android.apis.view;
|
||||
|
||||
// Need the following import to get access to the app resources, since this
|
||||
// class is in a sub-package.
|
||||
import com.example.android.apis.R;
|
||||
|
||||
|
||||
import android.app.ListActivity;
|
||||
import android.database.Cursor;
|
||||
import android.provider.Contacts.People;
|
||||
import android.os.Bundle;
|
||||
import android.provider.ContactsContract;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemSelectedListener;
|
||||
@@ -36,46 +33,69 @@ import android.widget.TextView;
|
||||
* A list view example where the data comes from a cursor.
|
||||
*/
|
||||
public class List7 extends ListActivity implements OnItemSelectedListener {
|
||||
private static String[] PROJECTION = new String[] {
|
||||
People._ID, People.NAME, People.NUMBER
|
||||
private static final String[] PROJECTION = new String[] {
|
||||
ContactsContract.Contacts._ID,
|
||||
ContactsContract.Contacts.DISPLAY_NAME,
|
||||
ContactsContract.Contacts.HAS_PHONE_NUMBER,
|
||||
ContactsContract.Contacts.LOOKUP_KEY
|
||||
};
|
||||
|
||||
private int mIdColumnIndex;
|
||||
private int mHasPhoneColumnIndex;
|
||||
|
||||
private TextView mPhone;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.list_7);
|
||||
|
||||
mPhone = (TextView) findViewById(R.id.phone);
|
||||
getListView().setOnItemSelectedListener(this);
|
||||
|
||||
// Get a cursor with all people
|
||||
Cursor c = getContentResolver().query(People.CONTENT_URI, PROJECTION, null, null, null);
|
||||
startManagingCursor(c);
|
||||
mPhoneColumnIndex = c.getColumnIndex(People.NUMBER);
|
||||
Cursor c = managedQuery(ContactsContract.Contacts.CONTENT_URI,
|
||||
PROJECTION, null, null, null);
|
||||
mIdColumnIndex = c.getColumnIndex(ContactsContract.Contacts._ID);
|
||||
mHasPhoneColumnIndex = c.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER);
|
||||
|
||||
ListAdapter adapter = new SimpleCursorAdapter(this,
|
||||
android.R.layout.simple_list_item_1, // Use a template
|
||||
// that displays a
|
||||
// text view
|
||||
c, // Give the cursor to the list adatper
|
||||
new String[] {People.NAME}, // Map the NAME column in the
|
||||
// people database to...
|
||||
new int[] {android.R.id.text1}); // The "text1" view defined in
|
||||
// the XML template
|
||||
// that displays a
|
||||
// text view
|
||||
c, // Give the cursor to the list adapter
|
||||
new String[] { ContactsContract.Contacts.DISPLAY_NAME }, // Map the NAME column in the
|
||||
// people database to...
|
||||
new int[] { android.R.id.text1 }); // The "text1" view defined in
|
||||
// the XML template
|
||||
setListAdapter(adapter);
|
||||
}
|
||||
|
||||
public void onItemSelected(AdapterView parent, View v, int position, long id) {
|
||||
if (position >= 0) {
|
||||
Cursor c = (Cursor) parent.getItemAtPosition(position);
|
||||
mPhone.setText(c.getString(mPhoneColumnIndex));
|
||||
final Cursor c = (Cursor) parent.getItemAtPosition(position);
|
||||
if (c.getInt(mHasPhoneColumnIndex) > 0) {
|
||||
final long contactId = c.getLong(mIdColumnIndex);
|
||||
final Cursor phones = getContentResolver().query(
|
||||
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
|
||||
new String[] { ContactsContract.CommonDataKinds.Phone.NUMBER },
|
||||
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + contactId, null,
|
||||
ContactsContract.CommonDataKinds.Phone.IS_SUPER_PRIMARY + " DESC");
|
||||
|
||||
try {
|
||||
phones.moveToFirst();
|
||||
mPhone.setText(phones.getString(0));
|
||||
} finally {
|
||||
phones.close();
|
||||
}
|
||||
} else {
|
||||
mPhone.setText(R.string.list_7_nothing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onNothingSelected(AdapterView parent) {
|
||||
mPhone.setText(R.string.list_7_nothing);
|
||||
|
||||
}
|
||||
|
||||
private int mPhoneColumnIndex;
|
||||
private TextView mPhone;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
16
samples/ContactManager/Android.mk
Normal 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))
|
||||
35
samples/ContactManager/AndroidManifest.xml
Normal 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>
|
||||
10
samples/ContactManager/_index.html
Normal 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" />
|
||||
BIN
samples/ContactManager/res/drawable-hdpi/icon.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
samples/ContactManager/res/drawable-ldpi/icon.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
samples/ContactManager/res/drawable-mdpi/icon.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
49
samples/ContactManager/res/layout/account_entry.xml
Normal 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>
|
||||
80
samples/ContactManager/res/layout/contact_adder.xml
Normal 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>
|
||||
24
samples/ContactManager/res/layout/contact_entry.xml
Normal 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>
|
||||
33
samples/ContactManager/res/layout/contact_manager.xml
Normal 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>
|
||||
33
samples/ContactManager/res/values/strings.xml
Normal 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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -17,4 +17,4 @@ It demonstrates using:
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<img alt="" src="../images/sample_multires.png"/>
|
||||
<img alt="" src="../images/MultiResolution.png"/>
|
||||
|
||||
16
samples/Wiktionary/Android.mk
Normal 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))
|
||||
70
samples/Wiktionary/AndroidManifest.xml
Normal 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>
|
||||
10
samples/Wiktionary/_index.html
Normal 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"/>
|
||||
22
samples/Wiktionary/res/anim/slide_in.xml
Normal 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>
|
||||
22
samples/Wiktionary/res/anim/slide_out.xml
Normal 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>
|
||||
BIN
samples/Wiktionary/res/drawable/app_icon.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
samples/Wiktionary/res/drawable/ic_menu_shuffle.png
Executable file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
samples/Wiktionary/res/drawable/logo_overlay.9.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
20
samples/Wiktionary/res/drawable/lookup_bg.xml
Normal 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>
|
||||
43
samples/Wiktionary/res/drawable/progress_spin.xml
Normal 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>
|
||||
BIN
samples/Wiktionary/res/drawable/star_logo.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
22
samples/Wiktionary/res/drawable/widget_bg.xml
Normal 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>
|
||||
BIN
samples/Wiktionary/res/drawable/widget_bg_normal.9.png
Normal file
|
After Width: | Height: | Size: 268 B |
BIN
samples/Wiktionary/res/drawable/widget_bg_pressed.9.png
Normal file
|
After Width: | Height: | Size: 275 B |
BIN
samples/Wiktionary/res/drawable/widget_bg_selected.9.png
Normal file
|
After Width: | Height: | Size: 276 B |
40
samples/Wiktionary/res/layout/about.xml
Normal 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>
|
||||
56
samples/Wiktionary/res/layout/lookup.xml
Normal 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>
|
||||
34
samples/Wiktionary/res/layout/widget_message.xml
Normal 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>
|
||||
79
samples/Wiktionary/res/layout/widget_word.xml
Normal 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>
|
||||
34
samples/Wiktionary/res/menu/lookup.xml
Normal 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>
|
||||
56
samples/Wiktionary/res/values/strings.xml
Normal 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>
|
||||
66
samples/Wiktionary/res/values/styles.xml
Normal 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>
|
||||
21
samples/Wiktionary/res/values/themes.xml
Normal 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>
|
||||
19
samples/Wiktionary/res/xml/searchable.xml
Normal 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" />
|
||||
21
samples/Wiktionary/res/xml/widget_word.xml
Normal 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" />
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
samples/WiktionarySimple/Android.mk
Normal 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))
|
||||
41
samples/WiktionarySimple/AndroidManifest.xml
Normal 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>
|
||||
10
samples/WiktionarySimple/_index.html
Normal 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"/>
|
||||
BIN
samples/WiktionarySimple/res/drawable/app_icon.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
samples/WiktionarySimple/res/drawable/star_logo.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
23
samples/WiktionarySimple/res/drawable/widget_bg.xml
Normal 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>
|
||||
BIN
samples/WiktionarySimple/res/drawable/widget_bg_normal.9.png
Normal file
|
After Width: | Height: | Size: 268 B |
BIN
samples/WiktionarySimple/res/drawable/widget_bg_pressed.9.png
Normal file
|
After Width: | Height: | Size: 275 B |
BIN
samples/WiktionarySimple/res/drawable/widget_bg_selected.9.png
Normal file
|
After Width: | Height: | Size: 276 B |
34
samples/WiktionarySimple/res/layout/widget_message.xml
Normal 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>
|
||||
79
samples/WiktionarySimple/res/layout/widget_word.xml
Normal 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>
|
||||
44
samples/WiktionarySimple/res/values/strings.xml
Normal 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>
|
||||
52
samples/WiktionarySimple/res/values/styles.xml
Normal 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>
|
||||
21
samples/WiktionarySimple/res/xml/widget_word.xml
Normal 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" />
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
tutorials/NotepadCodeLab/Notepadv1/AndroidManifest.xml
Executable 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>
|
||||
BIN
tutorials/NotepadCodeLab/Notepadv1/res/drawable/icon.png
Executable file
|
After Width: | Height: | Size: 6.0 KiB |
6
tutorials/NotepadCodeLab/Notepadv1/res/layout/notepad_list.xml
Executable 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>
|
||||
5
tutorials/NotepadCodeLab/Notepadv1/res/values/strings.xml
Executable 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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
30
tutorials/NotepadCodeLab/Notepadv1Solution/Android.mk
Normal 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)
|
||||
12
tutorials/NotepadCodeLab/Notepadv1Solution/AndroidManifest.xml
Executable 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>
|
||||
BIN
tutorials/NotepadCodeLab/Notepadv1Solution/res/drawable/icon.png
Executable file
|
After Width: | Height: | Size: 6.0 KiB |
14
tutorials/NotepadCodeLab/Notepadv1Solution/res/layout/notepad_list.xml
Executable 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>
|
||||
5
tutorials/NotepadCodeLab/Notepadv1Solution/res/layout/notes_row.xml
Executable 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"/>
|
||||
6
tutorials/NotepadCodeLab/Notepadv1Solution/res/values/strings.xml
Executable 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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
12
tutorials/NotepadCodeLab/Notepadv2/AndroidManifest.xml
Executable 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>
|
||||
BIN
tutorials/NotepadCodeLab/Notepadv2/res/drawable/icon.png
Executable file
|
After Width: | Height: | Size: 6.0 KiB |
33
tutorials/NotepadCodeLab/Notepadv2/res/layout/note_edit.xml
Executable 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>
|
||||