Merge change I2b0bb983 into eclair
* changes: Importing Wikitionary/SimpleWiktionary sample code from http://code.google.com/p/wiktionary-android/.
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 widget
|
||||
for display on the Android home screen.</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 open a custom WebView to render the
|
||||
definition.</p>
|
||||
|
||||
<img alt="" src="../images/wiktionary.png"/>
|
||||
<img alt="" src="../images/wiktionary_detail.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/wiktionary.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;
|
||||
}
|
||||
}
|
||||
}
|
||||