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" />
|
<category android:name="android.intent.category.TEST" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</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 android:name="PackageSummary" android:label="Package Summary">
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name="ShowActivity" android:label="Activity">
|
<activity android:name="ShowActivity" android:label="Activity">
|
||||||
@@ -128,44 +121,50 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name="GLSTester" android:label="Google Login Service">
|
<activity android:name="GLSTester" android:label="Google Login Service">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.TEST" />
|
<category android:name="android.intent.category.TEST" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name="RunningProcesses" android:label="Running processes">
|
<activity android:name="RunningProcesses" android:label="Running processes">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.TEST" />
|
<category android:name="android.intent.category.TEST" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name="ProcessInfo" android:label="Process Information">
|
<activity android:name="ProcessInfo" android:label="Process Information">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<!--
|
<!--
|
||||||
<activity android:name="AppHwConfigList" android:label="Applications Hw Configuration">
|
<activity android:name="AppHwConfigList" android:label="Applications Hw Configuration">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.TEST" />
|
<category android:name="android.intent.category.TEST" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
-->
|
-->
|
||||||
<activity android:name="AppHwPref" android:label="Applications Hardware Preferences">
|
<activity android:name="AppHwPref" android:label="Applications Hardware Preferences">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name="PermissionDetails" android:label="Permission Info">
|
<activity android:name="PermissionDetails" android:label="Permission Info">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.android.development.VIEW_PERMISSION" />
|
<action android:name="com.android.development.VIEW_PERMISSION" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</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>
|
</application>
|
||||||
</manifest>
|
</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_bind_failed">Bind failed</string>
|
||||||
<string name="binding_waiting_for_connection">Waiting for service to be connected...</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>
|
<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>
|
</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/SoftKeyboard platforms/${PLATFORM_NAME}/samples/SoftKeyboard
|
||||||
development/samples/JetBoy platforms/${PLATFORM_NAME}/samples/JetBoy
|
development/samples/JetBoy platforms/${PLATFORM_NAME}/samples/JetBoy
|
||||||
development/samples/SearchableDictionary platforms/${PLATFORM_NAME}/samples/SearchableDictionary
|
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
|
# dx
|
||||||
bin/dx platforms/${PLATFORM_NAME}/tools/dx
|
bin/dx platforms/${PLATFORM_NAME}/tools/dx
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ import android.content.ComponentName;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.IPackageManager;
|
import android.content.pm.IPackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Debug;
|
import android.os.Debug;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.server.data.CrashData;
|
|
||||||
import android.view.IWindowManager;
|
import android.view.IWindowManager;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
@@ -225,26 +225,18 @@ public class Monkey {
|
|||||||
return allow;
|
return allow;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg,
|
public boolean appCrashed(String processName, int pid,
|
||||||
byte[] crashData) {
|
String tag, String shortMsg, String longMsg,
|
||||||
|
long timeMillis, String stackTrace) {
|
||||||
System.err.println("// CRASH: " + processName + " (pid " + pid + ")");
|
System.err.println("// CRASH: " + processName + " (pid " + pid + ")");
|
||||||
System.err.println("// Short Msg: " + shortMsg);
|
System.err.println("// Short Msg: " + shortMsg);
|
||||||
System.err.println("// Long Msg: " + longMsg);
|
System.err.println("// Long Msg: " + longMsg);
|
||||||
if (crashData != null) {
|
System.err.println("// Build Label: " + Build.FINGERPRINT);
|
||||||
try {
|
System.err.println("// Build Changelist: " + Build.VERSION.INCREMENTAL);
|
||||||
CrashData cd = new CrashData(new DataInputStream(new ByteArrayInputStream(
|
System.err.println("// Build Time: " + Build.TIME);
|
||||||
crashData)));
|
System.err.println("// ID: "); // TODO: This was never set -- remove?
|
||||||
System.err.println("// Build Label: " + cd.getBuildData().getFingerprint());
|
System.err.println("// Tag: " + tag);
|
||||||
System.err.println("// Build Changelist: "
|
System.err.println("// " + stackTrace.replace("\n", "\n// "));
|
||||||
+ 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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mIgnoreCrashes) {
|
if (!mIgnoreCrashes) {
|
||||||
synchronized (Monkey.this) {
|
synchronized (Monkey.this) {
|
||||||
|
|||||||
@@ -34,11 +34,14 @@ HKR,,Icon,,-1
|
|||||||
%CompositeAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C03&MI_01
|
%CompositeAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C03&MI_01
|
||||||
;
|
;
|
||||||
;Moto Sholes
|
;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
|
%SingleAdbInterface% = USB_Install, USB\VID_22B8&PID_41DB
|
||||||
%CompositeAdbInterface% = USB_Install, USB\VID_22B8&PID_41DB&MI_01
|
%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]
|
[Google.NTamd64]
|
||||||
; HTC Dream
|
; HTC Dream
|
||||||
@@ -49,11 +52,14 @@ HKR,,Icon,,-1
|
|||||||
%CompositeAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C03&MI_01
|
%CompositeAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C03&MI_01
|
||||||
;
|
;
|
||||||
;Moto Sholes
|
;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
|
%SingleAdbInterface% = USB_Install, USB\VID_22B8&PID_41DB
|
||||||
%CompositeAdbInterface% = USB_Install, USB\VID_22B8&PID_41DB&MI_01
|
%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]
|
[USB_Install]
|
||||||
Include = winusb.inf
|
Include = winusb.inf
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ IMPORTANT CHANGES:
|
|||||||
OpenGL ES 2.0 is currently *not* available from Java, and must be used
|
OpenGL ES 2.0 is currently *not* available from Java, and must be used
|
||||||
through native code exclusively.
|
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
|
- The NDK build script will now remove installed binaries from the application
|
||||||
project's path before starting the build. This ensures that:
|
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
|
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
|
on Android 2.0 system images and above.
|
||||||
platform system images.
|
|
||||||
|
|
||||||
|
|
||||||
The OpenGL ES 2.0 Library:
|
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
|
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.
|
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>
|
</intent-filter>
|
||||||
</activity>
|
</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 -->
|
<!-- CONTENT PACKAGE SAMPLES -->
|
||||||
<!-- ************************************* -->
|
<!-- ************************************* -->
|
||||||
@@ -1407,6 +1416,25 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</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"
|
<activity android:name=".graphics.TranslucentGLSurfaceViewActivity"
|
||||||
android:label="Graphics/OpenGL ES/Translucent GLSurfaceView"
|
android:label="Graphics/OpenGL ES/Translucent GLSurfaceView"
|
||||||
android:theme="@style/Theme.Translucent"
|
android:theme="@style/Theme.Translucent"
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
android:layout_height="fill_parent"
|
android:layout_height="fill_parent"
|
||||||
android:paddingLeft="8dip"
|
android:paddingLeft="8dip"
|
||||||
android:paddingRight="8dip">
|
android:paddingRight="8dip">
|
||||||
|
|
||||||
<ListView android:id="@android:id/list"
|
<ListView android:id="@android:id/list"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="0dip"
|
android:layout_height="0dip"
|
||||||
@@ -31,5 +31,5 @@
|
|||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/blue"/>
|
android:background="@drawable/blue"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</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="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 -->
|
<!-- 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;
|
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 com.example.android.apis.R;
|
||||||
|
|
||||||
|
|
||||||
import android.app.ListActivity;
|
import android.app.ListActivity;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.provider.Contacts.People;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.provider.ContactsContract;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.AdapterView.OnItemSelectedListener;
|
import android.widget.AdapterView.OnItemSelectedListener;
|
||||||
@@ -36,46 +33,69 @@ import android.widget.TextView;
|
|||||||
* A list view example where the data comes from a cursor.
|
* A list view example where the data comes from a cursor.
|
||||||
*/
|
*/
|
||||||
public class List7 extends ListActivity implements OnItemSelectedListener {
|
public class List7 extends ListActivity implements OnItemSelectedListener {
|
||||||
private static String[] PROJECTION = new String[] {
|
private static final String[] PROJECTION = new String[] {
|
||||||
People._ID, People.NAME, People.NUMBER
|
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
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
setContentView(R.layout.list_7);
|
setContentView(R.layout.list_7);
|
||||||
|
|
||||||
mPhone = (TextView) findViewById(R.id.phone);
|
mPhone = (TextView) findViewById(R.id.phone);
|
||||||
getListView().setOnItemSelectedListener(this);
|
getListView().setOnItemSelectedListener(this);
|
||||||
|
|
||||||
// Get a cursor with all people
|
// Get a cursor with all people
|
||||||
Cursor c = getContentResolver().query(People.CONTENT_URI, PROJECTION, null, null, null);
|
Cursor c = managedQuery(ContactsContract.Contacts.CONTENT_URI,
|
||||||
startManagingCursor(c);
|
PROJECTION, null, null, null);
|
||||||
mPhoneColumnIndex = c.getColumnIndex(People.NUMBER);
|
mIdColumnIndex = c.getColumnIndex(ContactsContract.Contacts._ID);
|
||||||
|
mHasPhoneColumnIndex = c.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER);
|
||||||
|
|
||||||
ListAdapter adapter = new SimpleCursorAdapter(this,
|
ListAdapter adapter = new SimpleCursorAdapter(this,
|
||||||
android.R.layout.simple_list_item_1, // Use a template
|
android.R.layout.simple_list_item_1, // Use a template
|
||||||
// that displays a
|
// that displays a
|
||||||
// text view
|
// text view
|
||||||
c, // Give the cursor to the list adatper
|
c, // Give the cursor to the list adapter
|
||||||
new String[] {People.NAME}, // Map the NAME column in the
|
new String[] { ContactsContract.Contacts.DISPLAY_NAME }, // Map the NAME column in the
|
||||||
// people database to...
|
// people database to...
|
||||||
new int[] {android.R.id.text1}); // The "text1" view defined in
|
new int[] { android.R.id.text1 }); // The "text1" view defined in
|
||||||
// the XML template
|
// the XML template
|
||||||
setListAdapter(adapter);
|
setListAdapter(adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onItemSelected(AdapterView parent, View v, int position, long id) {
|
public void onItemSelected(AdapterView parent, View v, int position, long id) {
|
||||||
if (position >= 0) {
|
if (position >= 0) {
|
||||||
Cursor c = (Cursor) parent.getItemAtPosition(position);
|
final Cursor c = (Cursor) parent.getItemAtPosition(position);
|
||||||
mPhone.setText(c.getString(mPhoneColumnIndex));
|
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) {
|
public void onNothingSelected(AdapterView parent) {
|
||||||
mPhone.setText(R.string.list_7_nothing);
|
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
|
// Message types sent from the BluetoothChatService Handler
|
||||||
public static final int MESSAGE_STATE_CHANGE = 1;
|
public static final int MESSAGE_STATE_CHANGE = 1;
|
||||||
public static final int MESSAGE_READ = 2;
|
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_DEVICE_NAME = 4;
|
||||||
public static final int MESSAGE_TOAST = 5;
|
public static final int MESSAGE_TOAST = 5;
|
||||||
|
|
||||||
// Key names received from the BluetoothChatService Handler
|
// Key names received from the BluetoothChatService Handler
|
||||||
public static final String DEVICE_NAME = "device_name";
|
public static final String DEVICE_NAME = "device_name";
|
||||||
public static final String TOAST = "toast";
|
public static final String TOAST = "toast";
|
||||||
public static final String MESSAGE_WRITE = "message_write";
|
|
||||||
|
|
||||||
// Intent request codes
|
// Intent request codes
|
||||||
private static final int REQUEST_CONNECT_DEVICE = 1;
|
private static final int REQUEST_CONNECT_DEVICE = 1;
|
||||||
@@ -192,9 +191,10 @@ public class BluetoothChat extends Activity {
|
|||||||
|
|
||||||
private void ensureDiscoverable() {
|
private void ensureDiscoverable() {
|
||||||
if(D) Log.d(TAG, "ensure discoverable");
|
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);
|
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);
|
startActivity(discoverableIntent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,7 +223,8 @@ public class BluetoothChat extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The action listener for the EditText widget, to listen for the return key
|
// 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) {
|
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 the action is a key-up event on the return key, send the message
|
||||||
if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) {
|
if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) {
|
||||||
@@ -257,20 +258,27 @@ public class BluetoothChat extends Activity {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MESSAGE_OUTGOING_STRING:
|
case MESSAGE_WRITE:
|
||||||
mConversationArrayAdapter.add("Me: " + new String(msg.getData().getByteArray(MESSAGE_WRITE)).trim());
|
byte[] writeBuf = (byte[]) msg.obj;
|
||||||
mOutEditText.setText(mOutStringBuffer);
|
// construct a string from the buffer
|
||||||
|
String writeMessage = new String(writeBuf);
|
||||||
|
mConversationArrayAdapter.add("Me: " + writeMessage);
|
||||||
break;
|
break;
|
||||||
case MESSAGE_READ:
|
case MESSAGE_READ:
|
||||||
byte[] buf = (byte[]) msg.obj;
|
byte[] readBuf = (byte[]) msg.obj;
|
||||||
mConversationArrayAdapter.add(mConnectedDeviceName+": " + new String(buf).trim());
|
// construct a string from the valid bytes in the buffer
|
||||||
|
String readMessage = new String(readBuf, 0, msg.arg1);
|
||||||
|
mConversationArrayAdapter.add(mConnectedDeviceName+": " + readMessage);
|
||||||
break;
|
break;
|
||||||
case MESSAGE_DEVICE_NAME:
|
case MESSAGE_DEVICE_NAME:
|
||||||
mConnectedDeviceName = msg.getData().getString(DEVICE_NAME); // save the connected device's name
|
// save the connected device's name
|
||||||
Toast.makeText(getApplicationContext(), "Connected to " + mConnectedDeviceName, Toast.LENGTH_SHORT).show();
|
mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
|
||||||
|
Toast.makeText(getApplicationContext(), "Connected to "
|
||||||
|
+ mConnectedDeviceName, Toast.LENGTH_SHORT).show();
|
||||||
break;
|
break;
|
||||||
case MESSAGE_TOAST:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,7 +291,8 @@ public class BluetoothChat extends Activity {
|
|||||||
// When DeviceListActivity returns with a device to connect
|
// When DeviceListActivity returns with a device to connect
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
// Get the device MAC address
|
// 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
|
// Get the BLuetoothDevice object
|
||||||
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
|
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
|
||||||
// Attempt to connect to the device
|
// Attempt to connect to the device
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class BluetoothChatService {
|
|||||||
private int mState;
|
private int mState;
|
||||||
|
|
||||||
// Constants that indicate the current connection state
|
// 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_LISTEN = 1; // now listening for incoming connections
|
||||||
public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
|
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
|
public static final int STATE_CONNECTED = 3; // now connected to a remote device
|
||||||
@@ -397,10 +397,8 @@ public class BluetoothChatService {
|
|||||||
bytes = mmInStream.read(buffer);
|
bytes = mmInStream.read(buffer);
|
||||||
|
|
||||||
// Send the obtained bytes to the UI Activity
|
// Send the obtained bytes to the UI Activity
|
||||||
mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer).sendToTarget();
|
mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
|
||||||
|
.sendToTarget();
|
||||||
// Reload the buffer to clear extra bytes from the previous read
|
|
||||||
buffer = new byte[1024];
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, "disconnected", e);
|
Log.e(TAG, "disconnected", e);
|
||||||
connectionLost();
|
connectionLost();
|
||||||
@@ -411,18 +409,15 @@ public class BluetoothChatService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Write to the connected OutStream.
|
* 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 {
|
try {
|
||||||
mmOutStream.write(b);
|
mmOutStream.write(buffer);
|
||||||
|
|
||||||
// Share the sent message back to the UI Activity
|
// Share the sent message back to the UI Activity
|
||||||
Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_OUTGOING_STRING);
|
mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer)
|
||||||
Bundle bundle = new Bundle();
|
.sendToTarget();
|
||||||
bundle.putByteArray(BluetoothChat.MESSAGE_WRITE, b);
|
|
||||||
msg.setData(bundle);
|
|
||||||
mHandler.sendMessage(msg);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, "Exception during write", e);
|
Log.e(TAG, "Exception during write", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,13 @@ BackgroundPlugin::BackgroundPlugin(NPP inst) : SurfaceSubPlugin(inst) {
|
|||||||
test_domAccess();
|
test_domAccess();
|
||||||
test_javascript();
|
test_javascript();
|
||||||
test_loadJavaClass();
|
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() {
|
BackgroundPlugin::~BackgroundPlugin() {
|
||||||
@@ -170,7 +177,12 @@ int16 BackgroundPlugin::handleEvent(const ANPEvent* evt) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case kTouch_ANPEventType:
|
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;
|
break;
|
||||||
case kKey_ANPEventType:
|
case kKey_ANPEventType:
|
||||||
gLogI.log(kError_ANPLogType, " ------ %p the plugin did not request key events", inst());
|
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>
|
</ul>
|
||||||
</p>
|
</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>
|
||||||