cherrypick from master cl Change-Id: I45f91fd9cfe0cb89789017deec63565e4757063e
Adding the NewsReader sample.
This sample demonstrates how to design a UI for multiple screens and
is part of the "Designing for Multiple Screens" AndroidU lesson.
Change-Id: I462261933c68b3a40f9611fcf7153c25abdfcd5d
diff --git a/samples/training/multiscreen/newsreader/AndroidManifest.xml b/samples/training/multiscreen/newsreader/AndroidManifest.xml
new file mode 100644
index 0000000..04ddf29
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.newsreader"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="14" />
+
+ <application android:icon="@drawable/icon" android:label="@string/app_name"
+ android:logo="@drawable/logo" android:theme="@style/NewsReaderStyle">
+ <activity android:name=".NewsReaderActivity" android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".ArticleActivity"
+ android:theme="@style/NewsReaderStyle_NoActionBar" />
+ </application>
+ <supports-screens
+ android:smallScreens="true"
+ android:normalScreens="true"
+ android:largeScreens="true"
+ android:xlargeScreens="true"
+ />
+</manifest>
diff --git a/samples/training/multiscreen/newsreader/proguard.cfg b/samples/training/multiscreen/newsreader/proguard.cfg
new file mode 100644
index 0000000..b1cdf17
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/proguard.cfg
@@ -0,0 +1,40 @@
+-optimizationpasses 5
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-dontpreverify
+-verbose
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+-keep public class com.android.vending.licensing.ILicensingService
+
+-keepclasseswithmembernames class * {
+ native <methods>;
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclassmembers class * extends android.app.Activity {
+ public void *(android.view.View);
+}
+
+-keepclassmembers enum * {
+ public static **[] values();
+ public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable {
+ public static final android.os.Parcelable$Creator *;
+}
diff --git a/samples/training/multiscreen/newsreader/project.properties b/samples/training/multiscreen/newsreader/project.properties
new file mode 100644
index 0000000..730e911
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/project.properties
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-14
diff --git a/samples/training/multiscreen/newsreader/res/drawable-hdpi/button_normal.9.png b/samples/training/multiscreen/newsreader/res/drawable-hdpi/button_normal.9.png
new file mode 100644
index 0000000..1f53bc9
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-hdpi/button_normal.9.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-hdpi/button_pressed.9.png b/samples/training/multiscreen/newsreader/res/drawable-hdpi/button_pressed.9.png
new file mode 100644
index 0000000..71d157e
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-hdpi/button_pressed.9.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-hdpi/icon.png b/samples/training/multiscreen/newsreader/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..a40bd0e
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-hdpi/logo.png b/samples/training/multiscreen/newsreader/res/drawable-hdpi/logo.png
new file mode 100644
index 0000000..858562a
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-hdpi/logo.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-hdpi/tab_bg_normal.9.png b/samples/training/multiscreen/newsreader/res/drawable-hdpi/tab_bg_normal.9.png
new file mode 100644
index 0000000..51799f8
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-hdpi/tab_bg_normal.9.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-hdpi/tab_bg_selected.9.png b/samples/training/multiscreen/newsreader/res/drawable-hdpi/tab_bg_selected.9.png
new file mode 100644
index 0000000..f6873f3
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-hdpi/tab_bg_selected.9.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-ldpi/button_pressed.9.png b/samples/training/multiscreen/newsreader/res/drawable-ldpi/button_pressed.9.png
new file mode 100644
index 0000000..be34147
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-ldpi/button_pressed.9.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-ldpi/icon.png b/samples/training/multiscreen/newsreader/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..4a733c5
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-ldpi/icon.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-ldpi/logo.png b/samples/training/multiscreen/newsreader/res/drawable-ldpi/logo.png
new file mode 100644
index 0000000..58cc2b7
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-ldpi/logo.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-ldpi/tab_bg_normal.9.png b/samples/training/multiscreen/newsreader/res/drawable-ldpi/tab_bg_normal.9.png
new file mode 100644
index 0000000..64eefea
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-ldpi/tab_bg_normal.9.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-ldpi/tab_bg_selected.9.png b/samples/training/multiscreen/newsreader/res/drawable-ldpi/tab_bg_selected.9.png
new file mode 100644
index 0000000..23cbccc
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-ldpi/tab_bg_selected.9.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-mdpi/button_normal.9.png b/samples/training/multiscreen/newsreader/res/drawable-mdpi/button_normal.9.png
new file mode 100644
index 0000000..23733d7
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-mdpi/button_normal.9.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-mdpi/button_pressed.9.png b/samples/training/multiscreen/newsreader/res/drawable-mdpi/button_pressed.9.png
new file mode 100644
index 0000000..d228e60
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-mdpi/button_pressed.9.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-mdpi/icon.png b/samples/training/multiscreen/newsreader/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..6b42690
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-mdpi/logo.png b/samples/training/multiscreen/newsreader/res/drawable-mdpi/logo.png
new file mode 100644
index 0000000..397f96a
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-mdpi/logo.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-mdpi/tab_bg_normal.9.png b/samples/training/multiscreen/newsreader/res/drawable-mdpi/tab_bg_normal.9.png
new file mode 100644
index 0000000..5406bb3
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-mdpi/tab_bg_normal.9.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-mdpi/tab_bg_selected.9.png b/samples/training/multiscreen/newsreader/res/drawable-mdpi/tab_bg_selected.9.png
new file mode 100644
index 0000000..c6d3a7c
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-mdpi/tab_bg_selected.9.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-xhdpi/button_normal.9.png b/samples/training/multiscreen/newsreader/res/drawable-xhdpi/button_normal.9.png
new file mode 100644
index 0000000..2bd3843
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-xhdpi/button_normal.9.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-xhdpi/button_pressed.9.png b/samples/training/multiscreen/newsreader/res/drawable-xhdpi/button_pressed.9.png
new file mode 100644
index 0000000..50921cb
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-xhdpi/button_pressed.9.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-xhdpi/icon.png b/samples/training/multiscreen/newsreader/res/drawable-xhdpi/icon.png
new file mode 100644
index 0000000..76a8868
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-xhdpi/icon.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-xhdpi/logo.png b/samples/training/multiscreen/newsreader/res/drawable-xhdpi/logo.png
new file mode 100644
index 0000000..3400164
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-xhdpi/logo.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-xhdpi/tab_bg_normal.9.png b/samples/training/multiscreen/newsreader/res/drawable-xhdpi/tab_bg_normal.9.png
new file mode 100644
index 0000000..028afde
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-xhdpi/tab_bg_normal.9.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable-xhdpi/tab_bg_selected.9.png b/samples/training/multiscreen/newsreader/res/drawable-xhdpi/tab_bg_selected.9.png
new file mode 100644
index 0000000..5de99e9
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable-xhdpi/tab_bg_selected.9.png
Binary files differ
diff --git a/samples/training/multiscreen/newsreader/res/drawable/button_bg.xml b/samples/training/multiscreen/newsreader/res/drawable/button_bg.xml
new file mode 100644
index 0000000..cf7b9d5
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable/button_bg.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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_pressed="true"
+ android:drawable="@drawable/button_pressed" />
+ <item android:state_focused="true"
+ android:drawable="@drawable/button_pressed" />
+ <item android:state_selected="true"
+ android:drawable="@drawable/button_pressed" />
+ <item android:drawable="@drawable/button_normal" />
+</selector>
\ No newline at end of file
diff --git a/samples/training/multiscreen/newsreader/res/drawable/tab_bg.xml b/samples/training/multiscreen/newsreader/res/drawable/tab_bg.xml
new file mode 100644
index 0000000..39a8119
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/drawable/tab_bg.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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_selected="true"
+ android:drawable="@drawable/tab_bg_selected" />
+ <item android:drawable="@drawable/tab_bg_normal" />
+</selector>
diff --git a/samples/training/multiscreen/newsreader/res/layout/actionbar_list_item.xml b/samples/training/multiscreen/newsreader/res/layout/actionbar_list_item.xml
new file mode 100644
index 0000000..452d16b
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/layout/actionbar_list_item.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="14sp"
+ android:textColor="#01511a"
+ android:padding="5dp">
+</TextView>
diff --git a/samples/training/multiscreen/newsreader/res/layout/headline_item.xml b/samples/training/multiscreen/newsreader/res/layout/headline_item.xml
new file mode 100644
index 0000000..980e43c
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/layout/headline_item.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="16sp"
+ android:textColor="#01511a"
+ android:padding="10dp">
+</TextView>
diff --git a/samples/training/multiscreen/newsreader/res/layout/onepane.xml b/samples/training/multiscreen/newsreader/res/layout/onepane.xml
new file mode 100644
index 0000000..83eeb86
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/layout/onepane.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <fragment android:id="@+id/headlines" android:layout_height="fill_parent"
+ android:name="com.example.android.newsreader.HeadlinesFragment"
+ android:layout_width="match_parent" />
+
+</LinearLayout>
diff --git a/samples/training/multiscreen/newsreader/res/layout/onepane_with_bar.xml b/samples/training/multiscreen/newsreader/res/layout/onepane_with_bar.xml
new file mode 100644
index 0000000..0ff3a24
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/layout/onepane_with_bar.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout android:layout_width="match_parent" android:id="@+id/linearLayout1" android:gravity="center" android:layout_height="50dp">
+ <ImageView android:id="@+id/imageView1" android:layout_height="wrap_content" android:layout_width="wrap_content" android:src="@drawable/logo" android:paddingRight="30dp" android:layout_gravity="left" android:layout_weight="0"></ImageView>
+ <View android:layout_height="wrap_content" android:id="@+id/view1" android:layout_width="wrap_content" android:layout_weight="1"></View>
+ <Button android:id="@+id/categorybutton" android:text="one" android:background="@drawable/button_bg" android:layout_height="match_parent" android:layout_weight="0" android:layout_width="120dp" style="@style/CategoryButtonStyle"/>
+ </LinearLayout>
+
+ <fragment android:id="@+id/headlines" android:layout_height="fill_parent"
+ android:name="com.example.android.newsreader.HeadlinesFragment"
+ android:layout_width="match_parent" />
+
+</LinearLayout>
diff --git a/samples/training/multiscreen/newsreader/res/layout/twopanes.xml b/samples/training/multiscreen/newsreader/res/layout/twopanes.xml
new file mode 100644
index 0000000..96c958a
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/layout/twopanes.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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="horizontal">
+ <fragment android:id="@+id/headlines" android:layout_height="fill_parent"
+ android:name="com.example.android.newsreader.HeadlinesFragment"
+ android:layout_width="400dp" android:layout_marginRight="10dp"/>
+ <fragment android:id="@+id/article" android:layout_height="fill_parent"
+ android:name="com.example.android.newsreader.ArticleFragment"
+ android:layout_width="fill_parent" />
+</LinearLayout>
diff --git a/samples/training/multiscreen/newsreader/res/layout/twopanes_narrow.xml b/samples/training/multiscreen/newsreader/res/layout/twopanes_narrow.xml
new file mode 100644
index 0000000..98d9516
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/layout/twopanes_narrow.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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="horizontal">
+ <fragment android:id="@+id/headlines" android:layout_height="fill_parent"
+ android:name="com.example.android.newsreader.HeadlinesFragment"
+ android:layout_width="200dp" android:layout_marginRight="10dp"/>
+ <fragment android:id="@+id/article" android:layout_height="fill_parent"
+ android:name="com.example.android.newsreader.ArticleFragment"
+ android:layout_width="fill_parent" />
+</LinearLayout>
diff --git a/samples/training/multiscreen/newsreader/res/values-sw600dp-land/main.xml b/samples/training/multiscreen/newsreader/res/values-sw600dp-land/main.xml
new file mode 100644
index 0000000..85e5966
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/values-sw600dp-land/main.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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>
+ <item name="main_layout" type="layout">@layout/twopanes</item>
+ <string name="has_two_panes">true</string>
+</resources>
diff --git a/samples/training/multiscreen/newsreader/res/values-sw600dp-port/main.xml b/samples/training/multiscreen/newsreader/res/values-sw600dp-port/main.xml
new file mode 100644
index 0000000..4d032dd
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/values-sw600dp-port/main.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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>
+ <item name="main_layout" type="layout">@layout/onepane</item>
+ <string name="has_two_panes">false</string>
+</resources>
diff --git a/samples/training/multiscreen/newsreader/res/values-v11/main.xml b/samples/training/multiscreen/newsreader/res/values-v11/main.xml
new file mode 100644
index 0000000..cb82bb8
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/values-v11/main.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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>
+ <item name="main_layout" type="layout">@layout/onepane</item>
+</resources>
diff --git a/samples/training/multiscreen/newsreader/res/values-v11/style.xml b/samples/training/multiscreen/newsreader/res/values-v11/style.xml
new file mode 100644
index 0000000..566a689
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/values-v11/style.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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="CustomActionBarTabTextStyle">
+ <item name="android:textColor">#017729</item>
+ <item name="android:textSize">20sp</item>
+ <item name="android:typeface">sans</item>
+</style>
+<style name="CustomActionBarTabStyle" parent="android:style/Widget.Holo.Light.ActionBar.TabView">
+ <item name="android:background">@drawable/tab_bg</item>
+ <item name="android:paddingLeft">20dp</item>
+ <item name="android:paddingRight">20dp</item>
+</style>
+<style name="NewsReaderStyle" parent="android:Theme.Holo.Light">
+ <item name="android:actionBarTabTextStyle">@style/CustomActionBarTabTextStyle</item>
+ <item name="android:actionBarTabStyle">@style/CustomActionBarTabStyle</item>
+</style>
+
+<style name="NewsReaderStyle_NoActionBar" parent="android:Theme.Holo.Light.NoActionBar">
+</style>
+
+</resources>
diff --git a/samples/training/multiscreen/newsreader/res/values-xlarge-land/main.xml b/samples/training/multiscreen/newsreader/res/values-xlarge-land/main.xml
new file mode 100644
index 0000000..85e5966
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/values-xlarge-land/main.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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>
+ <item name="main_layout" type="layout">@layout/twopanes</item>
+ <string name="has_two_panes">true</string>
+</resources>
diff --git a/samples/training/multiscreen/newsreader/res/values-xlarge-port/main.xml b/samples/training/multiscreen/newsreader/res/values-xlarge-port/main.xml
new file mode 100644
index 0000000..90e6a4a
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/values-xlarge-port/main.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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>
+ <item name="main_layout" type="layout">@layout/twopanes_narrow</item>
+ <string name="has_two_panes">true</string>
+</resources>
diff --git a/samples/training/multiscreen/newsreader/res/values/main.xml b/samples/training/multiscreen/newsreader/res/values/main.xml
new file mode 100644
index 0000000..c598762
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/values/main.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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>
+ <item name="main_layout" type="layout">@layout/onepane_with_bar</item>
+ <string name="has_two_panes">false</string>
+</resources>
diff --git a/samples/training/multiscreen/newsreader/res/values/strings.xml b/samples/training/multiscreen/newsreader/res/values/strings.xml
new file mode 100644
index 0000000..e4c4fe2
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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="hello">Hello World, NewsReaderActivity!</string>
+ <string name="app_name">NewsReader</string>
+</resources>
diff --git a/samples/training/multiscreen/newsreader/res/values/style.xml b/samples/training/multiscreen/newsreader/res/values/style.xml
new file mode 100644
index 0000000..ddbd1c0
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/res/values/style.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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="NewsReaderStyle" parent="android:Theme.Light.NoTitleBar">
+</style>
+
+<style name="NewsReaderStyle_NoActionBar" parent="android:Theme.Light.NoTitleBar">
+</style>
+
+<style name="CategoryButtonStyle" parent="@android:style/Widget.Button">
+ <item name="android:textColor">#ffffff</item>
+</style>
+
+</resources>
diff --git a/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/ArticleActivity.java b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/ArticleActivity.java
new file mode 100644
index 0000000..2aff792
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/ArticleActivity.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2011 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.newsreader;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+
+/**
+ * Activity that displays a particular news article onscreen.
+ *
+ * This activity is started only when the screen is not large enough for a two-pane layout, in
+ * which case this separate activity is shown in order to display the news article. This activity
+ * kills itself if the display is reconfigured into a shape that allows a two-pane layout, since
+ * in that case the news article will be displayed by the {@link NewsReaderActivity} and this
+ * Activity therefore becomes unnecessary.
+ */
+public class ArticleActivity extends FragmentActivity {
+ // The news category index and the article index for the article we are to display
+ int mCatIndex, mArtIndex;
+
+ /**
+ * Sets up the activity.
+ *
+ * Setting up the activity means reading the category/article index from the Intent that
+ * fired this Activity and loading it onto the UI. We also detect if there has been a
+ * screen configuration change (in particular, a rotation) that makes this activity
+ * unnecessary, in which case we do the honorable thing and get out of the way.
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mCatIndex = getIntent().getExtras().getInt("catIndex", 0);
+ mArtIndex = getIntent().getExtras().getInt("artIndex", 0);
+
+ // If we are in two-pane layout mode and the device is in landscape orientation, we are
+ // no longer necessary
+ if (getResources().getString(R.string.has_two_panes).equals("true") &&
+ getResources().getConfiguration().orientation ==
+ Configuration.ORIENTATION_LANDSCAPE) {
+ finish();
+ return;
+ }
+
+ // Place an ArticleFragment as our content pane
+ ArticleFragment f = new ArticleFragment();
+ getSupportFragmentManager().beginTransaction().add(android.R.id.content, f).commit();
+
+ // Display the correct news article on the fragment
+ NewsArticle article = NewsSource.getInstance().getCategory(mCatIndex).getArticle(mArtIndex);
+ f.displayArticle(article);
+ }
+}
diff --git a/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/ArticleFragment.java b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/ArticleFragment.java
new file mode 100644
index 0000000..075e54f
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/ArticleFragment.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 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.newsreader;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.WebView;
+
+/**
+ * Fragment that displays a news article.
+ */
+public class ArticleFragment extends Fragment {
+ // The webview where we display the article (our only view)
+ WebView mWebView;
+
+ // The article we are to display
+ NewsArticle mNewsArticle = null;
+
+ // Parameterless constructor is needed by framework
+ public ArticleFragment() {
+ super();
+ }
+
+ /**
+ * Sets up the UI. It consists if a single WebView.
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ mWebView = new WebView(getActivity());
+ loadWebView();
+ return mWebView;
+ }
+
+ /**
+ * Displays a particular article.
+ *
+ * @param article the article to display
+ */
+ public void displayArticle(NewsArticle article) {
+ mNewsArticle = article;
+ loadWebView();
+ }
+
+ /**
+ * Loads article data into the webview.
+ *
+ * This method is called internally to update the webview's contents to the appropriate
+ * article's text.
+ */
+ void loadWebView() {
+ if (mWebView != null) {
+ mWebView.loadData(mNewsArticle == null ? "" : mNewsArticle.getBody(), "text/html",
+ "utf-8");
+ }
+ }
+}
diff --git a/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/CompatActionBarNavHandler.java b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/CompatActionBarNavHandler.java
new file mode 100644
index 0000000..e9bad7e
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/CompatActionBarNavHandler.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2011 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.newsreader;
+
+import android.app.ActionBar.OnNavigationListener;
+import android.app.ActionBar.Tab;
+import android.app.ActionBar.TabListener;
+import android.app.FragmentTransaction;
+
+/**
+ * Adapter for action bar navigation events.
+ *
+ * This class implements an adapter that facilitates handling of action bar navigation events.
+ * An instance of this class must be installed as a TabListener or OnNavigationListener on an
+ * Action Bar, and it will relay the navigation events to a configured listener
+ * (a {@link CompatActionBarNavListener}).
+ *
+ * This class should only be instanced and used on Android platforms that support the Action Bar,
+ * that is, SDK level 11 and above.
+ */
+public class CompatActionBarNavHandler implements TabListener, OnNavigationListener {
+ // The listener that we notify of navigation events
+ CompatActionBarNavListener mNavListener;
+
+ /**
+ * Constructs an instance with the given listener.
+ *
+ * @param listener the listener to notify when a navigation event occurs.
+ */
+ public CompatActionBarNavHandler(CompatActionBarNavListener listener) {
+ mNavListener = listener;
+ }
+
+ /**
+ * Called by framework when a tab is selected.
+ *
+ * This will cause a navigation event to be delivered to the configured listener.
+ */
+ @Override
+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
+ // TODO Auto-generated method stub
+ mNavListener.onCategorySelected(tab.getPosition());
+ }
+
+ /**
+ * Called by framework when a item on the navigation menu is selected.
+ *
+ * This will cause a navigation event to be delivered to the configured listener.
+ */
+ @Override
+ public boolean onNavigationItemSelected(int itemPosition, long itemId) {
+ mNavListener.onCategorySelected(itemPosition);
+ return true;
+ }
+
+
+ /**
+ * Called by framework when a tab is re-selected. That is, it was already selected and is
+ * tapped on again. This is not used in our app.
+ */
+ @Override
+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ // we don't care
+ }
+
+ /**
+ * Called by framework when a tab is unselected. Not used in our app.
+ */
+ @Override
+ public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+ // we don't care
+ }
+
+}
diff --git a/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/CompatActionBarNavListener.java b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/CompatActionBarNavListener.java
new file mode 100644
index 0000000..9a3f933
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/CompatActionBarNavListener.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 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.newsreader;
+
+/**
+ * A listener that listens to navigation events.
+ *
+ * Represents a listener for navigation events delivered by {@link CompatActionBarNavHandler}.
+ */
+public interface CompatActionBarNavListener {
+ /**
+ * Signals that the given news category was selected.
+ * @param catIndex the selected category's index.
+ */
+ public void onCategorySelected(int catIndex);
+}
diff --git a/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/HeadlinesFragment.java b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/HeadlinesFragment.java
new file mode 100644
index 0000000..99d02fa
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/HeadlinesFragment.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2011 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.newsreader;
+
+import android.os.Bundle;
+import android.support.v4.app.ListFragment;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fragment that displays the news headlines for a particular news category.
+ *
+ * This Fragment displays a list with the news headlines for a particular news category.
+ * When an item is selected, it notifies the configured listener that a headlines was selected.
+ */
+public class HeadlinesFragment extends ListFragment implements OnItemClickListener {
+ // The list of headlines that we are displaying
+ List<String> mHeadlinesList = new ArrayList<String>();
+
+ // The list adapter for the list we are displaying
+ ArrayAdapter<String> mListAdapter;
+
+ // The listener we are to notify when a headline is selected
+ OnHeadlineSelectedListener mHeadlineSelectedListener = null;
+
+ /**
+ * Represents a listener that will be notified of headline selections.
+ */
+ public interface OnHeadlineSelectedListener {
+ /**
+ * Called when a given headline is selected.
+ * @param index the index of the selected headline.
+ */
+ public void onHeadlineSelected(int index);
+ }
+
+ /**
+ * Default constructor required by framework.
+ */
+ public HeadlinesFragment() {
+ super();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ setListAdapter(mListAdapter);
+ getListView().setOnItemClickListener(this);
+ loadCategory(0);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mListAdapter = new ArrayAdapter<String>(getActivity(), R.layout.headline_item,
+ mHeadlinesList);
+ }
+
+ /**
+ * Sets the listener that should be notified of headline selection events.
+ * @param listener the listener to notify.
+ */
+ public void setOnHeadlineSelectedListener(OnHeadlineSelectedListener listener) {
+ mHeadlineSelectedListener = listener;
+ }
+
+ /**
+ * Load and display the headlines for the given news category.
+ * @param categoryIndex the index of the news category to display.
+ */
+ public void loadCategory(int categoryIndex) {
+ mHeadlinesList.clear();
+ int i;
+ NewsCategory cat = NewsSource.getInstance().getCategory(categoryIndex);
+ for (i = 0; i < cat.getArticleCount(); i++) {
+ mHeadlinesList.add(cat.getArticle(i).getHeadline());
+ }
+ mListAdapter.notifyDataSetChanged();
+ }
+
+ /**
+ * Handles a click on a headline.
+ *
+ * This causes the configured listener to be notified that a headline was selected.
+ */
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if (null != mHeadlineSelectedListener) {
+ mHeadlineSelectedListener.onHeadlineSelected(position);
+ }
+ }
+
+ /** Sets choice mode for the list
+ *
+ * @param selectable whether list is to be selectable.
+ */
+ public void setSelectable(boolean selectable) {
+ if (selectable) {
+ getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+ }
+ else {
+ getListView().setChoiceMode(ListView.CHOICE_MODE_NONE);
+ }
+ }
+}
diff --git a/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/NewsArticle.java b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/NewsArticle.java
new file mode 100644
index 0000000..9feaee2
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/NewsArticle.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2011 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.newsreader;
+
+/**
+ * A news article.
+ *
+ * An article consists of a headline and a body. In this example app, article text is dynamically
+ * generated nonsense.
+ */
+public class NewsArticle {
+ // How many sentences in each paragraph?
+ final int SENTENCES_PER_PARAGRAPH = 20;
+
+ // How many paragraphs in each article?
+ final int PARAGRAPHS_PER_ARTICLE = 5;
+
+ // Headline and body
+ String mHeadline, mBody;
+
+ /**
+ * Create a news article with randomly generated text.
+ * @param ngen the nonsense generator to use.
+ */
+ public NewsArticle(NonsenseGenerator ngen) {
+ mHeadline = ngen.makeHeadline();
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("<html><body>");
+ sb.append("<h1>" + mHeadline + "</h1>");
+ int i;
+ for (i = 0; i < PARAGRAPHS_PER_ARTICLE; i++) {
+ sb.append("<p>").append(ngen.makeText(SENTENCES_PER_PARAGRAPH)).append("</p>");
+ }
+
+ sb.append("</body></html>");
+ mBody = sb.toString();
+ }
+
+ /** Returns the headline. */
+ public String getHeadline() {
+ return mHeadline;
+ }
+
+ /** Returns the article body (HTML)*/
+ public String getBody() {
+ return mBody;
+ }
+}
diff --git a/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/NewsCategory.java b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/NewsCategory.java
new file mode 100644
index 0000000..75c7b22
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/NewsCategory.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 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.newsreader;
+
+/**
+ * A news category (collection of articles).
+ */
+public class NewsCategory {
+ // how many articles?
+ final int ARTICLES_PER_CATEGORY = 20;
+
+ // array of our articles
+ NewsArticle[] mArticles;
+
+ /**
+ * Create a news category.
+ *
+ * The articles are dynamically generated with fun and random nonsense.
+ */
+ public NewsCategory() {
+ NonsenseGenerator ngen = new NonsenseGenerator();
+ mArticles = new NewsArticle[ARTICLES_PER_CATEGORY];
+ int i;
+ for (i = 0; i < mArticles.length; i++) {
+ mArticles[i] = new NewsArticle(ngen);
+ }
+ }
+
+ /** Returns how many articles exist in this category. */
+ public int getArticleCount() {
+ return mArticles.length;
+ }
+
+ /** Gets a particular article by index. */
+ public NewsArticle getArticle(int index) {
+ return mArticles[index];
+ }
+}
diff --git a/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/NewsReaderActivity.java b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/NewsReaderActivity.java
new file mode 100644
index 0000000..30ae0ae
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/NewsReaderActivity.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2011 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.newsreader;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.SpinnerAdapter;
+
+/**
+ * Main activity: shows headlines list and articles, if layout permits.
+ *
+ * This is the main activity of the application. It can have several different layouts depending
+ * on the SDK version, screen size and orientation. The configurations are divided in two large
+ * groups: single-pane layouts and dual-pane layouts.
+ *
+ * In single-pane mode, this activity shows a list of headlines using a {@link HeadlinesFragment}.
+ * When the user clicks on a headline, a separate activity (a {@link ArticleActivity}) is launched
+ * to show the news article.
+ *
+ * In dual-pane mode, this activity shows a {@HeadlinesFragment} on the left side and an
+ * {@ArticleFragment} on the right side. When the user selects a headline on the left, the
+ * corresponding article is shown on the right.
+ *
+ * If an Action Bar is available (large enough screen and SDK version 11 or up), navigation
+ * controls are shown in the Action Bar (whether to show tabs or a list depends on the layout).
+ * If an Action Bar is not available, a regular image and button are shown in the top area of
+ * the screen, emulating an Action Bar.
+ */
+public class NewsReaderActivity extends FragmentActivity
+ implements HeadlinesFragment.OnHeadlineSelectedListener,
+ CompatActionBarNavListener,
+ OnClickListener {
+
+ // Whether or not we are in dual-pane mode
+ boolean mIsDualPane = false;
+
+ // The fragment where the headlines are displayed
+ HeadlinesFragment mHeadlinesFragment;
+
+ // The fragment where the article is displayed (null if absent)
+ ArticleFragment mArticleFragment;
+
+ // The news category and article index currently being displayed
+ int mCatIndex = 0;
+ int mArtIndex = 0;
+ NewsCategory mCurrentCat;
+
+ // List of category titles
+ final String CATEGORIES[] = { "Top Stories", "Politics", "Economy", "Technology" };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main_layout);
+
+ // find our fragments
+ mHeadlinesFragment = (HeadlinesFragment) getSupportFragmentManager().findFragmentById(
+ R.id.headlines);
+ mArticleFragment = (ArticleFragment) getSupportFragmentManager().findFragmentById(
+ R.id.article);
+
+ // Determine whether we are in single-pane or dual-pane mode by testing the visibility
+ // of the article view.
+ View articleView = findViewById(R.id.article);
+ mIsDualPane = articleView != null && articleView.getVisibility() == View.VISIBLE;
+
+ // Register ourselves as the listener for the headlines fragment events.
+ mHeadlinesFragment.setOnHeadlineSelectedListener(this);
+
+ // Set up the Action Bar (or not, if one is not available)
+ int catIndex = savedInstanceState == null ? 0 : savedInstanceState.getInt("catIndex", 0);
+ setUpActionBar(mIsDualPane, catIndex);
+
+ // Set up headlines fragment
+ mHeadlinesFragment.setSelectable(mIsDualPane);
+ restoreSelection(savedInstanceState);
+
+ // Set up the category button (shown if an Action Bar is not available)
+ Button catButton = (Button) findViewById(R.id.categorybutton);
+ if (catButton != null) {
+ catButton.setOnClickListener(this);
+ }
+ }
+
+ /** Restore category/article selection from saved state. */
+ void restoreSelection(Bundle savedInstanceState) {
+ if (savedInstanceState != null) {
+ setNewsCategory(savedInstanceState.getInt("catIndex", 0));
+ if (mIsDualPane) {
+ int artIndex = savedInstanceState.getInt("artIndex", 0);
+ mHeadlinesFragment.setSelection(artIndex);
+ onHeadlineSelected(artIndex);
+ }
+ }
+ }
+
+ @Override
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ restoreSelection(savedInstanceState);
+ }
+
+ /** Sets up Action Bar (if present).
+ *
+ * @param showTabs whether to show tabs (if false, will show list).
+ * @param selTab the selected tab or list item.
+ */
+ public void setUpActionBar(boolean showTabs, int selTab) {
+ if (Build.VERSION.SDK_INT < 11) {
+ // No action bar for you!
+ // But do not despair. In this case the layout includes a bar across the
+ // top that looks and feels like an action bar, but is made up of regular views.
+ return;
+ }
+
+ android.app.ActionBar actionBar = getActionBar();
+ actionBar.setDisplayShowTitleEnabled(false);
+
+ // Set up a CompatActionBarNavHandler to deliver us the Action Bar nagivation events
+ CompatActionBarNavHandler handler = new CompatActionBarNavHandler(this);
+ if (showTabs) {
+ actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_TABS);
+ int i;
+ for (i = 0; i < CATEGORIES.length; i++) {
+ actionBar.addTab(actionBar.newTab().setText(CATEGORIES[i]).setTabListener(handler));
+ }
+ actionBar.setSelectedNavigationItem(selTab);
+ }
+ else {
+ actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_LIST);
+ SpinnerAdapter adap = new ArrayAdapter<String>(this, R.layout.actionbar_list_item,
+ CATEGORIES);
+ actionBar.setListNavigationCallbacks(adap, handler);
+ }
+
+ // Show logo instead of icon+title.
+ actionBar.setDisplayUseLogoEnabled(true);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ setNewsCategory(0);
+ }
+
+ /** Sets the displayed news category.
+ *
+ * This causes the headlines fragment to be repopulated with the appropriate headlines.
+ */
+ void setNewsCategory(int categoryIndex) {
+ mCatIndex = categoryIndex;
+ mCurrentCat = NewsSource.getInstance().getCategory(categoryIndex);
+ mHeadlinesFragment.loadCategory(categoryIndex);
+
+ // If we are displaying the article on the right, we have to update that too
+ if (mIsDualPane) {
+ mArticleFragment.displayArticle(mCurrentCat.getArticle(0));
+ }
+
+ // If we are displaying a "category" button (on the ActionBar-less UI), we have to update
+ // its text to reflect the current category.
+ Button catButton = (Button) findViewById(R.id.categorybutton);
+ if (catButton != null) {
+ catButton.setText(CATEGORIES[mCatIndex]);
+ }
+ }
+
+ /** Called when a headline is selected.
+ *
+ * This is called by the HeadlinesFragment (via its listener interface) to notify us that a
+ * headline was selected in the Action Bar. The way we react depends on whether we are in
+ * single or dual-pane mode. In single-pane mode, we launch a new activity to display the
+ * selected article; in dual-pane mode we simply display it on the article fragment.
+ *
+ * @param index the index of the selected headline.
+ */
+ @Override
+ public void onHeadlineSelected(int index) {
+ mArtIndex = index;
+ if (mIsDualPane) {
+ // display it on the article fragment
+ mArticleFragment.displayArticle(mCurrentCat.getArticle(index));
+ }
+ else {
+ // use separate activity
+ Intent i = new Intent(this, ArticleActivity.class);
+ i.putExtra("catIndex", mCatIndex);
+ i.putExtra("artIndex", index);
+ startActivity(i);
+ }
+ }
+
+ /** Called when a news category is selected.
+ *
+ * This is called by our CompatActionBarNavHandler in response to the user selecting a
+ * news category in the Action Bar. We react by loading and displaying the headlines for
+ * that category.
+ *
+ * @param catIndex the index of the selected news category.
+ */
+ @Override
+ public void onCategorySelected(int catIndex) {
+ setNewsCategory(catIndex);
+ }
+
+ /** Save instance state. Saves current category/article index. */
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ outState.putInt("catIndex", mCatIndex);
+ outState.putInt("artIndex", mArtIndex);
+ super.onSaveInstanceState(outState);
+ }
+
+ /** Called when news category button is clicked.
+ *
+ * This is the button that we display on UIs that don't have an action bar. This button
+ * calls up a list of news categories and switches to the given category.
+ */
+ @Override
+ public void onClick(View v) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle("Select a Category");
+ builder.setItems(CATEGORIES, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ setNewsCategory(which);
+ }
+ });
+ AlertDialog d = builder.create();
+ d.show();
+ }
+}
diff --git a/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/NewsSource.java b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/NewsSource.java
new file mode 100644
index 0000000..18cbe8b
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/NewsSource.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 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.newsreader;
+
+/**
+ * Source of strange and wonderful news.
+ *
+ * This singleton functions as the repository for the news we display.
+ */
+public class NewsSource {
+ // the instance
+ static NewsSource instance = null;
+
+ // the category names
+ final String[] CATEGORIES = { "Top Stories", "US", "Politics", "Economy" };
+
+ // category objects, representing each category
+ NewsCategory[] mCategory;
+
+ /** Returns the singleton instance of this class. */
+ public static NewsSource getInstance() {
+ if (instance == null) {
+ instance = new NewsSource();
+ }
+ return instance;
+ }
+
+ public NewsSource() {
+ int i;
+ mCategory = new NewsCategory[CATEGORIES.length];
+ for (i = 0; i < CATEGORIES.length; i++) {
+ mCategory[i] = new NewsCategory();
+ }
+ }
+
+ /** Returns the list of news categories. */
+ public String[] getCategories() {
+ return CATEGORIES;
+ }
+
+ /** Returns a category by index. */
+ public NewsCategory getCategory(int categoryIndex) {
+ return mCategory[categoryIndex];
+ }
+}
diff --git a/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/NonsenseGenerator.java b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/NonsenseGenerator.java
new file mode 100644
index 0000000..e38b777
--- /dev/null
+++ b/samples/training/multiscreen/newsreader/src/com/example/android/newsreader/NonsenseGenerator.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2011 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.newsreader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/** Generator of random news. More fun than "lorem ipsum", isn't it?
+ *
+ * This generator can construct headlines and news articles by randomly composing sentences.
+ * Any resemblance to actual events (or, actually, any resemblance to anything that makes sense)
+ * is merely coincidental!
+ */
+public class NonsenseGenerator {
+ Random mRandom;
+
+ static final String[] THINGS = { "bottle", "bowl", "brick", "building",
+ "bunny", "cake", "car", "cat", "cup", "desk", "dog", "duck",
+ "elephant", "engineer", "fork", "glass", "griffon", "hat", "key", "knife", "lawyer",
+ "llama", "manual", "meat", "monitor", "mouse", "tangerine", "paper", "pear", "pen",
+ "pencil", "phone", "physicist", "planet", "potato", "road", "salad", "shoe", "slipper",
+ "soup", "spoon", "star", "steak", "table", "terminal", "treehouse", "truck",
+ "watermelon", "window" };
+
+ static final String[] ADJECTIVES = { "red", "green", "yellow", "gray", "solid", "fierce",
+ "friendly", "cowardly", "convenient", "foreign", "national", "tall",
+ "short", "metallic", "golden", "silver", "sweet", "nationwide", "competitive",
+ "stable", "municipal", "famous" };
+
+ static final String[] VERBS_PAST = { "accused", "threatened", "warned", "spoke to",
+ "has met with",
+ "was seen in the company of", "advanced towards", "collapsed on",
+ "signed a partnership with", "was converted into", "became", "was authorized to sell",
+ "sold", "bought", "rented", "allegedly spoke to", "leased", "is now investing on",
+ "is expected to buy", "is expected to sell", "was reported to have met with",
+ "will work together with", "plans to cease fire against", "started a war with",
+ "signed a truce with", "is now managing", "is investigating" };
+
+ static final String[] VERBS_PRESENT = { "accuses", "threatens", "warns", "speaks to",
+ "meets with",
+ "seen with", "advances towards", "collapses on",
+ "signs partnership with", "converts into", "becomes", "is authorized to sell",
+ "sells", "buys", "rents", "allegedly speaks to", "leases", "invests on",
+ "expected to buy", "expected to sell", "reported to have met with",
+ "works together with", "plans cease fire against", "starts war with",
+ "signs truce with", "now manages" };
+
+ public NonsenseGenerator() {
+ mRandom = new Random();
+ }
+
+ /** Produces something that reads like a headline. */
+ public String makeHeadline() {
+ return makeSentence(true);
+ }
+
+ /** Produces a sentence.
+ *
+ * @param isHeadline whether the sentence should look like a headline or not.
+ * @return the generated sentence.
+ */
+ public String makeSentence(boolean isHeadline) {
+ List<String> words = new ArrayList<String>();
+ generateSentence(words, isHeadline);
+ words.set(0, String.valueOf(Character.toUpperCase(words.get(0).charAt(0))) +
+ words.get(0).substring(1));
+ return joinWords(words);
+ }
+
+ /** Produces news article text.
+ *
+ * @param numSentences how many sentences the text is to contain.
+ * @return the generated text.
+ */
+ public String makeText(int numSentences) {
+ StringBuilder sb = new StringBuilder();
+ while (numSentences-- > 0) {
+ sb.append(makeSentence(false) + ".");
+ if (numSentences > 0) {
+ sb.append(" ");
+ }
+ }
+ return sb.toString();
+ }
+
+ /** Generates a sentence.
+ *
+ * @param words the list of words to which the sentence will be appended.
+ * @param isHeadline whether the sentence must look like a headline or not.
+ */
+ private void generateSentence(List<String> words, boolean isHeadline) {
+ if (!isHeadline && mRandom.nextInt(4) == 0)
+ generateTimeClause(words, isHeadline);
+ generateAgent(words, isHeadline);
+ generatePredicate(words, isHeadline);
+ }
+
+ private void generateTimeClause(List<String> words, boolean isHeadline) {
+ if (mRandom.nextInt(2) == 0) {
+ words.add(pickOneOf("today", "yesterday", "this afternoon", "this morning",
+ "last evening"));
+ }
+ else {
+ words.add(pickOneOf("this", "last"));
+ words.add(pickOneOf("Monday", "Tuesday", "Wednesday", "Thursday"));
+ words.add(pickOneOf("morning", "afternoon", "evening"));
+ }
+ }
+
+ private void generateAgent(List<String> words, boolean isHeadline) {
+ if (!isHeadline) {
+ words.add(pickOneOf("a", "the"));
+ }
+ if (mRandom.nextInt(3) != 0) {
+ words.add(pickOneOf(ADJECTIVES));
+ }
+ words.add(pickOneOf(THINGS));
+ }
+
+ private void generatePredicate(List<String> words, boolean isHeadline) {
+ words.add(pickOneOf(isHeadline ? VERBS_PRESENT : VERBS_PAST));
+ if (!isHeadline)
+ words.add(pickOneOf("a", "the"));
+ if (mRandom.nextInt(3) != 0) {
+ words.add(pickOneOf(ADJECTIVES));
+ }
+ words.add(pickOneOf(THINGS));
+
+ if (mRandom.nextInt(3) == 0) {
+ words.add(isHeadline ? pickOneOf(", claims", ", says") :
+ pickOneOf(", claimed", ", said", ", reported"));
+ if (!isHeadline)
+ words.add(pickOneOf("a", "the"));
+ if (mRandom.nextInt(3) != 0) {
+ words.add(pickOneOf(ADJECTIVES));
+ }
+ words.add(pickOneOf(THINGS));
+ }
+ }
+
+ private String pickOneOf(String ... options) {
+ return options[mRandom.nextInt(options.length)];
+ }
+
+ private static String joinWords(List<String> words) {
+ int i;
+ if (words.size() == 0) {
+ return "";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append(words.get(0));
+ for (i = 1; i < words.size(); i++) {
+ if (!words.get(i).startsWith(",")) {
+ sb.append(" ");
+ }
+ sb.append(words.get(i));
+ }
+ return sb.toString();
+ }
+}