diff options
56 files changed, 3067 insertions, 0 deletions
diff --git a/samples/ApiDemos/Android.mk b/samples/ApiDemos/Android.mk index 066c497cb..ca48f581d 100644 --- a/samples/ApiDemos/Android.mk +++ b/samples/ApiDemos/Android.mk @@ -12,6 +12,8 @@ LOCAL_SRC_FILES += \ LOCAL_JAVA_LIBRARIES := telephony-common mms-common +LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4 + LOCAL_PACKAGE_NAME := ApiDemos LOCAL_SDK_VERSION := current diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml index 3d1d2c1c2..d7bda3125 100644 --- a/samples/ApiDemos/AndroidManifest.xml +++ b/samples/ApiDemos/AndroidManifest.xml @@ -996,6 +996,42 @@ </intent-filter> </activity> + <activity android:name=".app.PrintBitmap" + android:label="@string/print_bitmap" + android:enabled="@bool/atLeastKitKat"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.SAMPLE_CODE" /> + </intent-filter> + </activity> + + <activity android:name=".app.PrintHtmlFromScreen" + android:label="@string/print_html_from_screen" + android:enabled="@bool/atLeastKitKat"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.SAMPLE_CODE" /> + </intent-filter> + </activity> + + <activity android:name=".app.PrintHtmlOffScreen" + android:label="@string/print_html_off_screen" + android:enabled="@bool/atLeastKitKat"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.SAMPLE_CODE" /> + </intent-filter> + </activity> + + <activity android:name=".app.PrintCustomContent" + android:label="@string/print_custom_content" + android:enabled="@bool/atLeastKitKat"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.SAMPLE_CODE" /> + </intent-filter> + </activity> + <!-- Application Updating Samples --> <!-- BEGIN_INCLUDE(app_update_declaration) --> diff --git a/samples/ApiDemos/res/layout/motogp_stat_item.xml b/samples/ApiDemos/res/layout/motogp_stat_item.xml new file mode 100644 index 000000000..e09fd6786 --- /dev/null +++ b/samples/ApiDemos/res/layout/motogp_stat_item.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 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="wrap_content" + android:paddingStart="16dip" + android:paddingEnd="16dip" + android:minHeight="64dip" + android:orientation="horizontal"> + + <TextView + android:id="@+id/year" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_weight="1" + android:gravity="start" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textIsSelectable="false"> + </TextView> + + <TextView + android:id="@+id/champion" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:textAppearance="?android:attr/textAppearanceMedium" + android:singleLine="true" + android:ellipsize="end" + android:textIsSelectable="false" + android:duplicateParentState="true"> + </TextView> + + <TextView + android:id="@+id/constructor" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_weight="1" + android:gravity="end" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textIsSelectable="false"> + </TextView> + +</LinearLayout> diff --git a/samples/ApiDemos/res/layout/print_bitmap.xml b/samples/ApiDemos/res/layout/print_bitmap.xml new file mode 100644 index 000000000..cce522acb --- /dev/null +++ b/samples/ApiDemos/res/layout/print_bitmap.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 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. +--> + +<ImageView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/image" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:src="@raw/android_logo" + android:contentDescription="@string/android_logo"> +</ImageView> diff --git a/samples/ApiDemos/res/layout/print_html_from_screen.xml b/samples/ApiDemos/res/layout/print_html_from_screen.xml new file mode 100644 index 000000000..535bcb2d1 --- /dev/null +++ b/samples/ApiDemos/res/layout/print_html_from_screen.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 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. +--> + +<WebView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/web_view" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:padding="16dip"> +</WebView> diff --git a/samples/ApiDemos/res/layout/print_html_off_screen.xml b/samples/ApiDemos/res/layout/print_html_off_screen.xml new file mode 100644 index 000000000..799b07927 --- /dev/null +++ b/samples/ApiDemos/res/layout/print_html_off_screen.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 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="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textIsSelectable="false" + android:text="@string/print_html_off_screen_msg"> +</TextView> diff --git a/samples/ApiDemos/res/menu/print_custom_content.xml b/samples/ApiDemos/res/menu/print_custom_content.xml new file mode 100644 index 000000000..ace8039e8 --- /dev/null +++ b/samples/ApiDemos/res/menu/print_custom_content.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/menu_print" + android:title="@string/print" + android:showAsAction="never" /> +</menu> diff --git a/samples/ApiDemos/res/raw/android_logo.png b/samples/ApiDemos/res/raw/android_logo.png Binary files differnew file mode 100644 index 000000000..f970c262e --- /dev/null +++ b/samples/ApiDemos/res/raw/android_logo.png diff --git a/samples/ApiDemos/res/raw/motogp_stats.html b/samples/ApiDemos/res/raw/motogp_stats.html new file mode 100644 index 000000000..b55ed9eb3 --- /dev/null +++ b/samples/ApiDemos/res/raw/motogp_stats.html @@ -0,0 +1,333 @@ +<!DOCTYPE html> +<html> +<body> +<table> +<caption>500cc/MotoGP champions</caption> +<thead><tr> +<th scope="col">Season</td> +<th scope="col">Rider</td> +<th scope="col">Constructor</td> +</tr></thead><tbody> +<tr> +<td>2012</td> +<td>Jorge Lorenzo</td> +<td>Yamaha</td> +</tr> +<tr> +<td>2011</td> +<td>Casey Stoner</td> +<td>Honda</td> +</tr> +<tr> +<td>2010</td> +<td>Jorge Lorenzo</td> +<td>Yamaha</td> +</tr> +<tr> +<td>2009</td> +<td>Valentino Rossi</td> +<td>Yamaha</td> +</tr> +<tr> +<td>2008</td> +<td>Valentino Rossi</td> +<td>Yamaha</td> +</tr> +<tr> +<td>2007</td> +<td>Casey Stoner</td> +<td>Ducati</td> +</tr> +<tr> +<td>2006</td> +<td>Nicky Hayden</td> +<td>Honda</td> +</tr> +<tr> +<td>2005</td> +<td>Valentino Rossi</td> +<td>Yamaha</td> +</tr> +<tr> +<td>2004</td> +<td>Valentino Rossi</td> +<td>Yamaha</td> +</tr> +<tr> +<td>2003</td> +<td>Valentino Rossi</td> +<td>Honda</td> +</tr> +<tr> +<td>2002</td> +<td>Valentino Rossi</td> +<td>Honda</td> +</tr> +<tr> +<td>2001</td> +<td>Valentino Rossi</td> +<td>Honda</td> +</tr> +<tr> +<td>2000</td> +<td>Kenny Roberts, Jr.</td> +<td>Suzuki</td> +</tr> +<tr> +<td>1999</td> +<td>Ëlex CrivillŽ</td> +<td>Honda</td> +</tr> +<tr> +<td>1998</td> +<td>Michael Doohan</td> +<td>Honda</td> +</tr> +<tr> +<td>1997</td> +<td>Michael Doohan</td> +<td>Honda</td> +</tr> +<tr> +<td>1996</td> +<td>Michael Doohan</td> +<td>Honda</td> +</tr> +<tr> +<td>1995</td> +<td>Michael Doohan</td> +<td>Honda</td> +</tr> +<tr> +<td>1994</td> +<td>Michael Doohan</td> +<td>Honda</td> +</tr> +<tr> +<td>1993</td> +<td>Kevin Schwantz</td> +<td>Suzuki</td> +</tr> +<tr> +<td>1992</td> +<td>Wayne Rainey</td> +<td>Yamaha</td> +</tr> +<tr> +<td>1991</td> +<td>Wayne Rainey</td> +<td>Yamaha</td> +</tr> +<tr> +<td>1990</td> +<td>Wayne Rainey</td> +<td>Yamaha</td> +</tr> +<tr> +<td>1989</td> +<td>Eddie Lawson</td> +<td>Honda</td> +</tr> +<tr> +<td>1988</td> +<td>Eddie Lawson</td> +<td>Yamaha</td> +</tr> +<tr> +<td>1987</td> +<td>Wayne Gardner</td> +<td>Honda</td> +</tr> +<tr> +<td>1986</td> +<td>Eddie Lawson</td> +<td>Yamaha</td> +</tr> +<tr> +<td>1985</td> +<td>Freddie Spencer</td> +<td>Honda</td> +</tr> +<tr> +<td>1984</td> +<td>Eddie Lawson</td> +<td>Yamaha</td> +</tr> +<tr> +<td>1983</td> +<td>Freddie Spencer</td> +<td>Honda</td> +</tr> +<tr> +<td>1982</td> +<td>Franco Uncini</td> +<td>Suzuki</td> +</tr> +<tr> +<td>1981</td> +<td>Marco Lucchinelli</td> +<td>Suzuki</td> +</tr> +<tr> +<td>1980</td> +<td>Kenny Roberts</td> +<td>Yamaha</td> +</tr> +<tr> +<td>1979</td> +<td>Kenny Roberts</td> +<td>Yamaha</td> +</tr> +<tr> +<td>1978</td> +<td>Kenny Roberts</td> +<td>Yamaha</td> +</tr> +<tr> +<td>1977</td> +<td>Barry Sheene</td> +<td>Suzuki</td> +</tr> +<tr> +<td>1976</td> +<td>Barry Sheene</td> +<td>Suzuki</td> +</tr> +<tr> +<td>1975</td> +<td>Giacomo Agostini</td> +<td>Yamaha</td> +</tr> +<tr> +<td>1974</td> +<td>Phil Read</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1973</td> +<td>Phil Read</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1972</td> +<td>Giacomo Agostini</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1971</td> +<td>Giacomo Agostini</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1970</td> +<td>Giacomo Agostini</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1969</td> +<td>Giacomo Agostini</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1968</td> +<td>Giacomo Agostini</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1967</td> +<td>Giacomo Agostini</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1966</td> +<td>Giacomo Agostini</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1965</td> +<td>Mike Hailwood</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1964</td> +<td>Mike Hailwood</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1963</td> +<td>Mike Hailwood</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1962</td> +<td>Mike Hailwood</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1961</td> +<td>Gary Hocking</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1960</td> +<td>John Surtees</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1959</td> +<td>John Surtees</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1958</td> +<td>John Surtees</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1957</td> +<td>Libero Liberati</td> +<td>Gilera</td> +</tr> +<tr> +<td>1956</td> +<td>John Surtees</td> +<td>MV Agusta</td> +</tr> +<tr> +<td>1955</td> +<td>Geoff Duke</td> +<td>Gilera</td> +</tr> +<tr> +<td>1954</td> +<td>Geoff Duke</td> +<td>Gilera</td> +</tr> +<tr> +<td>1953</td> +<td>Geoff Duke</td> +<td>Gilera</td> +</tr> +<tr> +<td>1952</td> +<td>Umberto Masetti</td> +<td>Gilera</td> +</tr> +<tr> +<td>1951</td> +<td>Geoff Duke</td> +<td>Norton</td> +</tr> +<tr> +<td>1950</td> +<td>Umberto Masetti</td> +<td>Gilera</td> +</tr> +<tr> +<td>1949</td> +<td>Leslie Graham</td> +<td>AJS</td> +</tr> +</tbody><tfoot></tfoot></table> +</body> +</html>
\ No newline at end of file diff --git a/samples/ApiDemos/res/values/arrays.xml b/samples/ApiDemos/res/values/arrays.xml index b06220975..8f987ae43 100644 --- a/samples/ApiDemos/res/values/arrays.xml +++ b/samples/ApiDemos/res/values/arrays.xml @@ -154,4 +154,209 @@ <item>No.</item> <item>Mmm... cheese.</item> </string-array> + + <!-- Used in app/Print/Custom Layout example --> + <string-array name="motogp_years"> + <item>2012</item> + <item>2011</item> + <item>2010</item> + <item>2009</item> + <item>2008</item> + <item>2007</item> + <item>2006</item> + <item>2005</item> + <item>2004</item> + <item>2003</item> + <item>2002</item> + <item>2001</item> + <item>2000</item> + <item>1999</item> + <item>1998</item> + <item>1997</item> + <item>1996</item> + <item>1995</item> + <item>1994</item> + <item>1993</item> + <item>1992</item> + <item>1991</item> + <item>1990</item> + <item>1989</item> + <item>1988</item> + <item>1987</item> + <item>1986</item> + <item>1985</item> + <item>1984</item> + <item>1983</item> + <item>1982</item> + <item>1981</item> + <item>1980</item> + <item>1979</item> + <item>1978</item> + <item>1977</item> + <item>1976</item> + <item>1975</item> + <item>1974</item> + <item>1973</item> + <item>1972</item> + <item>1971</item> + <item>1970</item> + <item>1969</item> + <item>1968</item> + <item>1967</item> + <item>1966</item> + <item>1965</item> + <item>1964</item> + <item>1963</item> + <item>1962</item> + <item>1961</item> + <item>1960</item> + <item>1959</item> + <item>1958</item> + <item>1957</item> + <item>1956</item> + <item>1955</item> + <item>1954</item> + <item>1953</item> + <item>1952</item> + <item>1951</item> + <item>1950</item> + <item>1949</item> + </string-array> + + <!-- Used in app/Print/Custom Layout example --> + <string-array name="motogp_champions"> + <item>Jorge Lorenzo</item> + <item>Casey Stoner</item> + <item>Jorge Lorenzo</item> + <item>Valentino Rossi</item> + <item>Valentino Rossi</item> + <item>Casey Stoner</item> + <item>Nicky Hayden</item> + <item>Valentino Rossi</item> + <item>Valentino Rossi</item> + <item>Valentino Rossi</item> + <item>Valentino Rossi</item> + <item>Valentino Rossi</item> + <item>Kenny Roberts, Jr.</item> + <item>Àlex Crivillé</item> + <item>Michael Doohan</item> + <item>Michael Doohan</item> + <item>Michael Doohan</item> + <item>Michael Doohan</item> + <item>Michael Doohan</item> + <item>Kevin Schwantz</item> + <item>Wayne Rainey</item> + <item>Wayne Rainey</item> + <item>Wayne Rainey</item> + <item>Eddie Lawson</item> + <item>Eddie Lawson</item> + <item>Wayne Gardner</item> + <item>Eddie Lawson</item> + <item>Freddie Spencer</item> + <item>Eddie Lawson</item> + <item>Freddie Spencer</item> + <item>Franco Uncini</item> + <item>Marco Lucchinelli</item> + <item>Kenny Roberts</item> + <item>Kenny Roberts</item> + <item>Kenny Roberts</item> + <item>Barry Sheene</item> + <item>Barry Sheene</item> + <item>Giacomo Agostini</item> + <item>Phil Read</item> + <item>Phil Read</item> + <item>Giacomo Agostini</item> + <item>Giacomo Agostini</item> + <item>Giacomo Agostini</item> + <item>Giacomo Agostini</item> + <item>Giacomo Agostini</item> + <item>Giacomo Agostini</item> + <item>Giacomo Agostini</item> + <item>Mike Hailwood</item> + <item>Mike Hailwood</item> + <item>Mike Hailwood</item> + <item>Mike Hailwood</item> + <item>Gary Hocking</item> + <item>John Surtees</item> + <item>John Surtees</item> + <item>John Surtees</item> + <item>Libero Liberati</item> + <item>John Surtees</item> + <item>Geoff Duke</item> + <item>Geoff Duke</item> + <item>Geoff Duke</item> + <item>Umberto Masetti</item> + <item>Geoff Duke</item> + <item>Umberto Masetti</item> + <item>Leslie Graham</item> + </string-array> + + <!-- Used in app/Print/Custom Layout example --> + <string-array name="motogp_constructors"> + <item>Yamaha</item> + <item>Honda</item> + <item>Yamaha</item> + <item>Yamaha</item> + <item>Yamaha</item> + <item>Ducati</item> + <item>Honda</item> + <item>Yamaha</item> + <item>Yamaha</item> + <item>Honda</item> + <item>Honda</item> + <item>Honda</item> + <item>Suzuki</item> + <item>Honda</item> + <item>Honda</item> + <item>Honda</item> + <item>Honda</item> + <item>Honda</item> + <item>Honda</item> + <item>Suzuki</item> + <item>Yamaha</item> + <item>Yamaha</item> + <item>Yamaha</item> + <item>Honda</item> + <item>Yamaha</item> + <item>Honda</item> + <item>Yamaha</item> + <item>Honda</item> + <item>Yamaha</item> + <item>Honda</item> + <item>Suzuki</item> + <item>Suzuki</item> + <item>Yamaha</item> + <item>Yamaha</item> + <item>Yamaha</item> + <item>Suzuki</item> + <item>Suzuki</item> + <item>Giacomo Agostini</item> + <item>Phil Read</item> + <item>Phil Read</item> + <item>Yamaha</item> + <item>MV Agusta</item> + <item>MV Agusta</item> + <item>MV Agusta</item> + <item>MV Agusta</item> + <item>MV Agusta</item> + <item>MV Agusta</item> + <item>MV Agusta</item> + <item>MV Agusta</item> + <item>MV Agusta</item> + <item>MV Agusta</item> + <item>MV Agusta</item> + <item>MV Agusta</item> + <item>MV Agusta</item> + <item>MV Agusta</item> + <item>Gilera</item> + <item>MV Agusta</item> + <item>Gilera</item> + <item>Gilera</item> + <item>Gilera</item> + <item>Gilera</item> + <item>Norton</item> + <item>Gilera</item> + <item>AJS</item> + </string-array> + </resources> diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml index 47445dde8..804087dd5 100644 --- a/samples/ApiDemos/res/values/strings.xml +++ b/samples/ApiDemos/res/values/strings.xml @@ -871,6 +871,19 @@ <string name="btn_toggle_tabs">Toggle tab mode</string> <string name="btn_remove_all_tabs">Remove all tabs</string> + <!-- ================================= --> + <!-- app/print print examples strings --> + <!-- ================================= --> + + <string name="print_bitmap">App/Print/Print Bitmap</string> + <string name="print_html_from_screen">App/Print/Print HTML from screen</string> + <string name="print_html_off_screen">App/Print/Print HTML off screen</string> + <string name="print_custom_content">App/Print/Print Custom Layout</string> + <string name="print">Print</string> + <string name="print_html_off_screen_msg">From the overflow menu you can print some + off screen content.</string> + <string name="android_logo">Android logo</string> + <!-- ============================ --> <!-- graphics examples strings --> <!-- ============================ --> diff --git a/samples/ApiDemos/src/com/example/android/apis/app/PrintBitmap.java b/samples/ApiDemos/src/com/example/android/apis/app/PrintBitmap.java new file mode 100644 index 000000000..762478a99 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/app/PrintBitmap.java @@ -0,0 +1,82 @@ +package com.example.android.apis.app; +/* + * Copyright (C) 2013 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. + */ + +import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.os.Bundle; +import android.print.PrintManager; +import android.support.v4.print.PrintHelper; +import android.view.Menu; +import android.view.MenuItem; +import android.webkit.WebView; +import android.widget.ImageView; + +import com.example.android.apis.R; + +/** + * This class demonstrates how to implement bitmap printing. + * <p> + * This activity shows an image and offers a print option in the overflow + * menu. When the user chooses to print a helper class from the support + * library is used to print the image. + * </p> + * + * @see PrintManager + * @see WebView + */ +public class PrintBitmap extends Activity { + + private ImageView mImageView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.print_bitmap); + mImageView = (ImageView) findViewById(R.id.image); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.print_custom_content, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.menu_print) { + print(); + return true; + } + return super.onOptionsItemSelected(item); + } + + private void print() { + // Get the print manager. + PrintHelper printHelper = new PrintHelper(this); + + // Set the desired scale mode. + printHelper.setScaleMode(PrintHelper.SCALE_MODE_FIT); + + // Get the bitmap for the ImageView's drawable. + Bitmap bitmap = ((BitmapDrawable) mImageView.getDrawable()).getBitmap(); + + // Print the bitmap. + printHelper.printBitmap("Print Bitmap", bitmap); + } +} diff --git a/samples/ApiDemos/src/com/example/android/apis/app/PrintCustomContent.java b/samples/ApiDemos/src/com/example/android/apis/app/PrintCustomContent.java new file mode 100644 index 000000000..7a7e7ed80 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/app/PrintCustomContent.java @@ -0,0 +1,562 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.apis.app; + +import android.app.ListActivity; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.pdf.PdfDocument.Page; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.CancellationSignal.OnCancelListener; +import android.os.ParcelFileDescriptor; +import android.print.PageRange; +import android.print.PrintAttributes; +import android.print.PrintDocumentAdapter; +import android.print.PrintDocumentInfo; +import android.print.PrintManager; +import android.print.pdf.PrintedPdfDocument; +import android.util.SparseIntArray; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.View.MeasureSpec; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.example.android.apis.R; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * This class demonstrates how to implement custom printing support. + * <p> + * This activity shows the list of the MotoGP champions by year and + * brand. The print option in the overflow menu allows the user to + * print the content. The list list of items is laid out to such that + * it fits the options selected by the user from the UI such as page + * size. Hence, for different page sizes the printed content will have + * different page count. + * </p> + * <p> + * This sample demonstrates how to completely implement a {@link + * PrintDocumentAdapter} in which: + * <ul> + * <li>Layout based on the selected print options is performed.</li> + * <li>Layout work is performed only if print options change would change the content.</li> + * <li>Layout result is properly reported.</li> + * <li>Only requested pages are written.</li> + * <li>Write result is properly reported.</li> + * <li>Both Layout and write respond to cancellation.</li> + * <li>Layout and render of views is demonstrated.</li> + * </ul> + * </p> + * + * @see PrintManager + * @see PrintDocumentAdapter + */ +public class PrintCustomContent extends ListActivity { + + private static final int MILS_IN_INCH = 1000; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setListAdapter(new MotoGpStatAdapter(loadMotoGpStats(), + getLayoutInflater())); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.print_custom_content, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.menu_print) { + print(); + return true; + } + return super.onOptionsItemSelected(item); + } + + private void print() { + PrintManager printManager = (PrintManager) getSystemService( + Context.PRINT_SERVICE); + + printManager.print("MotoGP stats", + new PrintDocumentAdapter() { + private int mRenderPageWidth; + private int mRenderPageHeight; + + private PrintAttributes mPrintAttributes; + private PrintDocumentInfo mDocumentInfo; + private Context mPrintContext; + + @Override + public void onLayout(final PrintAttributes oldAttributes, + final PrintAttributes newAttributes, + final CancellationSignal cancellationSignal, + final LayoutResultCallback callback, + final Bundle metadata) { + + // If we are already cancelled, don't do any work. + if (cancellationSignal.isCanceled()) { + callback.onLayoutCancelled(); + return; + } + + // Now we determined if the print attributes changed in a way that + // would change the layout and if so we will do a layout pass. + boolean layoutNeeded = false; + + final int density = Math.max(newAttributes.getResolution().getHorizontalDpi(), + newAttributes.getResolution().getVerticalDpi()); + + // Note that we are using the PrintedPdfDocument class which creates + // a PDF generating canvas whose size is in points (1/72") not screen + // pixels. Hence, this canvas is pretty small compared to the screen. + // The recommended way is to layout the content in the desired size, + // in this case as large as the printer can do, and set a translation + // to the PDF canvas to shrink in. Note that PDF is a vector format + // and you will not lose data during the transformation. + + // The content width is equal to the page width minus the margins times + // the horizontal printer density. This way we get the maximal number + // of pixels the printer can put horizontally. + final int marginLeft = (int) (density * (float) newAttributes.getMinMargins() + .getLeftMils() / MILS_IN_INCH); + final int marginRight = (int) (density * (float) newAttributes.getMinMargins() + .getRightMils() / MILS_IN_INCH); + final int contentWidth = (int) (density * (float) newAttributes.getMediaSize() + .getWidthMils() / MILS_IN_INCH) - marginLeft - marginRight; + if (mRenderPageWidth != contentWidth) { + mRenderPageWidth = contentWidth; + layoutNeeded = true; + } + + // The content height is equal to the page height minus the margins times + // the vertical printer resolution. This way we get the maximal number + // of pixels the printer can put vertically. + final int marginTop = (int) (density * (float) newAttributes.getMinMargins() + .getTopMils() / MILS_IN_INCH); + final int marginBottom = (int) (density * (float) newAttributes.getMinMargins() + .getBottomMils() / MILS_IN_INCH); + final int contentHeight = (int) (density * (float) newAttributes.getMediaSize() + .getHeightMils() / MILS_IN_INCH) - marginTop - marginBottom; + if (mRenderPageHeight != contentHeight) { + mRenderPageHeight = contentHeight; + layoutNeeded = true; + } + + // Create a context for resources at printer density. We will + // be inflating views to render them and would like them to use + // resources for a density the printer supports. + if (mPrintContext == null || mPrintContext.getResources() + .getConfiguration().densityDpi != density) { + Configuration configuration = new Configuration(); + configuration.densityDpi = density; + mPrintContext = createConfigurationContext( + configuration); + mPrintContext.setTheme(android.R.style.Theme_Holo_Light); + } + + // If no layout is needed that we did a layout at least once and + // the document info is not null, also the second argument is false + // to notify the system that the content did not change. This is + // important as if the system has some pages and the content didn't + // change the system will ask, the application to write them again. + if (!layoutNeeded) { + callback.onLayoutFinished(mDocumentInfo, false); + return; + } + + // For demonstration purposes we will do the layout off the main + // thread but for small content sizes like this one it is OK to do + // that on the main thread. + + // Store the data as we will layout off the main thread. + final List<MotoGpStatItem> items = ((MotoGpStatAdapter) + getListAdapter()).cloneItems(); + + new AsyncTask<Void, Void, PrintDocumentInfo>() { + @Override + protected void onPreExecute() { + // First register for cancellation requests. + cancellationSignal.setOnCancelListener(new OnCancelListener() { + @Override + public void onCancel() { + cancel(true); + } + }); + // Stash the attributes as we will need them for rendering. + mPrintAttributes = newAttributes; + } + + @Override + protected PrintDocumentInfo doInBackground(Void... params) { + try { + // Create an adapter with the stats and an inflater + // to load resources for the printer density. + MotoGpStatAdapter adapter = new MotoGpStatAdapter(items, + (LayoutInflater) mPrintContext.getSystemService( + Context.LAYOUT_INFLATER_SERVICE)); + + int currentPage = 0; + int pageContentHeight = 0; + int viewType = -1; + View view = null; + LinearLayout dummyParent = new LinearLayout(mPrintContext); + dummyParent.setOrientation(LinearLayout.VERTICAL); + + final int itemCount = adapter.getCount(); + for (int i = 0; i < itemCount; i++) { + // Be nice and respond to cancellation. + if (isCancelled()) { + return null; + } + + // Get the next view. + final int nextViewType = adapter.getItemViewType(i); + if (viewType == nextViewType) { + view = adapter.getView(i, view, dummyParent); + } else { + view = adapter.getView(i, null, dummyParent); + } + viewType = nextViewType; + + // Measure the next view + measureView(view); + + // Add the height but if the view crosses the page + // boundary we will put it to the next page. + pageContentHeight += view.getMeasuredHeight(); + if (pageContentHeight > mRenderPageHeight) { + pageContentHeight = view.getMeasuredHeight(); + currentPage++; + } + } + + // Create a document info describing the result. + PrintDocumentInfo info = new PrintDocumentInfo + .Builder("MotoGP_stats.pdf") + .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT) + .setPageCount(currentPage + 1) + .build(); + + // We completed the layout as a result of print attributes + // change. Hence, if we are here the content changed for + // sure which is why we pass true as the second argument. + callback.onLayoutFinished(info, true); + return info; + } catch (Exception e) { + // An unexpected error, report that we failed and + // one may pass in a human readable localized text + // for what the error is if known. + callback.onLayoutFailed(null); + throw new RuntimeException(e); + } + } + + @Override + protected void onPostExecute(PrintDocumentInfo result) { + // Update the cached info to send it over if the next + // layout pass does not result in a content change. + mDocumentInfo = result; + } + + @Override + protected void onCancelled(PrintDocumentInfo result) { + // Task was cancelled, report that. + callback.onLayoutCancelled(); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); + } + + @Override + public void onWrite(final PageRange[] pages, + final ParcelFileDescriptor destination, + final CancellationSignal cancellationSignal, + final WriteResultCallback callback) { + + // If we are already cancelled, don't do any work. + if (cancellationSignal.isCanceled()) { + callback.onWriteCancelled(); + return; + } + + // Store the data as we will layout off the main thread. + final List<MotoGpStatItem> items = ((MotoGpStatAdapter) + getListAdapter()).cloneItems(); + + new AsyncTask<Void, Void, Void>() { + private final SparseIntArray mWrittenPages = new SparseIntArray(); + private final PrintedPdfDocument mPdfDocument = new PrintedPdfDocument( + PrintCustomContent.this, mPrintAttributes); + + @Override + protected void onPreExecute() { + // First register for cancellation requests. + cancellationSignal.setOnCancelListener(new OnCancelListener() { + @Override + public void onCancel() { + cancel(true); + } + }); + } + + @Override + protected Void doInBackground(Void... params) { + // Go over all the pages and write only the requested ones. + // Create an adapter with the stats and an inflater + // to load resources for the printer density. + MotoGpStatAdapter adapter = new MotoGpStatAdapter(items, + (LayoutInflater) mPrintContext.getSystemService( + Context.LAYOUT_INFLATER_SERVICE)); + + int currentPage = -1; + int pageContentHeight = 0; + int viewType = -1; + View view = null; + Page page = null; + LinearLayout dummyParent = new LinearLayout(mPrintContext); + dummyParent.setOrientation(LinearLayout.VERTICAL); + + // The content is laid out and rendered in screen pixels with + // the width and height of the paper size times the print + // density but the PDF canvas size is in points which are 1/72", + // so we will scale down the content. + final float scale = Math.min( + (float) mPdfDocument.getPageContentRect().width() + / mRenderPageWidth, + (float) mPdfDocument.getPageContentRect().height() + / mRenderPageHeight); + + final int itemCount = adapter.getCount(); + for (int i = 0; i < itemCount; i++) { + // Be nice and respond to cancellation. + if (isCancelled()) { + return null; + } + + // Get the next view. + final int nextViewType = adapter.getItemViewType(i); + if (viewType == nextViewType) { + view = adapter.getView(i, view, dummyParent); + } else { + view = adapter.getView(i, null, dummyParent); + } + viewType = nextViewType; + + // Measure the next view + measureView(view); + + // Add the height but if the view crosses the page + // boundary we will put it to the next one. + pageContentHeight += view.getMeasuredHeight(); + if (currentPage < 0 || pageContentHeight > mRenderPageHeight) { + pageContentHeight = view.getMeasuredHeight(); + currentPage++; + // Done with the current page - finish it. + if (page != null) { + mPdfDocument.finishPage(page); + } + // If the page is requested, render it. + if (containsPage(pages, currentPage)) { + page = mPdfDocument.startPage(currentPage); + page.getCanvas().scale(scale, scale); + // Keep track which pages are written. + mWrittenPages.append(mWrittenPages.size(), currentPage); + } else { + page = null; + } + } + + // If the current view is on a requested page, render it. + if (page != null) { + // Layout an render the content. + view.layout(0, 0, view.getMeasuredWidth(), + view.getMeasuredHeight()); + view.draw(page.getCanvas()); + // Move the canvas for the next view. + page.getCanvas().translate(0, view.getHeight()); + } + } + + // Done with the last page. + if (page != null) { + mPdfDocument.finishPage(page); + } + + // Write the data and return success or failure. + try { + mPdfDocument.writeTo(new FileOutputStream( + destination.getFileDescriptor())); + // Compute which page ranges were written based on + // the bookkeeping we maintained. + PageRange[] pageRanges = computeWrittenPageRanges(mWrittenPages); + callback.onWriteFinished(pageRanges); + } catch (IOException ioe) { + callback.onWriteFailed(null); + } finally { + mPdfDocument.close(); + } + + return null; + } + + @Override + protected void onCancelled(Void result) { + // Task was cancelled, report that. + callback.onWriteCancelled(); + mPdfDocument.close(); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); + } + + private void measureView(View view) { + final int widthMeasureSpec = ViewGroup.getChildMeasureSpec( + MeasureSpec.makeMeasureSpec(mRenderPageWidth, + MeasureSpec.EXACTLY), 0, view.getLayoutParams().width); + final int heightMeasureSpec = ViewGroup.getChildMeasureSpec( + MeasureSpec.makeMeasureSpec(mRenderPageHeight, + MeasureSpec.EXACTLY), 0, view.getLayoutParams().height); + view.measure(widthMeasureSpec, heightMeasureSpec); + } + + private PageRange[] computeWrittenPageRanges(SparseIntArray writtenPages) { + List<PageRange> pageRanges = new ArrayList<PageRange>(); + + int start = -1; + int end = -1; + final int writtenPageCount = writtenPages.size(); + for (int i = 0; i < writtenPageCount; i++) { + if (start < 0) { + start = writtenPages.valueAt(i); + } + int oldEnd = end = start; + while (i < writtenPageCount && (end - oldEnd) <= 1) { + oldEnd = end; + end = writtenPages.valueAt(i); + i++; + } + PageRange pageRange = new PageRange(start, end); + pageRanges.add(pageRange); + start = end = -1; + } + + PageRange[] pageRangesArray = new PageRange[pageRanges.size()]; + pageRanges.toArray(pageRangesArray); + return pageRangesArray; + } + + private boolean containsPage(PageRange[] pageRanges, int page) { + final int pageRangeCount = pageRanges.length; + for (int i = 0; i < pageRangeCount; i++) { + if (pageRanges[i].getStart() <= page + && pageRanges[i].getEnd() >= page) { + return true; + } + } + return false; + } + }, null); + } + + private List<MotoGpStatItem> loadMotoGpStats() { + String[] years = getResources().getStringArray(R.array.motogp_years); + String[] champions = getResources().getStringArray(R.array.motogp_champions); + String[] constructors = getResources().getStringArray(R.array.motogp_constructors); + + List<MotoGpStatItem> items = new ArrayList<MotoGpStatItem>(); + + final int itemCount = years.length; + for (int i = 0; i < itemCount; i++) { + MotoGpStatItem item = new MotoGpStatItem(); + item.year = years[i]; + item.champion = champions[i]; + item.constructor = constructors[i]; + items.add(item); + } + + return items; + } + + private static final class MotoGpStatItem { + String year; + String champion; + String constructor; + } + + private class MotoGpStatAdapter extends BaseAdapter { + private final List<MotoGpStatItem> mItems; + private final LayoutInflater mInflater; + + public MotoGpStatAdapter(List<MotoGpStatItem> items, LayoutInflater inflater) { + mItems = items; + mInflater = inflater; + } + + public List<MotoGpStatItem> cloneItems() { + return new ArrayList<MotoGpStatItem>(mItems); + } + + @Override + public int getCount() { + return mItems.size(); + } + + @Override + public Object getItem(int position) { + return mItems.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = mInflater.inflate(R.layout.motogp_stat_item, parent, false); + } + + MotoGpStatItem item = (MotoGpStatItem) getItem(position); + + TextView yearView = (TextView) convertView.findViewById(R.id.year); + yearView.setText(item.year); + + TextView championView = (TextView) convertView.findViewById(R.id.champion); + championView.setText(item.champion); + + TextView constructorView = (TextView) convertView.findViewById(R.id.constructor); + constructorView.setText(item.constructor); + + return convertView; + } + } +} diff --git a/samples/ApiDemos/src/com/example/android/apis/app/PrintHtmlFromScreen.java b/samples/ApiDemos/src/com/example/android/apis/app/PrintHtmlFromScreen.java new file mode 100644 index 000000000..55c98ffc9 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/app/PrintHtmlFromScreen.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.apis.app; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.print.PrintManager; +import android.view.Menu; +import android.view.MenuItem; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +import com.example.android.apis.R; + +/** + * This class demonstrates how to implement HTML content printing + * from a {@link WebView} which is shown on the screen. + * <p> + * This activity shows a simple HTML content in a {@link WebView} + * and allows the user to print that content via an action in the + * action bar. The shown {@link WebView} is doing the printing. + * </p> + * + * @see PrintManager + * @see WebView + */ +public class PrintHtmlFromScreen extends Activity { + + private WebView mWebView; + + private boolean mDataLoaded; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.print_html_from_screen); + mWebView = (WebView) findViewById(R.id.web_view); + + // Important: Only enable the print option after the page is loaded. + mWebView.setWebViewClient(new WebViewClient() { + @Override + public void onPageFinished(WebView view, String url) { + // Data loaded, so now we want to show the print option. + mDataLoaded = true; + invalidateOptionsMenu(); + } + }); + + // Load an HTML page. + mWebView.loadUrl("file:///android_res/raw/motogp_stats.html"); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + if (mDataLoaded) { + getMenuInflater().inflate(R.menu.print_custom_content, menu); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.menu_print) { + print(); + return true; + } + return super.onOptionsItemSelected(item); + } + + private void print() { + // Get the print manager. + PrintManager printManager = (PrintManager) getSystemService( + Context.PRINT_SERVICE); + // Pass in the ViewView's document adapter. + printManager.print("MotoGP stats", mWebView.createPrintDocumentAdapter(), null); + } +} diff --git a/samples/ApiDemos/src/com/example/android/apis/app/PrintHtmlOffScreen.java b/samples/ApiDemos/src/com/example/android/apis/app/PrintHtmlOffScreen.java new file mode 100644 index 000000000..9c239b867 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/app/PrintHtmlOffScreen.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.apis.app; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.ParcelFileDescriptor; +import android.print.PageRange; +import android.print.PrintAttributes; +import android.print.PrintDocumentAdapter; +import android.print.PrintManager; +import android.view.Menu; +import android.view.MenuItem; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +import com.example.android.apis.R; + +/** + * This class demonstrates how to implement HTML content printing + * from a {@link WebView} which is not shown on the screen. + * <p> + * This activity shows a text prompt and when the user chooses the + * print option from the overflow menu an HTML page with content that + * is not on the screen is printed via an off-screen {@link WebView}. + * </p> + * + * @see PrintManager + * @see WebView + */ +public class PrintHtmlOffScreen extends Activity { + + private WebView mWebView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.print_html_off_screen); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.print_custom_content, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.menu_print) { + print(); + return true; + } + return super.onOptionsItemSelected(item); + } + + private void print() { + // Create a WebView and hold on to it as the printing will start when + // load completes and we do not want the WbeView to be garbage collected. + mWebView = new WebView(this); + + // Important: Only after the page is loaded we will do the print. + mWebView.setWebViewClient(new WebViewClient() { + @Override + public void onPageFinished(WebView view, String url) { + doPrint(); + } + }); + + // Load an HTML page. + mWebView.loadUrl("file:///android_res/raw/motogp_stats.html"); + } + + private void doPrint() { + // Get the print manager. + PrintManager printManager = (PrintManager) getSystemService( + Context.PRINT_SERVICE); + + // Create a wrapper PrintDocumentAdapter to clean up when done. + PrintDocumentAdapter adapter = new PrintDocumentAdapter() { + private final PrintDocumentAdapter mWrappedInstance = + mWebView.createPrintDocumentAdapter(); + + @Override + public void onStart() { + mWrappedInstance.onStart(); + } + + @Override + public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, + CancellationSignal cancellationSignal, LayoutResultCallback callback, + Bundle extras) { + mWrappedInstance.onLayout(oldAttributes, newAttributes, cancellationSignal, + callback, extras); + } + + @Override + public void onWrite(PageRange[] pages, ParcelFileDescriptor destination, + CancellationSignal cancellationSignal, WriteResultCallback callback) { + mWrappedInstance.onWrite(pages, destination, cancellationSignal, callback); + } + + @Override + public void onFinish() { + mWrappedInstance.onFinish(); + // Intercept the finish call to know when printing is done + // and destroy the WebView as it is expensive to keep around. + mWebView.destroy(); + mWebView = null; + } + }; + + // Pass in the ViewView's document adapter. + printManager.print("MotoGP stats", adapter, null); + } +} diff --git a/samples/devbytes/telephony/SmsSampleProject/README b/samples/devbytes/telephony/SmsSampleProject/README new file mode 100644 index 000000000..a26768cab --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/README @@ -0,0 +1,7 @@ +This is an Android Studio project: +http://developer.android.com/sdk/installing/studio.html + +To build you should first copy local.properties.sample to +local.properties and set your SDK path. + +Then use Android Studio to import the project. diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/build.gradle b/samples/devbytes/telephony/SmsSampleProject/SmsSample/build.gradle new file mode 100644 index 000000000..d0f9b9766 --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/build.gradle @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2013 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. + */ + +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:0.6.+' + } +} +apply plugin: 'android' + +repositories { + mavenCentral() +} + +android { + compileSdkVersion 19 + buildToolsVersion "18.1.1" + + defaultConfig { + minSdkVersion 10 + targetSdkVersion 19 + } +} + +dependencies { + compile 'com.android.support:support-v4:18.0.0' +} diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/AndroidManifest.xml b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/AndroidManifest.xml new file mode 100644 index 000000000..d6d43799d --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/AndroidManifest.xml @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2013 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.smssample" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk + android:minSdkVersion="10" + android:targetSdkVersion="19" /> + + <uses-permission android:name="android.permission.WRITE_SMS" /> + <uses-permission android:name="android.permission.READ_SMS" /> + <uses-permission android:name="android.permission.SEND_SMS" /> + <uses-permission android:name="android.permission.RECEIVE_SMS" /> + <uses-permission android:name="android.permission.RECEIVE_MMS" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + + <activity + android:name=".MainActivity" + android:label="@string/app_name" + android:windowSoftInputMode="stateHidden"> + + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + + <intent-filter> + <action android:name="android.intent.action.SEND" /> + <action android:name="android.intent.action.SENDTO" /> + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.BROWSABLE" /> + <data android:scheme="sms" /> + <data android:scheme="smsto" /> + <data android:scheme="mms" /> + <data android:scheme="mmsto" /> + </intent-filter> + + </activity> + + <!-- BroadcastReceiver that listens for incoming SMS messages --> + <!-- Note the use of android:enabled that is linked to a bool. This will mean this receiver + is enabled on KitKat devices and above --> + <receiver android:name=".receiver.SmsReceiver" + android:enabled="@bool/hasKitKat" + android:permission="android.permission.BROADCAST_SMS"> + + <!-- KitKat+ SMS received action --> + <intent-filter> + <action android:name="android.provider.Telephony.SMS_DELIVER" /> + </intent-filter> + + </receiver> + + <!-- BroadcastReceiver that listens for incoming SMS messages --> + <!-- Note the use of android:enabled that is linked to a bool. This will mean this receiver + is enabled on preKitKat devices --> + <receiver android:name=".receiver.SmsReceiverLegacy" + android:enabled="@bool/preKitKat"> + + <!-- Pre-KitKat SMS received action --> + <intent-filter> + <action android:name="android.provider.Telephony.SMS_RECEIVED" /> + </intent-filter> + + </receiver> + + <!-- BroadcastReceiver that listens for incoming MMS messages --> + <!-- Note the use of android:enabled that is linked to a bool. This will mean this receiver + is enabled on KitKat devices and above --> + <receiver android:name=".receiver.MmsReceiver" + android:enabled="@bool/hasKitKat" + android:permission="android.permission.BROADCAST_WAP_PUSH"> + + <!-- KitKat+ MMS received action --> + <intent-filter> + <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" /> + <data android:mimeType="application/vnd.wap.mms-message" /> + </intent-filter> + + </receiver> + + <!-- BroadcastReceiver that listens for incoming MMS messages --> + <!-- Note the use of android:enabled that is linked to a bool. This will mean this receiver + is enabled on preKitKat devices --> + <receiver android:name=".receiver.MmsReceiverLegacy" + android:enabled="@bool/preKitKat"> + + <!-- Pre-KitKat MMS received action --> + <intent-filter> + <action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" /> + <data android:mimeType="application/vnd.wap.mms-message" /> + </intent-filter> + + </receiver> + + <!-- Service that delivers SMS messages received from the phone "quick response" --> + <service android:name=".service.RespondService" + android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE" + android:exported="true" > + <intent-filter> + <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="sms" /> + <data android:scheme="smsto" /> + <data android:scheme="mms" /> + <data android:scheme="mmsto" /> + </intent-filter> + </service> + + <!-- A service used internally to process incoming SMS/MMS --> + <service android:name=".service.MessagingService" + android:exported="false" /> + + </application> + +</manifest> diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/MainActivity.java b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/MainActivity.java new file mode 100644 index 000000000..c94939704 --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/MainActivity.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2013 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.smssample; + +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.provider.Telephony.Sms.Inbox; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.LoaderManager.LoaderCallbacks; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v4.widget.SimpleCursorAdapter; +import android.text.TextUtils; +import android.view.Menu; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.RelativeLayout; +import android.widget.Toast; + +/** + * The main Activity that provides a sample of a few things: + * -detecting if this app is the default SMS app and then showing/hiding UI and enabling/disabling + * functionality. the UI that is shown has a button to prompt the user to set this app as the + * default. + * -a simple query to the SMS content provider to show a list of SMS messages in the inbox. even + * though the query uses KitKat APIs this query should still work on earlier versions of Android + * as the contract class and ContentProvider were still around (with essentially the same + * structure) but were private. + * -being triggered from another application when creating a new SMS. a good example is creating + * a new SMS from the system People application. although nothing is done with the incoming + * Intent in this case (just a Toast is displayed) + * + * Obviously this is far from a full implementation and should just be used as a sample of how + * an app could be set up to correctly integrate with the new Android 4.4 KitKat APIs while + * running normally on earlier Android versions. + */ +public class MainActivity extends FragmentActivity implements LoaderCallbacks<Cursor> { + private RelativeLayout mSetDefaultSmsLayout; + private Button mSendSmsButton; + private EditText mSendSmsEditText; + private SimpleCursorAdapter mAdapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + // Find some views + mSetDefaultSmsLayout = (RelativeLayout) findViewById(R.id.set_default_sms_layout); + mSendSmsEditText = (EditText) findViewById(R.id.send_sms_edittext); + ListView listView = (ListView) findViewById(android.R.id.list); + listView.setEmptyView(findViewById(android.R.id.empty)); + mSendSmsButton = (Button) findViewById(R.id.send_sms_button); + mSendSmsButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + sendSms(mSendSmsEditText.getText().toString()); + } + }); + + // Create adapter and set it to our ListView + final String[] fromFields = new String[] { + SmsQuery.PROJECTION[SmsQuery.ADDRESS], SmsQuery.PROJECTION[SmsQuery.BODY] }; + final int[] toViews = new int[] { android.R.id.text1, android.R.id.text2 }; + mAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, null, + fromFields, toViews, 0); + listView.setAdapter(mAdapter); + + // Placeholder to process incoming SEND/SENDTO intents + String intentAction = getIntent() == null ? null : getIntent().getAction(); + if (!TextUtils.isEmpty(intentAction) && (Intent.ACTION_SENDTO.equals(intentAction) + || Intent.ACTION_SEND.equals(intentAction))) { + // TODO: Handle incoming SEND and SENDTO intents by pre-populating UI components + Toast.makeText(this, "Handle SEND and SENDTO intents: " + getIntent().getDataString(), + Toast.LENGTH_SHORT).show(); + } + + // Simple query to show the most recent SMS messages in the inbox + getSupportLoaderManager().initLoader(SmsQuery.TOKEN, null, this); + } + + /** + * Dummy sendSms method, would need the "to" address to actually send a message :) + */ + private void sendSms(String smsText) { + if (!TextUtils.isEmpty(smsText)) { + if (Utils.isDefaultSmsApp(this)) { + // TODO: Use SmsManager to send SMS and then record the message in the system SMS + // ContentProvider + Toast.makeText(this, "Sending text message: " + smsText, Toast.LENGTH_SHORT).show(); + } else { + // TODO: Notify the user the app is not default and provide a way to trigger + // Utils.setDefaultSmsApp() so they can set it. + Toast.makeText(this, "Not default", Toast.LENGTH_SHORT).show(); + } + } + } + + @Override + protected void onResume() { + super.onResume(); + + // Only do these checks/changes on KitKat+, the "mSetDefaultSmsLayout" has its visibility + // set to "gone" in the xml layout so it won't show at all on earlier Android versions. + if (Utils.hasKitKat()) { + if (Utils.isDefaultSmsApp(this)) { + // This app is the default, remove the "make this app the default" layout and + // enable message sending components. + mSetDefaultSmsLayout.setVisibility(View.GONE); + mSendSmsEditText.setHint(R.string.sms_send_new_hint); + mSendSmsEditText.setEnabled(true); + mSendSmsButton.setEnabled(true); + } else { + // Not the default, show the "make this app the default" layout and disable + // message sending components. + mSetDefaultSmsLayout.setVisibility(View.VISIBLE); + mSendSmsEditText.setText(""); + mSendSmsEditText.setHint(R.string.sms_send_disabled); + mSendSmsEditText.setEnabled(false); + mSendSmsButton.setEnabled(false); + + Button button = (Button) findViewById(R.id.set_default_sms_button); + button.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + Utils.setDefaultSmsApp(MainActivity.this); + } + }); + } + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + @Override + public Loader<Cursor> onCreateLoader(int i, Bundle bundle) { + if (i == SmsQuery.TOKEN) { + // This will fetch all SMS messages in the inbox, ordered by date desc + return new CursorLoader(this, SmsQuery.CONTENT_URI, SmsQuery.PROJECTION, null, null, + SmsQuery.SORT_ORDER); + } + return null; + } + + @Override + public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) { + if (cursorLoader.getId() == SmsQuery.TOKEN && cursor != null) { + // Standard swap cursor in when load is done + mAdapter.swapCursor(cursor); + } + } + + @Override + public void onLoaderReset(Loader<Cursor> cursorLoader) { + // Standard swap cursor to null when loader is reset + mAdapter.swapCursor(null); + } + + /** + * A basic SmsQuery on android.provider.Telephony.Sms.Inbox + */ + private interface SmsQuery { + int TOKEN = 1; + + static final Uri CONTENT_URI = Inbox.CONTENT_URI; + + static final String[] PROJECTION = { + Inbox._ID, + Inbox.ADDRESS, + Inbox.BODY, + }; + + static final String SORT_ORDER = Inbox.DEFAULT_SORT_ORDER; + + int ID = 0; + int ADDRESS = 1; + int BODY = 2; + } +} diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/Utils.java b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/Utils.java new file mode 100644 index 000000000..00dc7d600 --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/Utils.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2013 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.smssample; + +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.Build.VERSION_CODES; +import android.provider.Telephony; +import android.provider.Telephony.Sms.Intents; + +public class Utils { + + /** + * Check if the device runs Android 4.3 (JB MR2) or higher. + */ + public static boolean hasJellyBeanMR2() { + return Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2; + } + + /** + * Check if the device runs Android 4.4 (KitKat) or higher. + */ + public static boolean hasKitKat() { + return Build.VERSION.SDK_INT >= VERSION_CODES.KITKAT; + } + + /** + * Check if your app is the default system SMS app. + * @param context The Context + * @return True if it is default, False otherwise. Pre-KitKat will always return True. + */ + public static boolean isDefaultSmsApp(Context context) { + if (hasKitKat()) { + return context.getPackageName().equals(Telephony.Sms.getDefaultSmsPackage(context)); + } + + return true; + } + + /** + * Trigger the intent to open the system dialog that asks the user to change the default + * SMS app. + * @param context The Context + */ + public static void setDefaultSmsApp(Context context) { + // This is a new intent which only exists on KitKat + if (hasKitKat()) { + Intent intent = new Intent(Intents.ACTION_CHANGE_DEFAULT); + intent.putExtra(Intents.EXTRA_PACKAGE_NAME, context.getPackageName()); + context.startActivity(intent); + } + } +} diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/receiver/MessagingReceiver.java b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/receiver/MessagingReceiver.java new file mode 100644 index 000000000..4dcb05533 --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/receiver/MessagingReceiver.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2013 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.smssample.receiver; + +import android.content.Context; +import android.content.Intent; +import android.provider.Telephony.Sms.Intents; +import android.support.v4.content.WakefulBroadcastReceiver; + +import com.example.android.smssample.service.MessagingService; +import com.example.android.smssample.Utils; + +/** + * The main messaging receiver class. Note that this is not directly included in + * AndroidManifest.xml, instead, subclassed versions of this are included which allows + * them to be enabled/disabled independently as they will have a unique component name. + */ +public class MessagingReceiver extends WakefulBroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent == null ? null : intent.getAction(); + + // If on KitKat+ and default messaging app then look for new deliver actions actions. + if (Utils.hasKitKat() && Utils.isDefaultSmsApp(context)) { + if (Intents.SMS_DELIVER_ACTION.equals(action)) { + handleIncomingSms(context, intent); + } else if (Intents.WAP_PUSH_DELIVER_ACTION.equals(action)) { + handleIncomingMms(context, intent); + } + } else { // Otherwise look for old pre-KitKat actions + if (Intents.SMS_RECEIVED_ACTION.equals(action)) { + handleIncomingSms(context, intent); + } else if (Intents.WAP_PUSH_RECEIVED_ACTION.equals(action)) { + handleIncomingMms(context, intent); + } + } + } + + private void handleIncomingSms(Context context, Intent intent) { + // TODO: Handle SMS here + // As an example, we'll start a wakeful service to handle the SMS + intent.setAction(MessagingService.ACTION_MY_RECEIVE_SMS); + intent.setClass(context, MessagingService.class); + startWakefulService(context, intent); + } + + private void handleIncomingMms(Context context, Intent intent) { + // TODO: Handle MMS here + // As an example, we'll start a wakeful service to handle the MMS + intent.setAction(MessagingService.ACTION_MY_RECEIVE_MMS); + intent.setClass(context, MessagingService.class); + startWakefulService(context, intent); + } +} diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/receiver/MmsReceiver.java b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/receiver/MmsReceiver.java new file mode 100644 index 000000000..40f04bb60 --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/receiver/MmsReceiver.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013 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.smssample.receiver; + +/** + * Stub subclass to allow individual receiver components in AndroidManifest to be enabled/disabled. + */ +public class MmsReceiver extends MessagingReceiver { + +} diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/receiver/MmsReceiverLegacy.java b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/receiver/MmsReceiverLegacy.java new file mode 100644 index 000000000..181fe45c5 --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/receiver/MmsReceiverLegacy.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013 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.smssample.receiver; + +/** + * Stub subclass to allow individual receiver components in AndroidManifest to be enabled/disabled. + */ +public class MmsReceiverLegacy extends MessagingReceiver { + +} diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/receiver/SmsReceiver.java b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/receiver/SmsReceiver.java new file mode 100644 index 000000000..0c4a2cc76 --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/receiver/SmsReceiver.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013 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.smssample.receiver; + +/** + * Stub subclass to allow individual receiver components in AndroidManifest to be enabled/disabled. + */ +public class SmsReceiver extends MessagingReceiver { + +} diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/receiver/SmsReceiverLegacy.java b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/receiver/SmsReceiverLegacy.java new file mode 100644 index 000000000..a02d18813 --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/receiver/SmsReceiverLegacy.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013 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.smssample.receiver; + +/** + * Stub subclass to allow individual receiver components in AndroidManifest to be enabled/disabled. + */ +public class SmsReceiverLegacy extends MessagingReceiver { + +} diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/service/MessagingService.java b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/service/MessagingService.java new file mode 100644 index 000000000..af191e8b2 --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/service/MessagingService.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013 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.smssample.service; + +import android.app.IntentService; +import android.content.Intent; + +import com.example.android.smssample.receiver.MessagingReceiver; + +/** + * This service is triggered internally only and is used to process incoming SMS and MMS messages + * that the {@link com.example.android.smssample.receiver.MessagingReceiver} passes over. It's + * preferable to handle these in a service in case there is significant work to do which may exceed + * the time allowed in a receiver. + */ +public class MessagingService extends IntentService { + private static final String TAG = "MessagingService"; + + // These actions are for this app only and are used by MessagingReceiver to start this service + public static final String ACTION_MY_RECEIVE_SMS = "com.example.android.smssample.RECEIVE_SMS"; + public static final String ACTION_MY_RECEIVE_MMS = "com.example.android.smssample.RECEIVE_MMS"; + + public MessagingService() { + super(TAG); + } + + @Override + protected void onHandleIntent(Intent intent) { + if (intent != null) { + String intentAction = intent.getAction(); + if (ACTION_MY_RECEIVE_SMS.equals(intentAction)) { + // TODO: Handle incoming SMS + + // Ensure wakelock is released that was created by the WakefulBroadcastReceiver + MessagingReceiver.completeWakefulIntent(intent); + } else if (ACTION_MY_RECEIVE_MMS.equals(intentAction)) { + // TODO: Handle incoming MMS + + // Ensure wakelock is released that was created by the WakefulBroadcastReceiver + MessagingReceiver.completeWakefulIntent(intent); + } + } + } +} diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/service/RespondService.java b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/service/RespondService.java new file mode 100644 index 000000000..d88a762bc --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/java/com/example/android/smssample/service/RespondService.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2013 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.smssample.service; + +import android.app.IntentService; +import android.content.Intent; +import android.telephony.TelephonyManager; + +import com.example.android.smssample.Utils; + +/** + * This service handles the system intent ACTION_RESPOND_VIA_MESSAGE when we are the default SMS + * app. + */ +public class RespondService extends IntentService { + private static final String TAG = "RespondService"; + + public RespondService() { + super(TAG); + } + + @Override + protected void onHandleIntent(Intent intent) { + if (intent != null) { + if (Utils.hasJellyBeanMR2() && Utils.isDefaultSmsApp(this) && + // ACTION_RESPOND_VIA_MESSAGE was added in JB MR2 + TelephonyManager.ACTION_RESPOND_VIA_MESSAGE.equals(intent.getAction())) { + // TODO: Handle "respond via message" quick reply + } + } + } +} diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..98cfcc5a8 --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6ccb6e05e --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..893b1ee4c --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/drawable-xxhdpi/ic_launcher.png b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..c51e62f69 --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/layout/activity_main.xml b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/layout/activity_main.xml new file mode 100644 index 000000000..3005568e3 --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/layout/activity_main.xml @@ -0,0 +1,93 @@ +<!-- + Copyright (C) 2013 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" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + tools:context=".MainActivity"> + + <RelativeLayout + android:id="@+id/set_default_sms_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone" + android:background="@color/alert_orange"> + + <Button + android:id="@+id/set_default_sms_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:layout_margin="@dimen/standard_margin" + android:text="@string/choose_sms_app" /> + + <TextView + android:id="@+id/set_default_sms_textview" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_toLeftOf="@+id/set_default_sms_button" + android:layout_centerVertical="true" + android:layout_margin="@dimen/standard_margin" + android:text="@string/set_default_sms_text" /> + + </RelativeLayout> + + <ListView + android:id="@android:id/list" + android:layout_height="0dp" + android:layout_width="match_parent" + android:layout_weight="1" + android:fastScrollEnabled="true" /> + + <TextView + android:id="@android:id/empty" + android:layout_height="0dp" + android:layout_width="match_parent" + android:layout_weight="1" + android:gravity="center" + android:visibility="gone" + android:layout_margin="@dimen/standard_margin" + style="?android:textAppearanceMedium" + android:text="@string/sms_empty" /> + + <RelativeLayout + android:id="@+id/send_sms_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="@dimen/standard_margin" + android:background="@color/light_gray"> + + <Button + android:id="@+id/send_sms_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:text="@string/sms_send" /> + + <EditText + android:id="@+id/send_sms_edittext" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_toLeftOf="@+id/send_sms_button" + android:layout_centerVertical="true" + android:hint="@string/sms_send_new_hint"/> + + </RelativeLayout> + +</LinearLayout> diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/menu/main.xml b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/menu/main.xml new file mode 100644 index 000000000..e4ee086db --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/menu/main.xml @@ -0,0 +1,23 @@ +<!-- + Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + + <item android:id="@+id/action_default" + android:title="@string/action_settings" + android:orderInCategory="100" + android:showAsAction="never" /> + +</menu> diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values-v11/styles.xml b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values-v11/styles.xml new file mode 100644 index 000000000..425c775b3 --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values-v11/styles.xml @@ -0,0 +1,26 @@ +<!-- + Copyright (C) 2013 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> + + <!-- + Base application theme for API 11+. This theme completely replaces + AppBaseTheme from res/values/styles.xml on API 11+ devices. + --> + <style name="AppBaseTheme" parent="android:Theme.Holo.Light"> + <!-- API 11 theme customizations can go here. --> + </style> + +</resources> diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values-v19/bools.xml b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values-v19/bools.xml new file mode 100644 index 000000000..20c993a7c --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values-v19/bools.xml @@ -0,0 +1,19 @@ +<!-- + Copyright (C) 2013 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> + <bool name="hasKitKat">true</bool> + <bool name="preKitKat">false</bool> +</resources>
\ No newline at end of file diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values/bools.xml b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values/bools.xml new file mode 100644 index 000000000..1bded920c --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values/bools.xml @@ -0,0 +1,19 @@ +<!-- + Copyright (C) 2013 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> + <bool name="hasKitKat">false</bool> + <bool name="preKitKat">true</bool> +</resources>
\ No newline at end of file diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values/colors.xml b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values/colors.xml new file mode 100644 index 000000000..7f13ecd5a --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values/colors.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2013 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> + <color name="alert_orange">#FFBB33</color> + <color name="light_gray">#F4F4F4</color> +</resources>
\ No newline at end of file diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values/dimens.xml b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values/dimens.xml new file mode 100644 index 000000000..020e4d3f6 --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values/dimens.xml @@ -0,0 +1,19 @@ +<!-- + Copyright (C) 2013 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> + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="standard_margin">8dp</dimen> +</resources> diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values/strings.xml b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values/strings.xml new file mode 100644 index 000000000..c0f18e3de --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values/strings.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + + <string name="app_name">SmsDemoApp</string> + <string name="action_settings">Settings</string> + <string name="choose_sms_app">Set as default</string> + <string name="sms_empty">No SMS messages</string> + <string name="set_default_sms_text">This app is not the default SMS app</string> + <string name="sms_send_new_hint">Send new SMS</string> + <string name="sms_send">Send</string> + <string name="sms_send_disabled">SMS Sending Disabled</string> + +</resources> diff --git a/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values/styles.xml b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values/styles.xml new file mode 100644 index 000000000..848be93ad --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/SmsSample/src/main/res/values/styles.xml @@ -0,0 +1,35 @@ +<!-- + Copyright (C) 2013 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> + + <!-- + Base application theme, dependent on API level. This theme is replaced + by AppBaseTheme from res/values-vXX/styles.xml on newer devices. + --> + <style name="AppBaseTheme" parent="android:Theme.Light"> + <!-- + Theme customizations available in newer API levels can go in + res/values-vXX/styles.xml, while customizations related to + backward-compatibility can go here. + --> + </style> + + <!-- Application theme. --> + <style name="AppTheme" parent="AppBaseTheme"> + <!-- All customizations that are NOT specific to a particular API-level can go here. --> + </style> + +</resources> diff --git a/samples/devbytes/telephony/SmsSampleProject/build.gradle b/samples/devbytes/telephony/SmsSampleProject/build.gradle new file mode 100644 index 000000000..495c5038e --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/build.gradle @@ -0,0 +1 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. diff --git a/samples/devbytes/telephony/SmsSampleProject/local.properties.sample b/samples/devbytes/telephony/SmsSampleProject/local.properties.sample new file mode 100644 index 000000000..37317f492 --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/local.properties.sample @@ -0,0 +1,7 @@ +# This file should be copied to local.properties and path set to point +# to your Android SDK. + +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +sdk.dir=/usr/local/lib/android-sdk
\ No newline at end of file diff --git a/samples/devbytes/telephony/SmsSampleProject/settings.gradle b/samples/devbytes/telephony/SmsSampleProject/settings.gradle new file mode 100644 index 000000000..e0867f09e --- /dev/null +++ b/samples/devbytes/telephony/SmsSampleProject/settings.gradle @@ -0,0 +1 @@ +include ':SmsSample'
\ No newline at end of file diff --git a/samples/devbytes/ui/ImmersiveMode/build.gradle b/samples/devbytes/ui/ImmersiveMode/build.gradle new file mode 100644 index 000000000..da6ff3598 --- /dev/null +++ b/samples/devbytes/ui/ImmersiveMode/build.gradle @@ -0,0 +1,39 @@ +/* + * Copyright 2013 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. + */ + +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:0.6.+' + } +} +apply plugin: 'android' + +repositories { + mavenCentral() +} + +android { + compileSdkVersion 19 + buildToolsVersion "18.0.1" + + defaultConfig { + minSdkVersion 19 + targetSdkVersion 19 + } +} diff --git a/samples/devbytes/ui/ImmersiveMode/src/main/AndroidManifest.xml b/samples/devbytes/ui/ImmersiveMode/src/main/AndroidManifest.xml new file mode 100644 index 000000000..b4a35321f --- /dev/null +++ b/samples/devbytes/ui/ImmersiveMode/src/main/AndroidManifest.xml @@ -0,0 +1,50 @@ +<!-- + Copyright 2013 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" + android:versionCode="1" + android:versionName="1.0" + package="com.example.android.immersive"> + + <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" /> + + <application android:icon="@drawable/ic_launcher" + android:label="@string/immersive_mode" + android:allowBackup="true"> + + <activity android:name=".ImmersiveActivity" + android:label="@string/immersive_mode" + android:configChanges="orientation|keyboardHidden|screenSize" + android:theme="@style/ImmersiveTheme"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <activity android:name=".ImmersiveStickyActivity" + android:taskAffinity="" + android:label="@string/immersive_sticky" + android:configChanges="orientation|keyboardHidden|screenSize" + android:theme="@style/ImmersiveStickyTheme"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + </application> +</manifest> diff --git a/samples/devbytes/ui/ImmersiveMode/src/main/java/com/example/android/immersive/ImmersiveActivity.java b/samples/devbytes/ui/ImmersiveMode/src/main/java/com/example/android/immersive/ImmersiveActivity.java new file mode 100644 index 000000000..8fdde0853 --- /dev/null +++ b/samples/devbytes/ui/ImmersiveMode/src/main/java/com/example/android/immersive/ImmersiveActivity.java @@ -0,0 +1,117 @@ +/* + * Copyright 2013 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.immersive; + +import android.app.Activity; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; + +public class ImmersiveActivity extends Activity { + private static final int INITIAL_HIDE_DELAY = 300; + + private View mDecorView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.immersive_activity); + + final View controlsView = findViewById(R.id.fullscreen_content_controls); + final View contentView = findViewById(R.id.fullscreen_content); + + mDecorView = getWindow().getDecorView(); + mDecorView.setOnSystemUiVisibilityChangeListener( + new View.OnSystemUiVisibilityChangeListener() { + @Override + public void onSystemUiVisibilityChange(int flags) { + boolean visible = (flags & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; + controlsView.animate() + .alpha(visible ? 1 : 0) + .translationY(visible ? 0 : controlsView.getHeight()); + } + }); + contentView.setClickable(true); + final GestureDetector clickDetector = new GestureDetector(this, + new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onSingleTapUp(MotionEvent e) { + boolean visible = (mDecorView.getSystemUiVisibility() + & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; + if (visible) { + hideSystemUI(); + } else { + showSystemUI(); + } + return true; + } + }); + contentView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + return clickDetector.onTouchEvent(motionEvent); + } + }); + + showSystemUI(); + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + + // When the window loses focus (e.g. the action overflow is shown), + // cancel any pending hide action. When the window gains focus, + // hide the system UI. + if (hasFocus) { + delayedHide(INITIAL_HIDE_DELAY); + } else { + mHideHandler.removeMessages(0); + } + } + + private void hideSystemUI() { + mDecorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_LOW_PROFILE + | View.SYSTEM_UI_FLAG_IMMERSIVE); + } + + private void showSystemUI() { + mDecorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); + } + + private final Handler mHideHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + hideSystemUI(); + } + }; + + private void delayedHide(int delayMillis) { + mHideHandler.removeMessages(0); + mHideHandler.sendEmptyMessageDelayed(0, delayMillis); + } +} diff --git a/samples/devbytes/ui/ImmersiveMode/src/main/java/com/example/android/immersive/ImmersiveStickyActivity.java b/samples/devbytes/ui/ImmersiveMode/src/main/java/com/example/android/immersive/ImmersiveStickyActivity.java new file mode 100644 index 000000000..d6dfd3695 --- /dev/null +++ b/samples/devbytes/ui/ImmersiveMode/src/main/java/com/example/android/immersive/ImmersiveStickyActivity.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013 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.immersive; + +import android.app.Activity; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; + +public class ImmersiveStickyActivity extends Activity { + private View mDecorView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.immersive_sticky_activity); + mDecorView = getWindow().getDecorView(); + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + if (hasFocus) { + mDecorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + } + } +} diff --git a/samples/devbytes/ui/ImmersiveMode/src/main/res/drawable-xxhdpi/ic_launcher.png b/samples/devbytes/ui/ImmersiveMode/src/main/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f47fc3203 --- /dev/null +++ b/samples/devbytes/ui/ImmersiveMode/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/devbytes/ui/ImmersiveMode/src/main/res/drawable-xxhdpi/ic_launcher_translucent_actionbar.png b/samples/devbytes/ui/ImmersiveMode/src/main/res/drawable-xxhdpi/ic_launcher_translucent_actionbar.png Binary files differnew file mode 100644 index 000000000..bf344ad86 --- /dev/null +++ b/samples/devbytes/ui/ImmersiveMode/src/main/res/drawable-xxhdpi/ic_launcher_translucent_actionbar.png diff --git a/samples/devbytes/ui/ImmersiveMode/src/main/res/layout/immersive_activity.xml b/samples/devbytes/ui/ImmersiveMode/src/main/res/layout/immersive_activity.xml new file mode 100644 index 000000000..e4ce45540 --- /dev/null +++ b/samples/devbytes/ui/ImmersiveMode/src/main/res/layout/immersive_activity.xml @@ -0,0 +1,50 @@ +<!-- + Copyright 2013 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. + --> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <include layout="@layout/include_content" /> + + <!-- This FrameLayout insets its children based on system windows using + android:fitsSystemWindows. These insets will be stable across system + UI visibility changes because we use the SYSTEM_UI_FLAG_LAYOUT_STABLE + flag in addition to SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and + SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION. --> + <FrameLayout android:layout_width="match_parent" + android:layout_height="match_parent" + android:clipToPadding="false" + android:fitsSystemWindows="true"> + + <LinearLayout android:id="@+id/fullscreen_content_controls" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom|center_horizontal" + android:background="@color/fullscreen_control_overlay_color" + android:orientation="horizontal"> + + <Button android:id="@+id/placeholder_button" + android:layout_width="0dp" + android:layout_height="48dp" + android:layout_weight="1" + android:text="@string/placeholder_button" + android:background="?android:selectableItemBackground" /> + + </LinearLayout> + </FrameLayout> + +</FrameLayout> diff --git a/samples/devbytes/ui/ImmersiveMode/src/main/res/layout/immersive_sticky_activity.xml b/samples/devbytes/ui/ImmersiveMode/src/main/res/layout/immersive_sticky_activity.xml new file mode 100644 index 000000000..b98b923aa --- /dev/null +++ b/samples/devbytes/ui/ImmersiveMode/src/main/res/layout/immersive_sticky_activity.xml @@ -0,0 +1,23 @@ +<!-- + Copyright 2013 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. + --> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <include layout="@layout/include_content" /> + +</FrameLayout> diff --git a/samples/devbytes/ui/ImmersiveMode/src/main/res/layout/include_content.xml b/samples/devbytes/ui/ImmersiveMode/src/main/res/layout/include_content.xml new file mode 100644 index 000000000..967573b3d --- /dev/null +++ b/samples/devbytes/ui/ImmersiveMode/src/main/res/layout/include_content.xml @@ -0,0 +1,31 @@ +<!-- + Copyright 2013 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. + --> + +<!-- The primary full-screen view. This can be replaced with whatever view + is needed to present your content, e.g. VideoView, SurfaceView, + TextureView, etc. --> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/fullscreen_content" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:keepScreenOn="true" + android:textColor="#fb3" + android:fontFamily="sans-serif-condensed" + android:textStyle="bold" + android:lineSpacingMultiplier="0.8" + android:textSize="50sp" + android:gravity="center" + android:text="@string/placeholder_content" /> diff --git a/samples/devbytes/ui/ImmersiveMode/src/main/res/values/colors.xml b/samples/devbytes/ui/ImmersiveMode/src/main/res/values/colors.xml new file mode 100644 index 000000000..7e91c5094 --- /dev/null +++ b/samples/devbytes/ui/ImmersiveMode/src/main/res/values/colors.xml @@ -0,0 +1,20 @@ +<!-- + Copyright 2013 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> + <color name="background">#f80</color> + <color name="fullscreen_control_overlay_color">#66000000</color> +</resources> diff --git a/samples/devbytes/ui/ImmersiveMode/src/main/res/values/strings.xml b/samples/devbytes/ui/ImmersiveMode/src/main/res/values/strings.xml new file mode 100644 index 000000000..3c520f939 --- /dev/null +++ b/samples/devbytes/ui/ImmersiveMode/src/main/res/values/strings.xml @@ -0,0 +1,22 @@ +<!-- + Copyright 2013 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="immersive_mode">Immersive Mode</string> + <string name="immersive_sticky">Immersive/Sticky</string> + <string name="placeholder_button">Placeholder Button</string> + <string name="placeholder_content">IMMERSIVE\nCONTENT</string> +</resources> diff --git a/samples/devbytes/ui/ImmersiveMode/src/main/res/values/styles.xml b/samples/devbytes/ui/ImmersiveMode/src/main/res/values/styles.xml new file mode 100644 index 000000000..2d7d458b3 --- /dev/null +++ b/samples/devbytes/ui/ImmersiveMode/src/main/res/values/styles.xml @@ -0,0 +1,36 @@ +<!-- + Copyright 2013 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="ImmersiveTheme" parent="android:Theme.Holo"> + <item name="android:windowBackground">@color/background</item> + <item name="android:windowContentOverlay">@null</item> + <item name="android:windowActionBarOverlay">true</item> + <item name="android:actionBarStyle">@style/ImmersiveActionBarStyle</item> + </style> + + <style name="ImmersiveStickyTheme" parent="ImmersiveTheme"> + <item name="android:windowNoTitle">true</item> + <item name="android:windowActionBar">false</item> + </style> + + <style name="ImmersiveActionBarStyle" parent="android:Widget.Holo.ActionBar.Solid"> + <item name="android:background">@color/fullscreen_control_overlay_color</item> + <item name="android:icon">@drawable/ic_launcher_translucent_actionbar</item> + </style> + +</resources> diff --git a/scripts/app_engine_server/redirects.yaml b/scripts/app_engine_server/redirects.yaml index 4f83713f8..d15017a4d 100644 --- a/scripts/app_engine_server/redirects.yaml +++ b/scripts/app_engine_server/redirects.yaml @@ -634,6 +634,10 @@ redirects: dst: /about/versions/android-4.0.html type: permanent +- src: /(k|kk|kitkat)/?$ + dst: /about/versions/kitkat.html + type: permanent + - src: /(j|jb|jellybean)/?$ dst: /about/versions/jelly-bean.html type: permanent |