summaryrefslogtreecommitdiffstats
path: root/samples/MySampleRss
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit5c11852110eeb03dc5a69111354b383f98d15336 (patch)
tree7b26fde6effb80c241f28fc3e620016e7f86418e /samples/MySampleRss
downloadandroid_development-5c11852110eeb03dc5a69111354b383f98d15336.tar.gz
android_development-5c11852110eeb03dc5a69111354b383f98d15336.tar.bz2
android_development-5c11852110eeb03dc5a69111354b383f98d15336.zip
Initial Contribution
Diffstat (limited to 'samples/MySampleRss')
-rw-r--r--samples/MySampleRss/Android.mk12
-rwxr-xr-xsamples/MySampleRss/AndroidManifest.xml66
-rwxr-xr-xsamples/MySampleRss/res/drawable/rss_icon.PNGbin0 -> 285 bytes
-rw-r--r--samples/MySampleRss/res/layout/add_item.xml59
-rw-r--r--samples/MySampleRss/res/layout/list_element.xml20
-rwxr-xr-xsamples/MySampleRss/res/layout/main_screen.xml35
-rw-r--r--samples/MySampleRss/res/layout/main_screen2.xml33
-rw-r--r--samples/MySampleRss/res/values/strings.xml16
-rw-r--r--samples/MySampleRss/src/com/example/codelab/rssexample/AddRssItem.java75
-rw-r--r--samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader.java107
-rw-r--r--samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader2.java181
-rw-r--r--samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader3.java184
-rw-r--r--samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader4.java197
-rw-r--r--samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader5.java241
-rw-r--r--samples/MySampleRss/src/com/example/codelab/rssexample/RssContentProvider.java202
-rw-r--r--samples/MySampleRss/src/com/example/codelab/rssexample/RssItem.java37
-rw-r--r--samples/MySampleRss/src/com/example/codelab/rssexample/RssService.java263
17 files changed, 1728 insertions, 0 deletions
diff --git a/samples/MySampleRss/Android.mk b/samples/MySampleRss/Android.mk
new file mode 100644
index 000000000..fea9729a2
--- /dev/null
+++ b/samples/MySampleRss/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := MyRSSReader
+
+LOCAL_SDK_VERSION := current
+
+##include $(BUILD_PACKAGE)
diff --git a/samples/MySampleRss/AndroidManifest.xml b/samples/MySampleRss/AndroidManifest.xml
new file mode 100755
index 000000000..575c81e29
--- /dev/null
+++ b/samples/MySampleRss/AndroidManifest.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Declare the contents of this Android application. The namespace
+ attribute brings in the Android platform namespace, and the package
+ supplies a unique name for the application. When writing your
+ own application, the package name must be changed from "com.example.*"
+ to come from a domain that you own or have control over. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.codelab.rssexample">
+ <application android:name="MyRssReader" android:label="My Rss Reader">
+ <activity>
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name="MyRssReader2" android:label="My Rss Reader V2">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name="MyRssReader3" android:label="@string/reader_3_label">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name="MyRssReader4" android:label="@string/reader_4_label">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name="MyRssReader5" android:label="@string/reader_5_label">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name="AddRssItem" android:label="@string/add_item_label" android:theme="@android:style/Theme.Dialog"/>
+ <provider android:name="RssContentProvider" android:authorities="my_rss_item" android:multiprocess="true" />
+ <service android:name="RssService" android:process=":myrss"/>
+ </application>
+</manifest>
+
+
diff --git a/samples/MySampleRss/res/drawable/rss_icon.PNG b/samples/MySampleRss/res/drawable/rss_icon.PNG
new file mode 100755
index 000000000..214a38db3
--- /dev/null
+++ b/samples/MySampleRss/res/drawable/rss_icon.PNG
Binary files differ
diff --git a/samples/MySampleRss/res/layout/add_item.xml b/samples/MySampleRss/res/layout/add_item.xml
new file mode 100644
index 000000000..8191bf582
--- /dev/null
+++ b/samples/MySampleRss/res/layout/add_item.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="fill_parent"
+ android:layout_width="fill_parent">
+
+ <TextView android:id="@+id/title_label"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:text="@string/add_item_title"/>
+ <EditText android:id="@+id/title_textbox"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:layout_toRightOf="@id/title_label"/>
+ <EditText android:id="@+id/url_textbox"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:layout_below="@id/title_textbox"
+ android:layout_alignLeft="@id/title_textbox"/>
+
+ <!-- Note that we declare url_label *after* url_textbox in this file
+ because it references url_textbox, and a resource file is
+ evaluated sequentially. -->
+ <TextView android:id="@+id/url_label"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_toRightOf="url_textbox"
+ android:layout_alignTop="@id/url_textbox"
+ android:text="@string/add_item_url"/>
+ <Button android:id="@+id/submit"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentBottom="true"
+ android:text="@string/submit"/>
+ <Button android:id="@+id/cancel"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_toLeftOf="@id/submit"
+ android:layout_alignTop="@id/submit"
+ android:text="@string/cancel"/>
+</RelativeLayout>
+
diff --git a/samples/MySampleRss/res/layout/list_element.xml b/samples/MySampleRss/res/layout/list_element.xml
new file mode 100644
index 000000000..4255d8654
--- /dev/null
+++ b/samples/MySampleRss/res/layout/list_element.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+ <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/list_item"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"/>
diff --git a/samples/MySampleRss/res/layout/main_screen.xml b/samples/MySampleRss/res/layout/main_screen.xml
new file mode 100755
index 000000000..5292bb273
--- /dev/null
+++ b/samples/MySampleRss/res/layout/main_screen.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="fill_parent"
+ android:layout_width="fill_parent"
+ android:orientation="vertical"
+ android:scrollbars="vertical">
+
+ <ListView android:id="@+id/rssListView"
+ android:layout_height="0px"
+ android:layout_width="fill_parent"
+ android:layout_weight="1"
+ android:background="#00CC00"/>
+
+ <WebView android:id="@+id/rssWebView"
+ android:background="#77CC0000"
+ android:layout_height="0px"
+ android:layout_width="fill_parent"
+ android:layout_weight="1"/>
+
+</LinearLayout>
diff --git a/samples/MySampleRss/res/layout/main_screen2.xml b/samples/MySampleRss/res/layout/main_screen2.xml
new file mode 100644
index 000000000..36b0af4dd
--- /dev/null
+++ b/samples/MySampleRss/res/layout/main_screen2.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="fill_parent"
+ android:layout_width="fill_parent"
+ android:orientation="vertical"
+ android:scrollbars="vertical">
+
+ <ListView android:id="@+id/rssListView"
+ android:layout_height="0px"
+ android:layout_width="fill_parent"
+ android:layout_weight="1"
+ android:background="#9900FF00"/>
+
+ <WebView android:id="@+id/rssWebView"
+ android:layout_height="0px"
+ android:layout_width="fill_parent"
+ android:layout_weight="2"/>
+</LinearLayout>
diff --git a/samples/MySampleRss/res/values/strings.xml b/samples/MySampleRss/res/values/strings.xml
new file mode 100644
index 000000000..86559955e
--- /dev/null
+++ b/samples/MySampleRss/res/values/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources>
+ <string name="menu_option_start">Start RSS Service</string>
+ <string name="menu_option_stop">Stop RSS Service</string>
+ <string name="menu_option_add">Add New Feed</string>
+ <string name="menu_option_delete">Delete Feed</string>
+ <string name="menu_option_update">Update All Feeds</string>
+ <string name="add_item_title">Title</string>
+ <string name="add_item_url">URL</string>
+ <string name="submit">Submit</string>
+ <string name="cancel">Cancel</string>
+ <string name="reader_3_label">MyRssReader3</string>
+ <string name="reader_4_label">MyRssReader4</string>
+ <string name="reader_5_label">MyRssReader5</string>
+ <string name="add_item_label">Add a new Feed</string>
+</resources> \ No newline at end of file
diff --git a/samples/MySampleRss/src/com/example/codelab/rssexample/AddRssItem.java b/samples/MySampleRss/src/com/example/codelab/rssexample/AddRssItem.java
new file mode 100644
index 000000000..980eec6c1
--- /dev/null
+++ b/samples/MySampleRss/src/com/example/codelab/rssexample/AddRssItem.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.codelab.rssexample;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.view.View.OnClickListener;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import android.text.TextUtils;
+import android.os.Bundle;
+
+/*** Form to add a new RSS feed.
+ It is a dialog form,**/
+public class AddRssItem extends Activity {
+
+ // Button handler for Submit/Cancel.
+ // It is a dialog style form because it's declared as such
+ // in the manifest.
+ private OnClickListener mClickListener = new OnClickListener(){
+ public void onClick(View v){
+ if(v.getId() == R.id.submit){
+ String title = ((TextView) findViewById(R.id.title_textbox)).getText().toString();
+ String url = ((TextView) findViewById(R.id.url_textbox)).getText().toString();
+ if(TextUtils.isEmpty(title) || TextUtils.isEmpty(url)){
+ showAlert("Missing Values",
+ "You must specify both a title and a URL value",
+ "OK",
+ null, false, null);
+ return;
+ }
+ Intent res = new Intent("Accepted");
+ res.putExtra(RssContentProvider.TITLE, title);
+ res.putExtra(RssContentProvider.URL, url);
+ res.putExtra(RssContentProvider.LAST_UPDATED, 0);
+ res.putExtra(RssContentProvider.CONTENT, "<html><body><h2>Not updated yet.</h2></body></html>");
+ setResult(RESULT_OK, res);
+ }
+ else
+ setResult(RESULT_CANCELED, (new Intent()).setAction("Canceled" + v.getId()));
+
+ finish();
+
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState){
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.add_item);
+ setTitle(getString(R.string.add_item_label));
+
+ Button btn = (Button) findViewById(R.id.cancel);
+ btn.setOnClickListener(mClickListener);
+
+ btn = (Button) findViewById(R.id.submit);
+ btn.setOnClickListener(mClickListener);
+ }
+
+}
diff --git a/samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader.java b/samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader.java
new file mode 100644
index 000000000..d30655436
--- /dev/null
+++ b/samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.codelab.rssexample;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Menu;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+//BEGIN_INCLUDE(1_1)
+public class MyRssReader extends Activity {
+ /** Called with the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState){
+ super.onCreate(savedInstanceState);
+
+ // Load screen layout.
+ setContentView(R.layout.main_screen);
+
+//END_INCLUDE(1_1)
+//BEGIN_INCLUDE(1_2)
+ // Load some simple values into the ListView
+ mRssList = (ListView) findViewById(R.id.rssListView);
+ mRssList.setAdapter(
+ new ArrayAdapter<String>(
+ this,
+ R.layout.list_element,
+ new String[] { "Scientific American", "BBC", "The Onion", "Engadget" }));
+//END_INCLUDE(1_2)
+}
+
+ // Store our state before we are potentially bumped from memory.
+ // We'd like to store the current ListView selection.
+ @Override
+ protected void onSaveInstanceState(Bundle outState){
+ int index = mRssList.getSelectedItemIndex();
+ if(index > -1){
+ outState.putInteger("lastIndexItem", index);
+ }
+ }
+
+ // Add our initial menu options. We will tweak this menu when it's loaded swap out
+ // "start service" or "stop service", depending on whether the service is currently running.
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu){
+ // Always call the superclass implementation to
+ // provide standard items.
+ super.onCreateOptionsMenu(menu);
+
+ menu.add(0, 0, "Start RSS Service", null);
+ menu.add(0, 1, "Stop RSS Service", null);
+ menu.add(0, 2, "Add New Feed", null);
+ menu.add(0, 3, "Delete Feed", null);
+ menu.add(0, 4, "Update All Feeds", null);
+
+ return true;
+ }
+
+ // Toggle out start service/stop service depending on whether the service is running.
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu){
+ return true;
+ }
+
+ // Handle our menu clicks.
+ @Override
+ public boolean onOptionsItemSelected(Menu.Item item){
+ switch (item.getId()) {
+ case 0:
+ showAlert(null, "You clicked 'start'!", "ok", null, false, null);
+ break;
+ case 1:
+ showAlert(null, "You clicked stop!", "ok", null, false, null);
+ break;
+ case 2:
+ showAlert(null, "You clicked 'Add'!", "ok", null, false, null);
+ break;
+ case 3:
+ showAlert(null, "You clicked 'Delete'!", "ok", null, false, null);
+ break;
+ case 4:
+ showAlert(null, "You clicked 'Update'!", "ok", null, false, null);
+ break;
+ default:
+ showAlert(null, "I have no idea what you clicked!", "ok", null, false, null);
+ break;
+ }
+ return true;
+ }
+
+ ListView mRssList;
+}
diff --git a/samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader2.java b/samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader2.java
new file mode 100644
index 000000000..352167a32
--- /dev/null
+++ b/samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader2.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.codelab.rssexample;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+public class MyRssReader2 extends Activity{
+ private ArrayList<RssItem> mFeeds = null;
+ ListView mRssList = null;
+ private Logger mLogger = Logger.getLogger("com.example.codelab.rssexample");
+
+ @Override
+ public void onCreate(Bundle savedInstanceState){
+ super.onCreate(savedInstanceState);
+
+ // Load screen layout.
+ setContentView(R.layout.main_screen2);
+
+ // Populate our list
+ mFeeds = initializeList();
+ mLogger.info("MyRssReader.onCreate-1 mFeeds value:" + mFeeds.size());
+// BEGIN_INCLUDE(2_2)
+ // Populate ArrayAdapter and bind it to ListView
+ mRssList = (ListView)findViewById(R.id.rssListView);
+ if(mRssList == null){
+ // Note: Calling showAlert() would fail here because dialogs opened
+ // in onCreate won't be displayed properly, if at all.
+ mLogger.warning("MyRssReader.onCreate-2 -- Couldn't instantiate a ListView!");
+ }
+ RssDataAdapter<RssItem> adap = new RssDataAdapter<RssItem>(this, R.layout.add_item, mFeeds);
+ if(adap == null){
+ mLogger.warning("MyRssReader.onCreate-3 -- Couldn't instantiate RssDataAdapter!");
+ }
+ if(mFeeds == null){
+ mLogger.warning("MyRssReader.onCreate-4 -- Couldn't instantiate a ListView!");
+ }
+ mRssList.setAdapter(adap);
+// END_INCLUDE(2_2)
+
+ mLogger.info("MyRssReader.onCreate-5 -- Loading preferences.");
+ // Set the last selected item.
+ // (icicle is only set if this is being restarted).
+ if(savedInstanceState != null && savedInstanceState.containsKey("lastIndexItem"))
+ {
+ Integer selectedItem = savedInstanceState.getInteger("lastIndexItem");
+ if(selectedItem >= 0 && selectedItem < mRssList.getChildCount()){
+ mRssList.setSelection(savedInstanceState.getInteger("lastIndexItem"));
+ }
+ mLogger.info("MyRssReader.onCreate-6 -- Last selected item:" + selectedItem);
+ }
+ }
+
+ // Store our state before we are potentially bumped from memory.
+ // We'd like to store the current ListView selection.
+ @Override
+ protected void onSaveInstanceState(Bundle outState){
+ int index = mRssList.getSelectedItemIndex();
+ if(index > -1){
+ outState.putInteger("lastIndexItem", index);
+ }
+ }
+
+
+
+ // Add our initial menu options. We will tweak this menu when it's loaded swap out
+ // "start service" or "stop service", depending on whether the service is currently running.
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu){
+ // Always call the superclass implementation to
+ // provide standard items.
+ super.onCreateOptionsMenu(menu);
+
+ menu.add(0, 0, "Start RSS Service", null);
+ menu.add(0, 1, "Stop RSS Service", null);
+ menu.add(0, 2, "Add New Feed", null);
+ menu.add(0, 3, "Delete Feed", null);
+ menu.add(0, 4, "Update All Feeds", null);
+
+ return true;
+ }
+
+ // Toggle out start service/stop service depending on whether the service is running.
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu){
+ return true;
+ }
+
+ // Handle our menu clicks.
+ @Override
+ public boolean onOptionsItemSelected(Menu.Item item){
+ switch (item.getId()){
+ case 0:
+ showAlert(null, "You clicked 'start'!", "ok", null, false, null);
+ break;
+ case 1:
+ showAlert(null, "You clicked stop!", "ok", null, false, null);
+ break;
+ case 2:
+ showAlert(null, "You clicked 'Add'!", "ok", null, false, null);
+ break;
+ case 3:
+ showAlert(null, "You clicked 'Delete'!", "ok", null, false, null);
+ break;
+ case 4:
+ showAlert(null, "You clicked 'Update'!", "ok", null, false, null);
+ break;
+ default:
+ showAlert(null, "I have no idea what you clicked!", "ok", null, false, null);
+ break;
+ }
+ return true;
+ }
+
+
+ // Our private ArrayAdapter implementation that returns a bold TextView for
+ // RSS items that are unread, or a normal TextView for items that have been read.
+// BEGIN_INCLUDE(2_3)
+ private class RssDataAdapter<T> extends ArrayAdapter<T> {
+ public RssDataAdapter(Context context, int resource, List objects) {
+ super(context, resource, objects);
+ }
+// END_INCLUDE(2_3)
+ // Here's our only important override--returning the list item.
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent){
+ TextView view = null;
+ // Get the item from the underlying array,
+ // Create a TextView wrapper, and change the typeface, if necessary.
+ RssItem item = (RssItem)this.getItem(position);
+ if(item != null)
+ {
+ view = new TextView(parent.getContext());
+ view.setText(item.toString());
+
+ if(! item.hasBeenRead){
+ Typeface type = view.getTypeface();
+ view.setTypeface(Typeface.create(type, Typeface.BOLD_ITALIC));
+ }
+ }
+ return view;
+ }
+ }
+
+//BEGIN_INCLUDE(2_1)
+ // Method to initialize our list of RSS items.
+ private ArrayList<RssItem> initializeList(){
+ ArrayList<RssItem> list = new ArrayList<RssItem>();
+ list.add(new RssItem("http://www.sciam.com/xml/sciam.xml", "Scientific American"));
+ list.add(new RssItem("http://newsrss.bbc.co.uk/rss/newsonline_world_edition/front_page/rss.xml", "BBC"));
+ list.add(new RssItem("http://www.theonion.com/content/feeds/daily.", "The Onion"));
+ list.add(new RssItem("http://feeds.engadget.com/weblogsinc/engadget", "Engadget"));
+ return list;
+ }
+//END_INCLUDE(2_1)
+}
diff --git a/samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader3.java b/samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader3.java
new file mode 100644
index 000000000..1ffe33ede
--- /dev/null
+++ b/samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader3.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.codelab.rssexample;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MyRssReader3 extends Activity{
+
+ private ArrayList<RssItem> mFeeds;
+ ListView mRssList;
+ ArrayAdapter mAdap;
+ private static final int ADD_ELEMENT_REQUEST = 1;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState){
+ super.onCreate(savedInstanceState);
+
+ // Load screen layout.
+ setContentView(R.layout.main_screen2);
+
+ // Populate our list
+ mFeeds = initializeList();
+
+ // Populate ArrayAdapter and bind it to ListView
+ mRssList = (ListView)findViewById(R.id.rssListView);
+ mAdap = new RssDataAdapter<RssItem>(this, R.layout.list_element, mFeeds);
+ mRssList.setAdapter(mAdap);
+
+ // Set the last selected item.
+ // (icicle is only set if this is being restarted).
+ if(savedInstanceState != null && savedInstanceState.containsKey("lastIndexItem"))
+ mRssList.setSelection(savedInstanceState.getInteger("lastIndexItem"));
+ }
+
+ // Store our state before we are potentially bumped from memory.
+ // We'd like to store the current ListView selection.
+ @Override
+ protected void onSaveInstanceState(Bundle outState){
+ int index = mRssList.getSelectedItemIndex();
+ if(index > -1){
+ outState.putInteger("lastIndexItem", index);
+ }
+ }
+
+
+ // Add our initial menu options. We will tweak this menu when it's loaded swap out
+ // "start service" or "stop service", depending on whether the service is currently running.
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu)
+ {
+ // Always call the superclass implementation to
+ // provide standard items.
+ super.onCreateOptionsMenu(menu);
+
+ menu.add(0, 0, R.string.menu_option_start, null);
+ menu.add(0, 1, R.string.menu_option_stop, null);
+ menu.add(0, 2, R.string.menu_option_add, null);
+ menu.add(0, 3, R.string.menu_option_delete, null);
+ menu.add(0, 4, R.string.menu_option_update, null);
+
+ return true;
+ }
+
+ // Toggle out start service/stop service depending on whether the service is running.
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu){
+ return true;
+ }
+
+ // Handle our menu clicks.
+ @Override
+ public boolean onOptionsItemSelected(Menu.Item item){
+ super.onOptionsItemSelected(item);
+
+ switch (item.getId()){
+ case 0: // Start service
+ showAlert(null, "You clicked 'start'!", "ok", null, false, null);
+ break;
+ case 1: // Stop service
+ showAlert(null, "You clicked stop!", "ok", null, false, null);
+ break;
+ case 2: // Add Item
+ Intent addIntent = new Intent(AddRssItem.class);
+
+ // Use an ID so that if we create a "remove item" form we
+ // can tell which form is returning a value.
+ startActivityForResult(addIntent, ADD_ELEMENT_REQUEST);
+ break;
+ case 3: // Delete item.
+ if(mRssList.hasFocus()){
+ Object selectedItem = mRssList.getSelectedItem();
+ mAdap.removeObject(mRssList.getSelectedItem());
+ }
+ break;
+ case 4: // Update all
+ showAlert(null, "You clicked 'Update'!", "ok", null, false, null);
+ break;
+ default:
+ showAlert(null, "I have no idea what you clicked!", "ok", null, false, null);
+ break;
+ }
+ return true;
+ }
+
+ // Called by the "Add RSS Item" floating screen when it closes.
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data){
+ if(resultCode == RESULT_OK){
+ switch (requestCode){
+ case ADD_ELEMENT_REQUEST:
+ RssItem newIt = new RssItem(
+ data.getStringExtra("url").toString(),
+ data.getStringExtra("title").toString());
+ mAdap.addObject(newIt);
+ mRssList.setSelection(mRssList.getCount() - 1);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Our private ArrayAdapter implementation that returns a bold TextView for
+ // RSS items that are unread, or a normal TextView for items that have been read.
+ private class RssDataAdapter<T> extends ArrayAdapter<T> {
+ public RssDataAdapter(Context context, int resource, List objects) {
+ super(context, resource, objects);
+ }
+
+ // Here's our only important override--returning the list item.
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent){
+
+ // Get the item from the underlying array,
+ // Create a TextView wrapper, and change the typeface, if necessary.
+ RssItem item = (RssItem)this.getItem(position);
+ TextView view = new TextView(parent.getContext());
+ view.setText(item.toString());
+
+ if(! item.hasBeenRead){
+ Typeface type = view.getTypeface();
+ view.setTypeface(Typeface.create(type, Typeface.BOLD_ITALIC));
+ }
+ return view;
+ }
+ }
+
+ // Method to initialize our list of RSS items.
+ private ArrayList<RssItem> initializeList(){
+ ArrayList<RssItem> list = new ArrayList<RssItem>();
+ list.add(new RssItem("http://www.sciam.com/xml/sciam.xml", "Scientific American"));
+ list.add(new RssItem("http://newsrss.bbc.co.uk/rss/newsonline_world_edition/front_page/rss.xml", "BBC"));
+ list.add(new RssItem("http://feeds.theonion.com/theonion/daily", "The Onion"));
+ list.add(new RssItem("http://feeds.engadget.com/weblogsinc/engadget", "Engadget"));
+ return list;
+ }
+}
diff --git a/samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader4.java b/samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader4.java
new file mode 100644
index 000000000..bc37786f1
--- /dev/null
+++ b/samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader4.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.codelab.rssexample;
+
+import android.app.Activity;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Typeface;
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.SimpleCursorAdapter;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+
+
+public class MyRssReader4 extends Activity {
+
+ ListView mRssList;
+ Cursor mCur;
+ RssCursorAdapter mAdap;
+ private static final int ADD_ELEMENT_REQUEST = 1;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState){
+ super.onCreate(savedInstanceState);
+
+ // Load screen layout.
+ setContentView(R.layout.main_screen2);
+
+ // Populate ArrayAdapter and bind it to ListView
+ mRssList = (ListView)findViewById(R.id.rssListView);
+
+ mCur = managedQuery(RssContentProvider.CONTENT_URI, // Query for all items.
+ null,
+ null,
+ RssContentProvider.DEFAULT_SORT_ORDER);
+// BEGIN_INCLUDE(4_1)
+ mAdap = new RssCursorAdapter(
+ this,
+ R.layout.list_element, // Our layout resource.
+ mCur,
+ new String[]{RssContentProvider.TITLE}, // Columns to retrieve.
+ new int[]{R.id.list_item}); // IDs of widgets to display
+ mRssList.setAdapter(mAdap); // the corresponding column.
+// END_INCLUDE(4_1)
+
+ // Set the last selected item.
+ // (icicle is only set if this is being restarted).
+ if(savedInstanceState != null && savedInstanceState.containsKey("lastIndexItem")){
+ mRssList.setSelection(savedInstanceState.getInteger("lastIndexItem"));
+ }
+ }
+
+ // Store our state before we are potentially bumped from memory.
+ // We'd like to store the current ListView selection.
+ @Override
+ protected void onSaveInstanceState(Bundle outState){
+ int index = mRssList.getSelectedItemIndex();
+ if(index > -1){
+ outState.putInteger("lastIndexItem", index);
+ }
+ }
+
+
+ // Add our initial menu options. We will tweak this menu when it's loaded swap out
+ // "start service" or "stop service", depending on whether the service is currently running.
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu){
+ // Always call the superclass implementation to
+ // provide standard items.
+ super.onCreateOptionsMenu(menu);
+
+ menu.add(0, 0, R.string.menu_option_start, null);
+ menu.add(0, 1, R.string.menu_option_stop, null);
+ menu.add(0, 2, R.string.menu_option_add, null);
+ menu.add(0, 3, R.string.menu_option_delete, null);
+ menu.add(0, 4, R.string.menu_option_update, null);
+
+ return true;
+ }
+
+ // Toggle out start service/stop service depending on whether the service is running.
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu){
+ return true;
+ }
+
+ // Handle our menu clicks.
+ @Override
+ public boolean onOptionsItemSelected(Menu.Item item){
+ super.onOptionsItemSelected(item);
+
+ switch (item.getId()){
+ case 0: // Start service
+ showAlert(null, "You clicked 'start'!", "ok", null, false, null);
+ break;
+ case 1: // Stop service
+ showAlert(null, "You clicked stop!", "ok", null, false, null);
+ break;
+ case 2: // Add Item
+ Intent addIntent = new Intent(AddRssItem.class);
+
+ // Use an ID so that if we create a "remove item" form we
+ // can tell which form is returning a value.
+ startActivityForResult(addIntent, ADD_ELEMENT_REQUEST);
+ break;
+ case 3: // Delete item.
+ if(mRssList.hasFocus()){
+ int currentSelectionIndex = mRssList.getSelectedItemIndex();
+
+ // Create our content URI by adding the ID of the currently selected item using a
+ // convenience method.
+ Long itemID = mAdap.getItemId(currentSelectionIndex);
+ getContentResolver().delete(RssContentProvider.CONTENT_URI.addId(itemID), null);
+ }
+ break;
+ case 4: // Update all
+ showAlert(null, "You clicked 'Update'!", "ok", null, false, null);
+ break;
+ default:
+ showAlert(null, "I have no idea what you clicked!", "ok", null, false, null);
+ break;
+ }
+ return true;
+ }
+
+ // Called by the "Add RSS Item" floating screen when it closes.
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data){
+ if(resultCode == RESULT_OK){
+ switch (requestCode){
+ case ADD_ELEMENT_REQUEST:
+ ContentValues vals = new ContentValues(4);
+ vals.put(RssContentProvider.TITLE, data.getStringExtra(RssContentProvider.TITLE));
+ vals.put(RssContentProvider.URL, data.getStringExtra(RssContentProvider.URL));
+ vals.put(RssContentProvider.CONTENT, data.getStringExtra(RssContentProvider.CONTENT));
+ vals.put(RssContentProvider.LAST_UPDATED, data.getIntExtra(RssContentProvider.LAST_UPDATED, 0));
+ Uri uri = getContentResolver().insert(
+ RssContentProvider.CONTENT_URI,
+ vals);
+ if(uri != null){
+ mRssList.setSelection(mRssList.getCount() - 1);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Our private ArrayAdapter implementation that returns a bold TextView for
+ // RSS items that are unread, or a normal TextView for items that have been read.
+ private class RssCursorAdapter extends SimpleCursorAdapter {
+ public RssCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to){
+ super(context, layout, c, from, to);
+ }
+
+ // Here's our only important override--returning the list item.
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent){
+ TextView view = (TextView)super.getView(position, convertView, parent);
+
+ if(view != null){
+
+ // Now get the hasBeenRead value to determine the font.
+ int hasBeenReadColumnIndex = getCursor().getColumnIndex(
+ RssContentProvider.HAS_BEEN_READ);
+ boolean hasBeenRead = (getCursor().getInt(hasBeenReadColumnIndex) == 1 ? true : false);
+ if(! hasBeenRead){
+ Typeface type = view.getTypeface();
+ view.setTypeface(Typeface.create(type, Typeface.BOLD_ITALIC));
+ }
+ }
+ return view;
+ }
+ }
+}
+
diff --git a/samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader5.java b/samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader5.java
new file mode 100644
index 000000000..0d59bfd00
--- /dev/null
+++ b/samples/MySampleRss/src/com/example/codelab/rssexample/MyRssReader5.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.codelab.rssexample;
+
+import android.app.Activity;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Typeface;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.WebView;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+import android.widget.TextView;
+import android.widget.AdapterView.OnItemSelectedListener;
+
+import java.util.logging.Logger;
+
+public class MyRssReader5 extends Activity implements OnItemSelectedListener {
+
+ private ListView mRssList;
+ private Cursor mCur;
+ private RssCursorAdapter mAdap;
+ private WebView mWebView;
+ private static final int ADD_ELEMENT_REQUEST = 1;
+ private Logger mLogger = Logger.getLogger(this.getPackageName());
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ //
+ // Load screen layout.
+ setContentView(R.layout.main_screen2);
+
+ // Populate ArrayAdapter and bind it to ListView
+ mRssList = (ListView)findViewById(R.id.rssListView);
+ mRssList.setOnItemSelectedListener(this);
+
+ mWebView = (WebView)findViewById(R.id.rssWebView);
+
+ mCur = managedQuery(RssContentProvider.CONTENT_URI, // Query for all items.
+ null,
+ null,
+ RssContentProvider.DEFAULT_SORT_ORDER);
+
+ mAdap = new RssCursorAdapter(
+ this,
+ R.layout.list_element, // Our layout resource.
+ mCur,
+ new String[]{RssContentProvider.TITLE}, // Columns to retrieve.
+ new int[]{R.id.list_item}); // IDs of widgets to display
+ mRssList.setAdapter(mAdap); // the corresponding column.
+
+ // Set the last selected item.
+ // (icicle is only set if this is being restarted).
+ if(savedInstanceState != null && savedInstanceState.containsKey("lastIndexItem")){
+ mRssList.setSelection(savedInstanceState.getInteger("lastIndexItem"));
+ }
+ }
+
+//BEGIN_INCLUDE(5_4)
+ // Listener to listen for list selection changes, and send the new text to
+ // the web view.
+ public void onItemSelected(AdapterView parent, View v, int position, long id){
+ // Need to nest this in a try block because we get called sometimes
+ // with the index of a recently deleted item.
+ String content = "";
+ try{
+ content = mCur.getString(mCur.getColumnIndex(RssContentProvider.CONTENT));
+ mLogger.info("MyRssReader5 content string:" + content);
+ }
+ catch (Exception e){
+ // This method is sometimes called after a backing data item
+ // is deleted. In that case, we don't want to throw an error.
+ mLogger.warning("MyRssReader5.onItemSelected() couldn't get the content" +
+ "from the cursor " + e.getMessage());
+ }
+ mWebView.loadData(content, "text/html", "utf-8");
+ }
+//END_INCLUDE(5_4)
+
+ public void onNothingSelected(AdapterView parent){
+ mWebView.loadData("<html><body><p>No selection chosen</p></body></html>", "text/html", "utf-8");
+ }
+
+ // Store our state before we are potentially bumped from memory.
+ // We'd like to store the current ListView selection.
+ @Override
+ protected void onSaveInstanceState(Bundle outState){
+ int index = mRssList.getSelectedItemIndex();
+ if(index > -1){
+ outState.putInteger("lastIndexItem", index);
+ }
+ }
+
+ // Add our initial menu options. We will tweak this menu when it's loaded swap out
+ // "start service" or "stop service", depending on whether the service is currently running.
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu){
+ // Always call the superclass implementation to
+ // provide standard items.
+ super.onCreateOptionsMenu(menu);
+
+ menu.add(0, 0, R.string.menu_option_start, null);
+ menu.add(0, 1, R.string.menu_option_stop, null);
+ menu.add(0, 2, R.string.menu_option_add, null);
+ menu.add(0, 3, R.string.menu_option_delete, null);
+ menu.add(0, 4, R.string.menu_option_update, null);
+
+ return true;
+ }
+
+ // Toggle out start service/stop service depending on whether the service is running.
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu){
+ return true;
+ }
+
+ // Handle our menu clicks.
+ @Override
+ public boolean onOptionsItemSelected(Menu.Item item){
+ super.onOptionsItemSelected(item);
+
+ switch (item.getId()){
+ case 0: // Start service
+ Intent basicStartIntent = new Intent(RssService.class);
+ startService(basicStartIntent);
+ break;
+ case 1: // Stop service
+ Intent stopIntent = new Intent(RssService.class);
+ stopService(stopIntent);
+ break;
+ case 2: // Add Item
+ Intent addIntent = new Intent(AddRssItem.class);
+ // Use an ID so that if we create a "remove item" form we
+ // can tell which form is returning a value.
+ startActivityForResult(addIntent, ADD_ELEMENT_REQUEST);
+ break;
+ case 3: // Delete item.
+ if(mRssList.hasFocus()){
+ int currentSelectionIndex = mRssList.getSelectedItemIndex();
+ mLogger.info("MyRssReader5.onOptionsItemSelected(): Deleting list member:" +
+ mRssList.getSelectedItemIndex());
+ // Create our content URI by adding the ID of the currently selected item using a
+ // convenience method.
+ Long itemID = mAdap.getItemId(currentSelectionIndex);
+ getContentResolver().delete(RssContentProvider.CONTENT_URI.addId(itemID), null);
+ }
+ break;
+ case 4: // Requery all
+ Bundle startupVals = new Bundle(1);
+ startupVals.putBoolean(RssService.REQUERY_KEY, true);
+ Intent requeryIntent = new Intent(RssService.class);
+ startService(requeryIntent, startupVals);
+ break;
+ default:
+ showAlert(null, "I have no idea what you clicked!", "ok", null, false, null);
+ break;
+ }
+ return true;
+ }
+
+ // Called by the "Add RSS Item" floating screen when it closes.
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data){
+ if(resultCode == RESULT_OK){
+ switch (requestCode){
+ case ADD_ELEMENT_REQUEST:
+ ContentValues vals = new ContentValues(5);
+ vals.put(RssContentProvider.TITLE, data.getStringExtra(RssContentProvider.TITLE));
+ vals.put(RssContentProvider.URL, data.getStringExtra(RssContentProvider.URL));
+ vals.put(RssContentProvider.CONTENT, data.getStringExtra(RssContentProvider.CONTENT));
+ vals.put(RssContentProvider.LAST_UPDATED, data.getIntExtra(RssContentProvider.LAST_UPDATED, 0));
+ Uri uri = getContentResolver().insert(
+ RssContentProvider.CONTENT_URI,
+ vals);
+ if(uri != null){
+ // Tell the service to requery the service, then set
+ // it as the active selection.
+ Bundle startupVals = new Bundle(1);
+ startupVals.putString(RssService.RSS_URL, data.getStringExtra("URL"));
+ Intent startIntent = new Intent(RssService.class);
+ startIntent.putExtras(startupVals);
+ startService(startIntent);
+ mRssList.setSelection(mRssList.getCount() - 1);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Our private ArrayAdapter implementation that returns a bold TextView for
+ // RSS items that are unread, or a normal TextView for items that have been read.
+ private class RssCursorAdapter extends SimpleCursorAdapter {
+ public RssCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
+ super(context, layout, c, from, to);
+ }
+
+ // Here's our only important override--returning the list item.
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent){
+ TextView view = (TextView)super.getView(position, convertView, parent);
+
+ if(view != null){
+
+ // Now get the hasBeenRead value to determine the font.
+ int hasBeenReadColumnIndex = getCursor().getColumnIndex(RssContentProvider.HAS_BEEN_READ);
+ boolean hasBeenRead = (getCursor().getInt(hasBeenReadColumnIndex) == 1 ? true : false);
+ if(! hasBeenRead){
+ Typeface type = view.getTypeface();
+ view.setTypeface(Typeface.create(type, Typeface.BOLD_ITALIC));
+ }
+ }
+ return view;
+ }
+ }
+
+}
+
diff --git a/samples/MySampleRss/src/com/example/codelab/rssexample/RssContentProvider.java b/samples/MySampleRss/src/com/example/codelab/rssexample/RssContentProvider.java
new file mode 100644
index 000000000..5d523dccb
--- /dev/null
+++ b/samples/MySampleRss/src/com/example/codelab/rssexample/RssContentProvider.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.codelab.rssexample;
+
+import android.content.ContentProvider;
+import android.content.ContentProviderDatabaseHelper;
+import android.content.UriMatcher;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.content.ContentValues;
+import android.text.TextUtils;
+
+import java.util.logging.Logger;
+
+// Content Provider for RSS feed information. Each row describes a single
+// RSS feed. See the public static constants at the end of this class
+// to learn what each record contains.
+public class RssContentProvider extends ContentProvider {
+ private Logger mLogger = Logger.getLogger("com.example.codelab.rssexample");
+ private SQLiteDatabase mDb;
+ private DatabaseHelper mDbHelper = new DatabaseHelper();
+ private static final String DATABASE_NAME = "rssitems.db";
+ private static final String DATABASE_TABLE_NAME = "rssItems";
+ private static final int DB_VERSION = 1;
+ private static final int ALL_MESSAGES = 1;
+ private static final int SPECIFIC_MESSAGE = 2;
+
+ // Set up our URL matchers to help us determine what an
+ // incoming URI parameter is.
+ private static final UriMatcher URI_MATCHER;
+ static{
+ URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
+ URI_MATCHER.addURI("my_rss_item", "rssitem", ALL_MESSAGES);
+ URI_MATCHER.addURI("my_rss_item", "rssitem/#", SPECIFIC_MESSAGE);
+ }
+
+ // Here's the public URI used to query for RSS items.
+ public static final Uri CONTENT_URI = Uri.parse( "content://my_rss_item/rssitem");
+
+ // Here are our column name constants, used to query for field values.
+ public static final String ID = "_id";
+ public static final String URL = "url";
+ public static final String TITLE = "title";
+ public static final String HAS_BEEN_READ = "hasbeenread";
+ public static final String CONTENT = "rawcontent";
+ public static final String LAST_UPDATED = "lastupdated";
+ public static final String DEFAULT_SORT_ORDER = TITLE + " DESC";
+
+ // Database creation/version management helper.
+ // Create it statically because we don't need to have customized instances.
+ private static class DatabaseHelper extends ContentProviderDatabaseHelper{
+
+ @Override
+ public void onCreate(SQLiteDatabase db){
+ try{
+ String sql = "CREATE TABLE " + DATABASE_TABLE_NAME + "(" +
+ ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ URL + " TEXT," +
+ TITLE + " TEXT," +
+ HAS_BEEN_READ + " BOOLEAN DEFAULT 0," +
+ CONTENT + " TEXT," +
+ LAST_UPDATED + " INTEGER DEFAULT 0);";
+ Logger.getLogger("com.example.codelab.rssexample").info("DatabaseHelper.onCreate(): SQL statement: " + sql);
+ db.execSQL(sql);
+ Logger.getLogger("com.example.codelab.rssexample").info("DatabaseHelper.onCreate(): Created a database");
+ } catch (SQLException e) {
+ Logger.getLogger("com.example.codelab.rssexample").warning("DatabaseHelper.onCreate(): Couldn't create a database!");
+ }
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
+ // Don't have any upgrades yet, so if this gets called for some reason we'll
+ // just drop the existing table, and recreate the database with the
+ // standard method.
+ db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE_NAME + ";");
+
+ }
+ }
+
+ @Override
+ public boolean onCreate() {
+ // First we need to open the database. If this is our first time,
+ // the attempt to retrieve a database will throw
+ // FileNotFoundException, and we will then create the database.
+ final Context con = getContext();
+ try{
+ mDb = mDbHelper.openDatabase(getContext(), DATABASE_NAME, null, DB_VERSION);
+ mLogger.info("RssContentProvider.onCreate(): Opened a database");
+ } catch (Exception ex) {
+ return false;
+ }
+ if(mDb == null){
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ // Convert the URI into a custom MIME type.
+ // Our UriMatcher will parse the URI to decide whether the
+ // URI is for a single item or a list.
+ @Override
+ public String getType(Uri uri) {
+ switch (URI_MATCHER.match(uri)){
+ case ALL_MESSAGES:
+ return "vnd.android.cursor.dir/rssitem"; // List of items.
+ case SPECIFIC_MESSAGE:
+ return "vnd.android.cursor.item/rssitem"; // Specific item.
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String groupBy, String having, String sortOrder) {
+ // We won't bother checking the validity of params here, but you should!
+
+ // SQLiteQueryBuilder is the helper class that creates the
+ // proper SQL syntax for us.
+ SQLiteQueryBuilder qBuilder = new SQLiteQueryBuilder();
+
+ // Set the table we're querying.
+ qBuilder.setTables(DATABASE_TABLE_NAME);
+
+ // If the query ends in a specific record number, we're
+ // being asked for a specific record, so set the
+ // WHERE clause in our query.
+ if((URI_MATCHER.match(uri)) == SPECIFIC_MESSAGE){
+ qBuilder.appendWhere("_id=" + uri.getPathLeafId());
+ }
+
+ // Set sort order. If none specified, use default.
+ if(TextUtils.isEmpty(sortOrder)){
+ sortOrder = DEFAULT_SORT_ORDER;
+ }
+
+ // Make the query.
+ Cursor c = qBuilder.query(mDb,
+ projection,
+ selection,
+ selectionArgs,
+ groupBy,
+ having,
+ sortOrder);
+ c.setNotificationUri(getContext().getContentResolver(), uri);
+ return c;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String whereClause) {
+ // NOTE Argument checking code omitted. Check your parameters!
+ int updateCount = mDb.update(DATABASE_TABLE_NAME, values, whereClause);
+
+ // Notify any listeners and return the updated row count.
+ getContext().getContentResolver().notifyUpdate(uri, null);
+ return updateCount;
+ }
+
+ @Override
+ public Uri insert(Uri requestUri, ContentValues initialValues) {
+ // NOTE Argument checking code omitted. Check your parameters! Check that
+ // your row addition request succeeded!
+
+ long rowId = -1;
+ rowId = mDb.insert(DATABASE_TABLE_NAME, "rawcontent", initialValues);
+ Uri newUri = CONTENT_URI.addId(rowId);
+
+ // Notify any listeners and return the URI of the new row.
+ getContext().getContentResolver().notifyInsert(CONTENT_URI, null);
+ return newUri;
+ }
+
+ @Override
+ public int delete(Uri uri, String where) {
+ // NOTE Argument checking code omitted. Check your parameters!
+ int rowCount = mDb.delete(DATABASE_TABLE_NAME, ID + " = " + uri.getPathLeafId());
+
+ // Notify any listeners and return the deleted row count.
+ getContext().getContentResolver().notifyDelete(uri, null);
+ return rowCount;
+ }
+}
diff --git a/samples/MySampleRss/src/com/example/codelab/rssexample/RssItem.java b/samples/MySampleRss/src/com/example/codelab/rssexample/RssItem.java
new file mode 100644
index 000000000..f13357d8d
--- /dev/null
+++ b/samples/MySampleRss/src/com/example/codelab/rssexample/RssItem.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.codelab.rssexample;
+
+import java.util.Date;
+
+// Custom class to hold an RSS item.
+public class RssItem{
+ public String url;
+ public String title;
+ public boolean hasBeenRead = false;
+ public String content;
+ public Date lastUpdated;
+
+ public RssItem(String url, String title){
+ this.url = url;
+ this.title = title;
+ }
+
+ @Override public String toString(){
+ return title;
+ }
+}
diff --git a/samples/MySampleRss/src/com/example/codelab/rssexample/RssService.java b/samples/MySampleRss/src/com/example/codelab/rssexample/RssService.java
new file mode 100644
index 000000000..e84f89d22
--- /dev/null
+++ b/samples/MySampleRss/src/com/example/codelab/rssexample/RssService.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.codelab.rssexample;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Bundle;
+import android.database.Cursor;
+import android.content.ContentResolver;
+import android.os.Handler;
+import android.text.TextUtils;
+import java.io.BufferedReader;
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.lang.StringBuilder;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.util.GregorianCalendar;
+import java.text.SimpleDateFormat;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.text.ParseException;
+
+public class RssService extends Service implements Runnable{
+ private Logger mLogger = Logger.getLogger(this.getPackageName());
+ public static final String REQUERY_KEY = "Requery_All"; // Sent to tell us force a requery.
+ public static final String RSS_URL = "RSS_URL"; // Sent to tell us to requery a specific item.
+ private NotificationManager mNM;
+ private Cursor mCur; // RSS content provider cursor.
+ private GregorianCalendar mLastCheckedTime; // Time we last checked our feeds.
+ private final String LAST_CHECKED_PREFERENCE = "last_checked";
+ static final int UPDATE_FREQUENCY_IN_MINUTES = 60;
+ private Handler mHandler; // Handler to trap our update reminders.
+ private final int NOTIFY_ID = 1; // Identifies our service icon in the icon tray.
+
+ @Override
+ protected void onCreate(){
+ // Display an icon to show that the service is running.
+ mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ Intent clickIntent = new Intent(Intent.ACTION_MAIN);
+ clickIntent.setClassName(MyRssReader5.class.getName());
+ Notification note = new Notification(this, R.drawable.rss_icon, "RSS Service",
+ clickIntent, null);
+ mNM.notify(NOTIFY_ID, note);
+ mHandler = new Handler();
+
+ // Create the intent that will be launched if the user clicks the
+ // icon on the status bar. This will launch our RSS Reader app.
+ Intent intent = new Intent(MyRssReader.class);
+
+ // Get a cursor over the RSS items.
+ ContentResolver rslv = getContentResolver();
+ mCur = rslv.query(RssContentProvider.CONTENT_URI, null, null, null, null);
+
+ // Load last updated value.
+ // We store last updated value in preferences.
+ SharedPreferences pref = getSharedPreferences("", 0);
+ mLastCheckedTime = new GregorianCalendar();
+ mLastCheckedTime.setTimeInMillis(pref.getLong(LAST_CHECKED_PREFERENCE, 0));
+
+//BEGIN_INCLUDE(5_1)
+ // Need to run ourselves on a new thread, because
+ // we will be making resource-intensive HTTP calls.
+ // Our run() method will check whether we need to requery
+ // our sources.
+ Thread thr = new Thread(null, this, "rss_service_thread");
+ thr.start();
+//END_INCLUDE(5_1)
+ mLogger.info("RssService created");
+ }
+
+//BEGIN_INCLUDE(5_3)
+ // A cheap way to pass a message to tell the service to requery.
+ @Override
+ protected void onStart(Intent intent, int startId){
+ super.onStart(startId, arguments);
+ Bundle arguments = intent.getExtras();
+ if(arguments != null) {
+ if(arguments.containsKey(REQUERY_KEY)) {
+ queryRssItems();
+ }
+ if(arguments.containsKey(RSS_URL)) {
+ // Typically called after adding a new RSS feed to the list.
+ queryItem(arguments.getString(RSS_URL));
+ }
+ }
+ }
+//END_INCLUDE(5_3)
+
+ // When the service is destroyed, get rid of our persistent icon.
+ @Override
+ protected void onDestroy(){
+ mNM.cancel(NOTIFY_ID);
+ }
+
+ // Determines whether the next scheduled check time has passed.
+ // Loads this value from a stored preference. If it has (or if no
+ // previous value has been stored), it will requery all RSS feeds;
+ // otherwise, it will post a delayed reminder to check again after
+ // now - next_check_time milliseconds.
+ public void queryIfPeriodicRefreshRequired() {
+ GregorianCalendar nextCheckTime = new GregorianCalendar();
+ nextCheckTime = (GregorianCalendar) mLastCheckedTime.clone();
+ nextCheckTime.add(GregorianCalendar.MINUTE, UPDATE_FREQUENCY_IN_MINUTES);
+ mLogger.info("last checked time:" + mLastCheckedTime.toString() + " Next checked time: " + nextCheckTime.toString());
+
+ if(mLastCheckedTime.before(nextCheckTime)) {
+ queryRssItems();
+ } else {
+ // Post a message to query again when we get to the next check time.
+ long timeTillNextUpdate = mLastCheckedTime.getTimeInMillis() - GregorianCalendar.getInstance().getTimeInMillis();
+ mHandler.postDelayed(this, 1000 * 60 * UPDATE_FREQUENCY_IN_MINUTES);
+ }
+
+ }
+
+ // Query all feeds. If the new feed has a newer pubDate than the previous,
+ // then update it.
+ void queryRssItems(){
+ mLogger.info("Querying Rss feeds...");
+
+ // The cursor might have gone stale. Requery to be sure.
+ // We need to call next() after a requery to get to the
+ // first record.
+ mCur.requery();
+ while (mCur.next()){
+ // Get the URL for the feed from the cursor.
+ int urlColumnIndex = mCur.getColumnIndex(RssContentProvider.URL);
+ String url = mCur.getString(urlColumnIndex);
+ queryItem(url);
+ }
+ // Reset the global "last checked" time
+ mLastCheckedTime.setTimeInMillis(System.currentTimeMillis());
+
+ // Post a message to query again in [update_frequency] minutes
+ mHandler.postDelayed(this, 1000 * 60 * UPDATE_FREQUENCY_IN_MINUTES);
+ }
+
+
+ // Query an individual RSS feed. Returns true if successful, false otherwise.
+ private boolean queryItem(String url) {
+ try {
+ URL wrappedUrl = new URL(url);
+ String rssFeed = readRss(wrappedUrl);
+ mLogger.info("RSS Feed " + url + ":\n " + rssFeed);
+ if(TextUtils.isEmpty(rssFeed)) {
+ return false;
+ }
+
+ // Parse out the feed update date, and compare to the current version.
+ // If feed update time is newer, or zero (if never updated, for new
+ // items), then update the content, date, and hasBeenRead fields.
+ // lastUpdated = <rss><channel><pubDate>value</pubDate></channel></rss>.
+ // If that value doesn't exist, the current date is used.
+ GregorianCalendar feedPubDate = parseRssDocPubDate(rssFeed);
+ GregorianCalendar lastUpdated = new GregorianCalendar();
+ int lastUpdatedColumnIndex = mCur.getColumnIndex(RssContentProvider.LAST_UPDATED);
+ lastUpdated.setTimeInMillis(mCur.getLong(lastUpdatedColumnIndex));
+ if(lastUpdated.getTimeInMillis() == 0 ||
+ lastUpdated.before(feedPubDate) && !TextUtils.isEmpty(rssFeed)) {
+ // Get column indices.
+ int contentColumnIndex = mCur.getColumnIndex(RssContentProvider.CONTENT);
+ int updatedColumnIndex = mCur.getColumnIndex(RssContentProvider.HAS_BEEN_READ);
+
+ // Update values.
+ mCur.updateString(contentColumnIndex, rssFeed);
+ mCur.updateLong(lastUpdatedColumnIndex, feedPubDate.getTimeInMillis());
+ mCur.updateInt(updatedColumnIndex, 0);
+ mCur.commitUpdates();
+ }
+ } catch (MalformedURLException ex) {
+ mLogger.warning("Error in queryItem: Bad url");
+ return false;
+ }
+ return true;
+ }
+
+ // BEGIN_INCLUDE(5_2)
+ // Get the <pubDate> content from a feed and return a
+ // GregorianCalendar version of the date.
+ // If the element doesn't exist or otherwise can't be
+ // found, return a date of 0 to force a refresh.
+ private GregorianCalendar parseRssDocPubDate(String xml){
+ GregorianCalendar cal = new GregorianCalendar();
+ cal.setTimeInMillis(0);
+ String patt ="<[\\s]*pubDate[\\s]*>(.+?)</pubDate[\\s]*>";
+ Pattern p = Pattern.compile(patt);
+ Matcher m = p.matcher(xml);
+ try {
+ if(m.find()) {
+ mLogger.info("pubDate: " + m.group());
+ SimpleDateFormat pubDate = new SimpleDateFormat();
+ cal.setTime(pubDate.parse(m.group(1)));
+ }
+ } catch(ParseException ex) {
+ mLogger.warning("parseRssDocPubDate couldn't find a <pubDate> tag. Returning default value.");
+ }
+ return cal;
+ }
+
+ // Read the submitted RSS page.
+ String readRss(URL url){
+ String html = "<html><body><h2>No data</h2></body></html>";
+ try {
+ mLogger.info("URL is:" + url.toString());
+ BufferedReader inStream =
+ new BufferedReader(new InputStreamReader(url.openStream()),
+ 1024);
+ String line;
+ StringBuilder rssFeed = new StringBuilder();
+ while ((line = inStream.readLine()) != null){
+ rssFeed.append(line);
+ }
+ html = rssFeed.toString();
+ } catch(IOException ex) {
+ mLogger.warning("Couldn't open an RSS stream");
+ }
+ return html;
+ }
+//END_INCLUDE(5_2)
+
+ // Callback we send to ourself to requery all feeds.
+ public void run() {
+ queryIfPeriodicRefreshRequired();
+ }
+
+ // Required by Service. We won't implement it here, but need to
+ // include this basic code.
+ @Override
+ public IBinder onBind(Intent intent){
+ return mBinder;
+ }
+
+ // This is the object that receives RPC calls from clients.See
+ // RemoteService for a more complete example.
+ private final IBinder mBinder = new Binder() {
+ @Override
+ protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
+ return super.onTransact(code, data, reply, flags);
+ }
+ };
+}