diff options
| author | Dirk Dougherty <ddougherty@google.com> | 2013-10-29 20:56:17 -0700 |
|---|---|---|
| committer | Dirk Dougherty <ddougherty@google.com> | 2013-10-29 20:56:17 -0700 |
| commit | 4b737b695e4e72b26c995400e58d566b0c92f545 (patch) | |
| tree | fcbb54cef59145c8c5fac9b6c2073febe76881a2 | |
| parent | 7165109e6d189ce518f6d3f0d1e09289c1d056c9 (diff) | |
| download | android_development-4b737b695e4e72b26c995400e58d566b0c92f545.tar.gz android_development-4b737b695e4e72b26c995400e58d566b0c92f545.tar.bz2 android_development-4b737b695e4e72b26c995400e58d566b0c92f545.zip | |
Add prebuilt browseable samples as static files.
Change-Id: Ifb5382223343400882834d2dd9c182c3df602e34
840 files changed, 35304 insertions, 0 deletions
diff --git a/samples/AccelerometerPlay/_index.jd b/samples/AccelerometerPlay/_index.jd index cdca3a6b1..488cda777 100644 --- a/samples/AccelerometerPlay/_index.jd +++ b/samples/AccelerometerPlay/_index.jd @@ -1,3 +1,4 @@ +page.keywords="Sensor", "Games", "Accelerometer" page.tags="Sensor", "Games", "Accelerometer" sample.group=Sensors @jd:body diff --git a/samples/browseable/ActivityInstrumentation/AndroidManifest.xml b/samples/browseable/ActivityInstrumentation/AndroidManifest.xml new file mode 100644 index 000000000..547d95ee7 --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/AndroidManifest.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.activityinstrumentation" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="17" /> + + <application android:allowBackup="true" + android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@style/AppTheme"> + + <activity android:name=".MainActivity" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + + +</manifest> diff --git a/samples/browseable/ActivityInstrumentation/_index.jd b/samples/browseable/ActivityInstrumentation/_index.jd new file mode 100644 index 000000000..d1967d3cd --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/_index.jd @@ -0,0 +1,13 @@ + + + +page.tags="ActivityInstrumentation" +sample.group=Testing +@jd:body + +<p> + + This sample provides a basic example of using an InstrumentationTest to probe the + internal state of an Activity. + + </p> diff --git a/samples/browseable/ActivityInstrumentation/res/drawable-hdpi/ic_launcher.png b/samples/browseable/ActivityInstrumentation/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..b1efaf4b2 --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/ActivityInstrumentation/res/drawable-hdpi/tile.9.png b/samples/browseable/ActivityInstrumentation/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/ActivityInstrumentation/res/drawable-mdpi/ic_launcher.png b/samples/browseable/ActivityInstrumentation/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f5f9244f2 --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/ActivityInstrumentation/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/ActivityInstrumentation/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..5d07b3f06 --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/ActivityInstrumentation/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/ActivityInstrumentation/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6ef21e1f4 --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/ActivityInstrumentation/res/layout/activity_main.xml b/samples/browseable/ActivityInstrumentation/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/ActivityInstrumentation/res/layout/sample_main.xml b/samples/browseable/ActivityInstrumentation/res/layout/sample_main.xml new file mode 100644 index 000000000..2b7a4d161 --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/res/layout/sample_main.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<RelativeLayout 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:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + tools:context=".MainActivity"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/instructions" + android:id="@+id/instructions"/> + + <Spinner + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/spinner" + android:layout_below="@+id/instructions" + android:layout_centerHorizontal="true"/> + +</RelativeLayout> diff --git a/samples/browseable/ActivityInstrumentation/res/values-sw600dp/dimens.xml b/samples/browseable/ActivityInstrumentation/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/ActivityInstrumentation/res/values-sw600dp/styles.xml b/samples/browseable/ActivityInstrumentation/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/ActivityInstrumentation/res/values-sw720dp-land/dimens.xml b/samples/browseable/ActivityInstrumentation/res/values-sw720dp-land/dimens.xml new file mode 100644 index 000000000..0dfce6a0c --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/res/values-sw720dp-land/dimens.xml @@ -0,0 +1,21 @@ +<!-- + ~ 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> + <!-- Customize dimensions originally defined in res/values/dimens.xml (such as + screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here. --> + <dimen name="activity_horizontal_margin">128dp</dimen> +</resources> diff --git a/samples/browseable/ActivityInstrumentation/res/values/base-strings.xml b/samples/browseable/ActivityInstrumentation/res/values/base-strings.xml new file mode 100644 index 000000000..dfe40f4f6 --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/res/values/base-strings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">ActivityInstrumentation</string> + <string name="intro_message"> + <![CDATA[ + + + This sample provides a basic example of using an InstrumentationTest to probe the + internal state of an Activity. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/ActivityInstrumentation/res/values/dimens.xml b/samples/browseable/ActivityInstrumentation/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/ActivityInstrumentation/res/values/strings.xml b/samples/browseable/ActivityInstrumentation/res/values/strings.xml new file mode 100644 index 000000000..4ed22434a --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/res/values/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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="instructions">The value of the spinner below should be persisted when this activity is destroyed.</string> +</resources> diff --git a/samples/browseable/ActivityInstrumentation/res/values/styles.xml b/samples/browseable/ActivityInstrumentation/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/ActivityInstrumentation/src/com.example.android.activityinstrumentation/MainActivity.java b/samples/browseable/ActivityInstrumentation/src/com.example.android.activityinstrumentation/MainActivity.java new file mode 100644 index 000000000..39056eaa3 --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/src/com.example.android.activityinstrumentation/MainActivity.java @@ -0,0 +1,110 @@ +/* + * 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.activityinstrumentation; + +import android.app.Activity; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Spinner; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Basic activity with a spinner. The spinner should persist its position to disk every time a + * new selection is made. + */ +public class MainActivity extends Activity { + + /** Shared preferences key: Holds spinner position. Must not be negative. */ + private static final String PREF_SPINNER_POS = "spinner_pos"; + /** Magic constant to indicate that no value is stored for PREF_SPINNER_POS. */ + private static final int PREF_SPINNER_VALUE_ISNULL = -1; + /** Values for display in spinner. */ + private static final String[] SPINNER_VALUES = new String[] { + "Select Weather...", "Sunny", "Partly Cloudy", "Cloudy", "Rain", "Snow", "Hurricane"}; + + // Constants representing each of the options in SPINNER_VALUES. Declared package-private + // so that they can be accessed from our test suite. + static final int WEATHER_NOSELECTION = 0; + static final int WEATHER_SUNNY = 1; + static final int WEATHER_PARTLY_CLOUDY = 2; + static final int WEATHER_CLOUDY = 3; + static final int WEATHER_RAIN = 4; + static final int WEATHER_SNOW = 5; + static final int WEATHER_HURRICANE = 6; + + /** Handle to default shared preferences for this activity. */ + private SharedPreferences mPrefs; + /** Handle to the spinner in this Activity's layout. */ + private Spinner mSpinner; + + /** + * Setup activity state. + * + * @param savedInstanceState + */ + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Inflate UI from res/layout/activity_main.xml + setContentView(R.layout.sample_main); + + // Get handle to default shared preferences for this activity + mPrefs = PreferenceManager.getDefaultSharedPreferences(MainActivity.this); + + // Populate spinner with sample values from an array + mSpinner = (Spinner) findViewById(R.id.spinner); + mSpinner.setAdapter( + new ArrayAdapter<String>( + this, // Context + android.R.layout.simple_list_item_1, // Layout + new ArrayList<String>(Arrays.asList(SPINNER_VALUES)) // Data source + )); + + // Read in a sample value, if it's not set. + int selection = mPrefs.getInt(PREF_SPINNER_POS, PREF_SPINNER_VALUE_ISNULL); + if (selection != PREF_SPINNER_VALUE_ISNULL) { + mSpinner.setSelection(selection); + } + + // Callback to persist spinner data whenever a new value is selected. This will be the + // focus of our sample unit test. + mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + + // The methods below commit the ID of the currently selected item in the spinner + // to disk, using a SharedPreferences file. + // + // Note: A common mistake here is to forget to call .commit(). Try removing this + // statement and running the tests to watch them fail. + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + mPrefs.edit().putInt(PREF_SPINNER_POS, position).commit(); + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + mPrefs.edit().remove(PREF_SPINNER_POS).commit(); + } + }); + } +} diff --git a/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/Log.java b/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/LogFragment.java b/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/LogNode.java b/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/LogView.java b/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/LogWrapper.java b/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/MessageOnlyLogFilter.java b/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/ActivityInstrumentation/src/com.example.android.common.logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/AdvancedImmersiveMode/AndroidManifest.xml b/samples/browseable/AdvancedImmersiveMode/AndroidManifest.xml new file mode 100644 index 000000000..1d01856c8 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/AndroidManifest.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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 versionCode is an integer representation of this version of your application. New + versions get higher numbers, so the upgrade system can avoid dealing with the ambiguity + of "1.9" vs "1.10". versionName, on the other hand, can be whatever you want, as the code + that handles upgrading Android apps between versions on your device just ignores it.--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.advancedimmersivemode" + android:versionCode="1" + android:versionName="1.0"> + + <!-- This sample is to demonstrate features released in API 19. + So while it would technically run on an earlier version of Android, + there wouldn't be much point) --> + <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" /> + <!-- allowBackup declares if the app can be part of device-wide backups such as "adb backup" --> + <!-- theme is a way of applying UI decisions across your entire application. You can also + define it on a per-application basis. --> + <application + android:allowBackup="true" + android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@style/AppTheme"> + + <!-- Every activity needs its own Manifest element. The intent-filter contained in the + element declares the intents that can be used to activate this Activity. For instance, + the one below flags this Activity as a "main" entry point of this app, and suitable + for creating a shortcut to in the Launcher. If you wanted your app to have 5 + different Activities available in the launcher, you could just make 5 activities + with that intent filter. Please don't do that. Just because it's a good example + doesn't mean it's a good idea. --> + <activity android:name=".MainActivity" + android:label="@string/app_name" + android:uiOptions="splitActionBarWhenNarrow"> + <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/browseable/AdvancedImmersiveMode/_index.jd b/samples/browseable/AdvancedImmersiveMode/_index.jd new file mode 100644 index 000000000..901b56940 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/_index.jd @@ -0,0 +1,18 @@ + + + +page.tags="AdvancedImmersiveMode" +sample.group=UI +@jd:body + +<p> + + \"Immersive Mode\" is a new UI mode which improves \"hide full screen\" and + \"hide nav bar\" modes, by letting users swipe the bars in and out. This sample + lets the user experiment with immersive mode by enabling it and seeing how it interacts + with some of the other UI flags related to full-screen apps. + \n\nThis sample also lets the user choose between normal immersive mode and "sticky" + immersive mode, which removes the status bar and nav bar + a few seconds after the user has swiped them back in. + + </p> diff --git a/samples/browseable/AdvancedImmersiveMode/res/drawable-hdpi/ic_launcher.png b/samples/browseable/AdvancedImmersiveMode/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..b1efaf4b2 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/AdvancedImmersiveMode/res/drawable-hdpi/tile.9.png b/samples/browseable/AdvancedImmersiveMode/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/AdvancedImmersiveMode/res/drawable-mdpi/ic_launcher.png b/samples/browseable/AdvancedImmersiveMode/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f5f9244f2 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/AdvancedImmersiveMode/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/AdvancedImmersiveMode/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..5d07b3f06 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/AdvancedImmersiveMode/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/AdvancedImmersiveMode/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6ef21e1f4 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/AdvancedImmersiveMode/res/layout/activity_main.xml b/samples/browseable/AdvancedImmersiveMode/res/layout/activity_main.xml new file mode 100755 index 000000000..bc5a57591 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/res/layout/activity_main.xml @@ -0,0 +1,38 @@ +<!-- + 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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:id="@+id/sample_main_layout"> + <TextView android:id="@+id/sample_output" + style="@style/Widget.SampleMessage" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:text="@string/intro_message" /> + <View + android:layout_width="fill_parent" + android:layout_height="1dp" + android:background="@android:color/darker_gray"/> + <fragment + android:name="com.example.android.common.logger.LogFragment" + android:id="@+id/log_fragment" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" /> +</LinearLayout> diff --git a/samples/browseable/AdvancedImmersiveMode/res/menu/main.xml b/samples/browseable/AdvancedImmersiveMode/res/menu/main.xml new file mode 100644 index 000000000..2c3515dd4 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/res/menu/main.xml @@ -0,0 +1,21 @@ +<!-- + 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. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/sample_action" + android:showAsAction="ifRoom|withText" + android:title="@string/sample_action" /> +</menu> diff --git a/samples/browseable/AdvancedImmersiveMode/res/values-sw600dp/dimens.xml b/samples/browseable/AdvancedImmersiveMode/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/AdvancedImmersiveMode/res/values-sw600dp/styles.xml b/samples/browseable/AdvancedImmersiveMode/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/AdvancedImmersiveMode/res/values/base-strings.xml b/samples/browseable/AdvancedImmersiveMode/res/values/base-strings.xml new file mode 100644 index 000000000..305e12abf --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/res/values/base-strings.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">AdvancedImmersiveMode</string> + <string name="intro_message"> + <![CDATA[ + + + \"Immersive Mode\" is a new UI mode which improves \"hide full screen\" and + \"hide nav bar\" modes, by letting users swipe the bars in and out. This sample + lets the user experiment with immersive mode by enabling it and seeing how it interacts + with some of the other UI flags related to full-screen apps. + \n\nThis sample also lets the user choose between normal immersive mode and "sticky" + immersive mode, which removes the status bar and nav bar + a few seconds after the user has swiped them back in. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/AdvancedImmersiveMode/res/values/dimens.xml b/samples/browseable/AdvancedImmersiveMode/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/AdvancedImmersiveMode/res/values/strings.xml b/samples/browseable/AdvancedImmersiveMode/res/values/strings.xml new file mode 100644 index 000000000..a65b8916a --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/res/values/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="sample_action">Try these settings!</string> +</resources> diff --git a/samples/browseable/AdvancedImmersiveMode/res/values/styles.xml b/samples/browseable/AdvancedImmersiveMode/res/values/styles.xml new file mode 100644 index 000000000..d3f82ff64 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/res/values/styles.xml @@ -0,0 +1,51 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="AppTheme" parent="Theme.Base" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + + + <style name="Widget.SampleOutput"> + <item name="android:padding">@dimen/margin_medium</item> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Log" parent="Widget.SampleOutput"> + <item name="android:typeface">monospace</item> + </style> + +</resources> diff --git a/samples/browseable/AdvancedImmersiveMode/src/com.example.android.advancedimmersivemode/AdvancedImmersiveModeFragment.java b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.advancedimmersivemode/AdvancedImmersiveModeFragment.java new file mode 100644 index 000000000..fe11ecb4d --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.advancedimmersivemode/AdvancedImmersiveModeFragment.java @@ -0,0 +1,174 @@ +/* +* Copyright (C) 2012 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.advancedimmersivemode; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; + +import com.example.android.common.logger.Log; + +/** + * Demonstrates how to update the app's UI by toggling immersive mode. + * Checkboxes are also made available for toggling other UI flags which can + * alter the behavior of immersive mode. + */ +public class AdvancedImmersiveModeFragment extends Fragment { + + public static final String TAG = "AdvancedImmersiveModeFragment"; + public CheckBox mHideNavCheckbox; + public CheckBox mHideStatusBarCheckBox; + public CheckBox mImmersiveModeCheckBox; + public CheckBox mImmersiveModeStickyCheckBox; + public CheckBox mLowProfileCheckBox; + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + final View decorView = getActivity().getWindow().getDecorView(); + ViewGroup parentView = (ViewGroup) getActivity().getWindow().getDecorView() + .findViewById(R.id.sample_main_layout); + + mLowProfileCheckBox = new CheckBox(getActivity()); + mLowProfileCheckBox.setText("Enable Low Profile mode."); + parentView.addView(mLowProfileCheckBox); + + mHideNavCheckbox = new CheckBox(getActivity()); + mHideNavCheckbox.setChecked(true); + mHideNavCheckbox.setText("Hide Navigation bar"); + parentView.addView(mHideNavCheckbox); + + mHideStatusBarCheckBox = new CheckBox(getActivity()); + mHideStatusBarCheckBox.setChecked(true); + mHideStatusBarCheckBox.setText("Hide Status Bar"); + parentView.addView(mHideStatusBarCheckBox); + + mImmersiveModeCheckBox = new CheckBox(getActivity()); + mImmersiveModeCheckBox.setText("Enable Immersive Mode."); + parentView.addView(mImmersiveModeCheckBox); + + mImmersiveModeStickyCheckBox = new CheckBox(getActivity()); + mImmersiveModeStickyCheckBox.setText("Enable Immersive Mode (Sticky)"); + parentView.addView(mImmersiveModeStickyCheckBox); + + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.sample_action) { + toggleImmersiveMode(); + } + return true; + } + + /** + * Detects and toggles immersive mode (also known as "hidey bar" mode). + */ + public void toggleImmersiveMode() { + + // BEGIN_INCLUDE (get_current_ui_flags) + // The "Decor View" is the parent view of the Activity. It's also conveniently the easiest + // one to find from within a fragment, since there's a handy helper method to pull it, and + // we don't have to bother with picking a view somewhere deeper in the hierarchy and calling + // "findViewById" on it. + View decorView = getActivity().getWindow().getDecorView(); + int uiOptions = decorView.getSystemUiVisibility(); + int newUiOptions = uiOptions; + // END_INCLUDE (get_current_ui_flags) + + // BEGIN_INCLUDE (toggle_lowprofile_mode) + // Low profile mode doesn't resize the screen at all, but it covers the nav & status bar + // icons with black so they're less distracting. Unlike "full screen" and "hide nav bar," + // this mode doesn't interact with immersive mode at all, but it's instructive when running + // this sample to observe the differences in behavior. + if (mLowProfileCheckBox.isChecked()) { + newUiOptions |= View.SYSTEM_UI_FLAG_LOW_PROFILE; + } else { + newUiOptions &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE; + } + // END_INCLUDE (toggle_lowprofile_mode) + + // BEGIN_INCLUDE (toggle_fullscreen_mode) + // When enabled, this flag hides non-critical UI, such as the status bar, + // which usually shows notification icons, battery life, etc + // on phone-sized devices. The bar reappears when the user swipes it down. When immersive + // mode is also enabled, the app-drawable area expands, and when the status bar is swiped + // down, it appears semi-transparently and slides in over the app, instead of pushing it + // down. + if (mHideStatusBarCheckBox.isChecked()) { + newUiOptions |= View.SYSTEM_UI_FLAG_FULLSCREEN; + } else { + newUiOptions &= ~View.SYSTEM_UI_FLAG_FULLSCREEN; + } + // END_INCLUDE (toggle_fullscreen_mode) + + // BEGIN_INCLUDE (toggle_hidenav_mode) + // When enabled, this flag hides the black nav bar along the bottom, + // where the home/back buttons are. The nav bar normally instantly reappears + // when the user touches the screen. When immersive mode is also enabled, the nav bar + // stays hidden until the user swipes it back. + if (mHideNavCheckbox.isChecked()) { + newUiOptions |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + } else { + newUiOptions &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + } + // END_INCLUDE (toggle_hidenav_mode) + + // BEGIN_INCLUDE (toggle_immersive_mode) + // Immersive mode doesn't do anything without at least one of the previous flags + // enabled. When enabled, it allows the user to swipe the status and/or nav bars + // off-screen. When the user swipes the bars back onto the screen, the flags are cleared + // and immersive mode is automatically disabled. + if (mImmersiveModeCheckBox.isChecked()) { + newUiOptions |= View.SYSTEM_UI_FLAG_IMMERSIVE; + } else { + newUiOptions &= ~View.SYSTEM_UI_FLAG_IMMERSIVE; + } + // END_INCLUDE (toggle_immersive_mode) + + // BEGIN_INCLUDE (toggle_immersive_mode_sticky) + // There's actually two forms of immersive mode, normal and "sticky". Sticky immersive mode + // is different in 2 key ways: + // + // * Uses semi-transparent bars for the nav and status bars + // * This UI flag will *not* be cleared when the user interacts with the UI. + // When the user swipes, the bars will temporarily appear for a few seconds and then + // disappear again. + if (mImmersiveModeStickyCheckBox.isChecked()) { + newUiOptions |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + } else { + newUiOptions &= ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + } + // END_INCLUDE (toggle_immersive_mode_sticky) + + // BEGIN_INCLUDE (set_ui_flags) + //Set the new UI flags. + decorView.setSystemUiVisibility(newUiOptions); + Log.i(TAG, "Current height: " + decorView.getHeight() + ", width: " + decorView.getWidth()); + // END_INCLUDE (set_ui_flags) + } +} diff --git a/samples/browseable/AdvancedImmersiveMode/src/com.example.android.advancedimmersivemode/MainActivity.java b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.advancedimmersivemode/MainActivity.java new file mode 100644 index 000000000..0ebe8784f --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.advancedimmersivemode/MainActivity.java @@ -0,0 +1,80 @@ +/* +* 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.advancedimmersivemode; + +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.view.Menu; + +import com.example.android.common.activities.SampleActivityBase; +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogFragment; +import com.example.android.common.logger.LogWrapper; +import com.example.android.common.logger.MessageOnlyLogFilter; + +/** + * A simple launcher activity containing a summary sample description + * and a few action bar buttons. + */ +public class MainActivity extends SampleActivityBase { + + public static final String TAG = "MainActivity"; + + public static final String FRAGTAG = "AdvancedImmersiveModeFragment"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + if (getSupportFragmentManager().findFragmentByTag(FRAGTAG) == null ) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + AdvancedImmersiveModeFragment fragment = new AdvancedImmersiveModeFragment(); + transaction.add(fragment, FRAGTAG); + transaction.commit(); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + /** Create a chain of targets that will receive log data */ + @Override + public void initializeLogging() { + // Wraps Android's native log framework. + LogWrapper logWrapper = new LogWrapper(); + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + Log.setLogNode(logWrapper); + + // Filter strips out everything except the message text. + MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); + logWrapper.setNext(msgFilter); + + // On screen logging via a fragment with a TextView. + LogFragment logFragment = (LogFragment) getSupportFragmentManager() + .findFragmentById(R.id.log_fragment); + msgFilter.setNext(logFragment.getLogView()); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/activities/SampleActivityBase.java new file mode 100644 index 000000000..3228927b7 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/activities/SampleActivityBase.java @@ -0,0 +1,52 @@ +/* +* 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.common.activities; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogWrapper; + +/** + * Base launcher activity, to handle most of the common plumbing for samples. + */ +public class SampleActivityBase extends FragmentActivity { + + public static final String TAG = "SampleActivityBase"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + initializeLogging(); + } + + /** Set up targets to receive log data */ + public void initializeLogging() { + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/Log.java b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/LogNode.java b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/LogView.java b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/src/com.example.android.common/logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/AppRestrictions/AndroidManifest.xml b/samples/browseable/AppRestrictions/AndroidManifest.xml new file mode 100644 index 000000000..b492bbfce --- /dev/null +++ b/samples/browseable/AppRestrictions/AndroidManifest.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.apprestrictions" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk android:minSdkVersion="18" /> + + <application android:label="@string/app_name" + android:icon="@drawable/ic_launcher"> + + <activity android:name="MainActivity" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <activity android:name="CustomRestrictionsActivity" + android:label="@string/restrictions_activity_label" /> + + <receiver android:name="GetRestrictionsReceiver"> + <intent-filter> + <action android:name="android.intent.action.GET_RESTRICTION_ENTRIES" /> + </intent-filter> + </receiver> + </application> + +</manifest> diff --git a/samples/browseable/AppRestrictions/_index.jd b/samples/browseable/AppRestrictions/_index.jd new file mode 100644 index 000000000..83aa08cb7 --- /dev/null +++ b/samples/browseable/AppRestrictions/_index.jd @@ -0,0 +1,17 @@ + + + +page.tags="AppRestrictions" +sample.group=Content +@jd:body + +<p> + + This sample demonstrates the use of the App Restriction feature, which is available on + Android 4.3 and above tablet device with the multiuser feature. + + When launched under the primary User account, you can toggle between standard app restriction + types and custom. When launched under a restricted profile, this activity displays app + restriction settings, if available. + + </p> diff --git a/samples/browseable/AppRestrictions/res/drawable-hdpi/ic_launcher.png b/samples/browseable/AppRestrictions/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..f36c473a1 --- /dev/null +++ b/samples/browseable/AppRestrictions/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictions/res/drawable-hdpi/tile.9.png b/samples/browseable/AppRestrictions/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/AppRestrictions/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/AppRestrictions/res/drawable-mdpi/ic_launcher.png b/samples/browseable/AppRestrictions/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..5ab2e0d33 --- /dev/null +++ b/samples/browseable/AppRestrictions/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictions/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/AppRestrictions/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..76228388e --- /dev/null +++ b/samples/browseable/AppRestrictions/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictions/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/AppRestrictions/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..7f55feff2 --- /dev/null +++ b/samples/browseable/AppRestrictions/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictions/res/layout/activity_main.xml b/samples/browseable/AppRestrictions/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/AppRestrictions/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/AppRestrictions/res/layout/main.xml b/samples/browseable/AppRestrictions/res/layout/main.xml new file mode 100644 index 000000000..55e2c8eb4 --- /dev/null +++ b/samples/browseable/AppRestrictions/res/layout/main.xml @@ -0,0 +1,114 @@ +<?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. +--> +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <LinearLayout android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="20dp"> + + <TextView android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="10dp" + android:textSize="18sp" + android:text="@string/sample_app_description"/> + + <LinearLayout android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="15dp"> + <CheckBox android:id="@+id/custom_app_limits" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:onClick="onCustomClicked"/> + <TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="18sp" + android:text="@string/custom_description" + android:onClick="onCustomClicked" + android:layout_weight="1"/> + </LinearLayout> + + <!-- Separator --> + <View android:layout_height="1dp" + android:background="@android:color/white" + android:layout_width="match_parent" + android:layout_margin="25dp"/> + + <!-- Section to show app restriction settings under a restricted profile. --> + <TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="20sp" + android:text="@string/current_app_limits_label"/> + <TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="18sp" + android:layout_marginBottom="10dp" + android:text="@string/current_app_limits_description"/> + + <LinearLayout android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="20dp"> + <TextView android:layout_width="210dp" + android:layout_height="wrap_content" + android:textSize="18sp" + android:text="@string/boolean_entry_title"/> + <Space android:layout_height="1dp" + android:layout_width="15dp"/> + <TextView android:id="@+id/boolean_entry_id" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="18sp" + android:text="@string/boolean_entry_title"/> + </LinearLayout> + + <LinearLayout android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="20dp"> + <TextView android:layout_width="210dp" + android:layout_height="wrap_content" + android:textSize="18sp" + android:text="@string/choice_entry_title"/> + <Space android:layout_height="1dp" + android:layout_width="15dp"/> + <TextView android:id="@+id/choice_entry_id" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="18sp" + android:text="@string/boolean_entry_title"/> + </LinearLayout> + + <LinearLayout android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="20dp"> + <TextView android:layout_width="210dp" + android:layout_height="wrap_content" + android:textSize="18sp" + android:text="@string/multi_entry_title"/> + <Space android:layout_height="1dp" + android:layout_width="15dp"/> + <TextView android:id="@+id/multi_entry_id" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="18sp" + android:text="@string/multi_entry_title"/> + </LinearLayout> + </LinearLayout> +</ScrollView>
\ No newline at end of file diff --git a/samples/browseable/AppRestrictions/res/values-sw600dp/dimens.xml b/samples/browseable/AppRestrictions/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/AppRestrictions/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/AppRestrictions/res/values-sw600dp/styles.xml b/samples/browseable/AppRestrictions/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/AppRestrictions/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/AppRestrictions/res/values/base-strings.xml b/samples/browseable/AppRestrictions/res/values/base-strings.xml new file mode 100644 index 000000000..08a466397 --- /dev/null +++ b/samples/browseable/AppRestrictions/res/values/base-strings.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">AppRestrictions</string> + <string name="intro_message"> + <![CDATA[ + + + This sample demonstrates the use of the App Restriction feature, which is available on + Android 4.3 and above tablet device with the multiuser feature. + + When launched under the primary User account, you can toggle between standard app restriction + types and custom. When launched under a restricted profile, this activity displays app + restriction settings, if available. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/AppRestrictions/res/values/dimens.xml b/samples/browseable/AppRestrictions/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/AppRestrictions/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/AppRestrictions/res/values/strings.xml b/samples/browseable/AppRestrictions/res/values/strings.xml new file mode 100644 index 000000000..534edf033 --- /dev/null +++ b/samples/browseable/AppRestrictions/res/values/strings.xml @@ -0,0 +1,64 @@ +<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="restrictions_activity_label">Custom app restrictions</string> + <string name="boolean_entry_title">Test boolean type</string> + <string name="choice_entry_title">Test choice type</string> + <string name="multi_entry_title">Test multi-select type</string> + <string name="custom_description">If checked, use a custom app restriction Activity. Otherwise, + use standard restriction types. + </string> + <string name="sample_app_description">Note: This sample app requires the restricted profile + feature.\n\n + 1. If this is the primary user, go to Settings > Users.\n\n + 2. Create a restricted profile, if one doesn\'t exist already.\n\n + 3. Open the profile settings, locate the sample app, and tap the app restriction settings + icon. Configure app restrictions for the app.\n\n + 4. In the lock screen, switch to the user\'s restricted profile, launch this sample app, + and see the configured app restrictions displayed.\n + </string> + <string name="settings_button_label">Go to Settings</string> + <string name="current_app_limits_label">Current app restriction settings:</string> + <string name="na">N/A</string> + <string name="current_app_limits_description">Your app can restrict its content based on these + settings, which can be configured through the primary user\'s Users Settings. + </string> + + <string-array name="multi_entry_entries"> + <item>Ice Cream</item> + <item>Jelly Bean</item> + <item>More Jelly Bean</item> + </string-array> + + <string-array name="multi_entry_values" translateable="false"> + <item>1</item> + <item>2</item> + <item>3</item> + </string-array> + + <string-array name="choice_entry_entries"> + <item>Ice Cream</item> + <item>Jelly Bean</item> + <item>More Jelly Bean</item> + </string-array> + + <string-array name="choice_entry_values" translateable="false"> + <item>1</item> + <item>2</item> + <item>3</item> + </string-array> + +</resources>
\ No newline at end of file diff --git a/samples/browseable/AppRestrictions/res/values/styles.xml b/samples/browseable/AppRestrictions/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/AppRestrictions/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/AppRestrictions/res/xml/custom_prefs.xml b/samples/browseable/AppRestrictions/res/xml/custom_prefs.xml new file mode 100644 index 000000000..5a3cf0df5 --- /dev/null +++ b/samples/browseable/AppRestrictions/res/xml/custom_prefs.xml @@ -0,0 +1,33 @@ +<?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. +--> + +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" + android:title="@string/restrictions_activity_label"> + + <CheckBoxPreference android:key="pref_boolean" + android:title="@string/boolean_entry_title" /> + + <ListPreference android:key="pref_choice" + android:title="@string/choice_entry_title" + android:entries="@array/choice_entry_entries" + android:entryValues="@array/choice_entry_values" /> + + <MultiSelectListPreference android:key="pref_multi" + android:title="@string/multi_entry_title" + android:entries="@array/multi_entry_entries" + android:entryValues="@array/multi_entry_values" /> + +</PreferenceScreen> diff --git a/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/CustomRestrictionsActivity.java b/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/CustomRestrictionsActivity.java new file mode 100644 index 000000000..213b31357 --- /dev/null +++ b/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/CustomRestrictionsActivity.java @@ -0,0 +1,44 @@ +/* + * 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.apprestrictions; + +import android.app.Activity; +import android.os.Bundle; + +/** + * This activity demonstrates how an app can integrate its own custom app restriction settings + * with the restricted profile feature. + * + * This sample app maintains custom app restriction settings in shared preferences. When + * the activity is invoked (from Settings > Users), the stored settings are used to initialize + * the custom configuration on the user interface. Three sample input types are + * shown: checkbox, single-choice, and multi-choice. When the settings are modified by the user, + * the corresponding restriction entries are saved, which are retrievable under a restricted + * profile. + */ +public class CustomRestrictionsActivity extends Activity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (savedInstanceState == null) { + getFragmentManager().beginTransaction().replace(android.R.id.content, + new CustomRestrictionsFragment()).commit(); + } + } +} diff --git a/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/CustomRestrictionsFragment.java b/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/CustomRestrictionsFragment.java new file mode 100644 index 000000000..b04dfd1f7 --- /dev/null +++ b/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/CustomRestrictionsFragment.java @@ -0,0 +1,202 @@ +/* + * 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.apprestrictions; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.RestrictionEntry; +import android.os.Bundle; +import android.os.UserManager; +import android.preference.CheckBoxPreference; +import android.preference.ListPreference; +import android.preference.MultiSelectListPreference; +import android.preference.Preference; +import android.preference.PreferenceFragment; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * This fragment is included in {@code CustomRestrictionsActivity}. It demonstrates how an app + * can integrate its own custom app restriction settings with the restricted profile feature. + * + * This sample app maintains custom app restriction settings in shared preferences. Your app + * can use other methods to maintain the settings. When this activity is invoked + * (from Settings > Users > Restricted Profile), the shared preferences are used to initialize + * the custom configuration on the user interface. + * + * Three sample input types are shown: checkbox, single-choice, and multi-choice. When the + * settings are modified by the user, the corresponding restriction entries are saved in the + * platform. The saved restriction entries are retrievable when the app is launched under a + * restricted profile. + */ +public class CustomRestrictionsFragment extends PreferenceFragment + implements Preference.OnPreferenceChangeListener { + + // Shared preference key for the boolean restriction. + private static final String KEY_BOOLEAN_PREF = "pref_boolean"; + // Shared preference key for the single-select restriction. + private static final String KEY_CHOICE_PREF = "pref_choice"; + // Shared preference key for the multi-select restriction. + private static final String KEY_MULTI_PREF = "pref_multi"; + + + private List<RestrictionEntry> mRestrictions; + private Bundle mRestrictionsBundle; + + // Shared preferences for each of the sample input types. + private CheckBoxPreference mBooleanPref; + private ListPreference mChoicePref; + private MultiSelectListPreference mMultiPref; + + // Restriction entries for each of the sample input types. + private RestrictionEntry mBooleanEntry; + private RestrictionEntry mChoiceEntry; + private RestrictionEntry mMultiEntry; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.custom_prefs); + + // This sample app uses shared preferences to maintain app restriction settings. Your app + // can use other methods to maintain the settings. + mBooleanPref = (CheckBoxPreference) findPreference(KEY_BOOLEAN_PREF); + mChoicePref = (ListPreference) findPreference(KEY_CHOICE_PREF); + mMultiPref = (MultiSelectListPreference) findPreference(KEY_MULTI_PREF); + + mBooleanPref.setOnPreferenceChangeListener(this); + mChoicePref.setOnPreferenceChangeListener(this); + mMultiPref.setOnPreferenceChangeListener(this); + + setRetainInstance(true); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + final Activity activity = getActivity(); + + // BEGIN_INCLUDE (GET_CURRENT_RESTRICTIONS) + // Existing app restriction settings, if exist, can be retrieved from the Bundle. + mRestrictionsBundle = + activity.getIntent().getBundleExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE); + + if (mRestrictionsBundle == null) { + mRestrictionsBundle = + ((UserManager) activity.getSystemService(Context.USER_SERVICE)) + .getApplicationRestrictions(activity.getPackageName()); + } + + if (mRestrictionsBundle == null) { + mRestrictionsBundle = new Bundle(); + } + + mRestrictions = activity.getIntent().getParcelableArrayListExtra( + Intent.EXTRA_RESTRICTIONS_LIST); + // END_INCLUDE (GET_CURRENT_RESTRICTIONS) + + // Transfers the saved values into the preference hierarchy. + if (mRestrictions != null) { + for (RestrictionEntry entry : mRestrictions) { + if (entry.getKey().equals(GetRestrictionsReceiver.KEY_BOOLEAN)) { + mBooleanPref.setChecked(entry.getSelectedState()); + mBooleanEntry = entry; + } else if (entry.getKey().equals(GetRestrictionsReceiver.KEY_CHOICE)) { + mChoicePref.setValue(entry.getSelectedString()); + mChoiceEntry = entry; + } else if (entry.getKey().equals(GetRestrictionsReceiver.KEY_MULTI_SELECT)) { + HashSet<String> set = new HashSet<String>(); + for (String value : entry.getAllSelectedStrings()) { + set.add(value); + } + mMultiPref.setValues(set); + mMultiEntry = entry; + } + } + } else { + mRestrictions = new ArrayList<RestrictionEntry>(); + + // Initializes the boolean restriction entry and updates its corresponding shared + // preference value. + mBooleanEntry = new RestrictionEntry(GetRestrictionsReceiver.KEY_BOOLEAN, + mRestrictionsBundle.getBoolean(GetRestrictionsReceiver.KEY_BOOLEAN, false)); + mBooleanEntry.setType(RestrictionEntry.TYPE_BOOLEAN); + mBooleanPref.setChecked(mBooleanEntry.getSelectedState()); + + // Initializes the single choice restriction entry and updates its corresponding + // shared preference value. + mChoiceEntry = new RestrictionEntry(GetRestrictionsReceiver.KEY_CHOICE, + mRestrictionsBundle.getString(GetRestrictionsReceiver.KEY_CHOICE)); + mChoiceEntry.setType(RestrictionEntry.TYPE_CHOICE); + mChoicePref.setValue(mChoiceEntry.getSelectedString()); + + // Initializes the multi-select restriction entry and updates its corresponding + // shared preference value. + mMultiEntry = new RestrictionEntry(GetRestrictionsReceiver.KEY_MULTI_SELECT, + mRestrictionsBundle.getStringArray( + GetRestrictionsReceiver.KEY_MULTI_SELECT)); + mMultiEntry.setType(RestrictionEntry.TYPE_MULTI_SELECT); + if (mMultiEntry.getAllSelectedStrings() != null) { + HashSet<String> set = new HashSet<String>(); + final String[] values = mRestrictionsBundle.getStringArray( + GetRestrictionsReceiver.KEY_MULTI_SELECT); + if (values != null) { + for (String value : values) { + set.add(value); + } + } + mMultiPref.setValues(set); + } + mRestrictions.add(mBooleanEntry); + mRestrictions.add(mChoiceEntry); + mRestrictions.add(mMultiEntry); + } + // Prepares result to be passed back to the Settings app when the custom restrictions + // activity finishes. + Intent intent = new Intent(getActivity().getIntent()); + intent.putParcelableArrayListExtra(Intent.EXTRA_RESTRICTIONS_LIST, + new ArrayList<RestrictionEntry>(mRestrictions)); + getActivity().setResult(Activity.RESULT_OK, intent); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference == mBooleanPref) { + mBooleanEntry.setSelectedState((Boolean) newValue); + } else if (preference == mChoicePref) { + mChoiceEntry.setSelectedString((String) newValue); + } else if (preference == mMultiPref) { + String[] selectedStrings = new String[((Set<String>)newValue).size()]; + int i = 0; + for (String value : (Set<String>) newValue) { + selectedStrings[i++] = value; + } + mMultiEntry.setAllSelectedStrings(selectedStrings); + } + + // Saves all the app restriction configuration changes from the custom activity. + Intent intent = new Intent(getActivity().getIntent()); + intent.putParcelableArrayListExtra(Intent.EXTRA_RESTRICTIONS_LIST, + new ArrayList<RestrictionEntry>(mRestrictions)); + getActivity().setResult(Activity.RESULT_OK, intent); + return true; + } +} diff --git a/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/GetRestrictionsReceiver.java b/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/GetRestrictionsReceiver.java new file mode 100644 index 000000000..bb5a28391 --- /dev/null +++ b/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/GetRestrictionsReceiver.java @@ -0,0 +1,162 @@ +/* + * 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.apprestrictions; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.RestrictionEntry; +import android.content.res.Resources; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.util.Log; + +import java.util.ArrayList; + +public class GetRestrictionsReceiver extends BroadcastReceiver { + private static final String TAG = GetRestrictionsReceiver.class.getSimpleName(); + + // Keys for referencing app restriction settings from the platform. + public static final String KEY_BOOLEAN = "boolean_key"; + public static final String KEY_CHOICE = "choice_key"; + public static final String KEY_MULTI_SELECT = "multi_key"; + + @Override + public void onReceive(final Context context, Intent intent) { + final PendingResult result = goAsync(); + + // If app restriction settings are already created, they will be included in the Bundle + // as key/value pairs. + final Bundle existingRestrictions = + intent.getBundleExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE); + Log.i(TAG, "existingRestrictions = " + existingRestrictions); + + new Thread() { + public void run() { + createRestrictions(context, result, existingRestrictions); + } + }.start(); + } + + // Initializes a boolean type restriction entry. + public static void populateBooleanEntry(Resources res, RestrictionEntry entry) { + entry.setType(RestrictionEntry.TYPE_BOOLEAN); + entry.setTitle(res.getString(R.string.boolean_entry_title)); + } + + // Initializes a single choice type restriction entry. + public static void populateChoiceEntry(Resources res, RestrictionEntry reSingleChoice) { + String[] choiceEntries = res.getStringArray(R.array.choice_entry_entries); + String[] choiceValues = res.getStringArray(R.array.choice_entry_values); + if (reSingleChoice.getSelectedString() == null) { + reSingleChoice.setSelectedString(choiceValues[0]); + } + reSingleChoice.setTitle(res.getString(R.string.choice_entry_title)); + reSingleChoice.setChoiceEntries(choiceEntries); + reSingleChoice.setChoiceValues(choiceValues); + reSingleChoice.setType(RestrictionEntry.TYPE_CHOICE); + } + + // Initializes a multi-select type restriction entry. + public static void populateMultiEntry(Resources res, RestrictionEntry reMultiSelect) { + String[] multiEntries = res.getStringArray(R.array.multi_entry_entries); + String[] multiValues = res.getStringArray(R.array.multi_entry_values); + if (reMultiSelect.getAllSelectedStrings() == null) { + reMultiSelect.setAllSelectedStrings(new String[0]); + } + reMultiSelect.setTitle(res.getString(R.string.multi_entry_title)); + reMultiSelect.setChoiceEntries(multiEntries); + reMultiSelect.setChoiceValues(multiValues); + reMultiSelect.setType(RestrictionEntry.TYPE_MULTI_SELECT); + } + + // Demonstrates the creation of standard app restriction types: boolean, single choice, and + // multi-select. + private ArrayList<RestrictionEntry> initRestrictions(Context context) { + ArrayList<RestrictionEntry> newRestrictions = new ArrayList<RestrictionEntry>(); + Resources res = context.getResources(); + + RestrictionEntry reBoolean = new RestrictionEntry(KEY_BOOLEAN, false); + populateBooleanEntry(res, reBoolean); + newRestrictions.add(reBoolean); + + RestrictionEntry reSingleChoice = new RestrictionEntry(KEY_CHOICE, (String) null); + populateChoiceEntry(res, reSingleChoice); + newRestrictions.add(reSingleChoice); + + RestrictionEntry reMultiSelect = new RestrictionEntry(KEY_MULTI_SELECT, (String[]) null); + populateMultiEntry(res, reMultiSelect); + newRestrictions.add(reMultiSelect); + + return newRestrictions; + } + + private void createRestrictions(Context context, PendingResult result, + Bundle existingRestrictions) { + // The incoming restrictions bundle contains key/value pairs representing existing app + // restrictions for this package. In order to retain existing app restrictions, you need to + // construct new restriction entries and then copy in any existing values for the new keys. + ArrayList<RestrictionEntry> newEntries = initRestrictions(context); + + // If app restrictions were not previously configured for the package, create the default + // restrictions entries and return them. + if (existingRestrictions == null) { + Bundle extras = new Bundle(); + extras.putParcelableArrayList(Intent.EXTRA_RESTRICTIONS_LIST, newEntries); + result.setResult(Activity.RESULT_OK, null, extras); + result.finish(); + return; + } + + // Retains current restriction settings by transferring existing restriction entries to + // new ones. + for (RestrictionEntry entry : newEntries) { + final String key = entry.getKey(); + if (KEY_BOOLEAN.equals(key)) { + entry.setSelectedState(existingRestrictions.getBoolean(KEY_BOOLEAN)); + } else if (KEY_CHOICE.equals(key)) { + if (existingRestrictions.containsKey(KEY_CHOICE)) { + entry.setSelectedString(existingRestrictions.getString(KEY_CHOICE)); + } + } else if (KEY_MULTI_SELECT.equals(key)) { + if (existingRestrictions.containsKey(KEY_MULTI_SELECT)) { + entry.setAllSelectedStrings(existingRestrictions.getStringArray(key)); + } + } + } + + final Bundle extras = new Bundle(); + + // This path demonstrates the use of a custom app restriction activity instead of standard + // types. When a custom activity is set, the standard types will not be available under + // app restriction settings. + // + // If your app has an existing activity for app restriction configuration, you can set it + // up with the intent here. + if (PreferenceManager.getDefaultSharedPreferences(context) + .getBoolean(MainActivity.CUSTOM_CONFIG_KEY, false)) { + final Intent customIntent = new Intent(); + customIntent.setClass(context, CustomRestrictionsActivity.class); + extras.putParcelable(Intent.EXTRA_RESTRICTIONS_INTENT, customIntent); + } + + extras.putParcelableArrayList(Intent.EXTRA_RESTRICTIONS_LIST, newEntries); + result.setResult(Activity.RESULT_OK, null, extras); + result.finish(); + } +} diff --git a/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/MainActivity.java b/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/MainActivity.java new file mode 100644 index 000000000..57c443906 --- /dev/null +++ b/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/MainActivity.java @@ -0,0 +1,131 @@ +/* + * 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.apprestrictions; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.os.UserManager; +import android.preference.PreferenceManager; +import android.view.View; +import android.widget.CheckBox; +import android.widget.TextView; + +/** + * This is the main user interface of the App Restrictions sample app. It demonstrates the use + * of the App Restriction feature, which is available on Android 4.3 and above tablet devices + * with the multiuser feature. + * + * When launched under the primary User account, you can toggle between standard app restriction + * types and custom. When launched under a restricted profile, this activity displays app + * restriction settings, if available. + * + * Follow these steps to exercise the feature: + * 1. If this is the primary user, go to Settings > Users. + * 2. Create a restricted profile, if one doesn't exist already. + * 3. Open the profile settings, locate the sample app, and tap the app restriction settings + * icon. Configure app restrictions for the app. + * 4. In the lock screen, switch to the user's restricted profile, launch this sample app, + * and see the configured app restrictions displayed. + */ +public class MainActivity extends Activity { + private Bundle mRestrictionsBundle; + + // Checkbox to indicate whether custom or standard app restriction types are selected. + private CheckBox mCustomConfig; + + public static final String CUSTOM_CONFIG_KEY = "custom_config"; + + private TextView mMultiEntryValue; + private TextView mChoiceEntryValue; + private TextView mBooleanEntryValue; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Sets up user interface elements. + setContentView(R.layout.main); + + mCustomConfig = (CheckBox) findViewById(R.id.custom_app_limits); + final boolean customChecked = + PreferenceManager.getDefaultSharedPreferences(this).getBoolean( + CUSTOM_CONFIG_KEY, false); + if (customChecked) mCustomConfig.setChecked(true); + + mMultiEntryValue = (TextView) findViewById(R.id.multi_entry_id); + mChoiceEntryValue = (TextView) findViewById(R.id.choice_entry_id); + mBooleanEntryValue = (TextView) findViewById(R.id.boolean_entry_id); + } + + @Override + protected void onResume() { + super.onResume(); + + // If app restrictions are set for this package, when launched from a restricted profile, + // the settings are available in the returned Bundle as key/value pairs. + mRestrictionsBundle = + ((UserManager) getSystemService(Context.USER_SERVICE)) + .getApplicationRestrictions(getPackageName()); + if (mRestrictionsBundle == null) { + mRestrictionsBundle = new Bundle(); + } + + // Reads and displays values from a boolean type restriction entry, if available. + // An app can utilize these settings to restrict its content under a restricted profile. + final String booleanRestrictionValue = + mRestrictionsBundle.containsKey(GetRestrictionsReceiver.KEY_BOOLEAN) ? + mRestrictionsBundle.getBoolean(GetRestrictionsReceiver.KEY_BOOLEAN) + "": + getString(R.string.na); + mBooleanEntryValue.setText(booleanRestrictionValue); + + // Reads and displays values from a single choice restriction entry, if available. + final String singleChoiceRestrictionValue = + mRestrictionsBundle.containsKey(GetRestrictionsReceiver.KEY_CHOICE) ? + mRestrictionsBundle.getString(GetRestrictionsReceiver.KEY_CHOICE) : + getString(R.string.na); + mChoiceEntryValue.setText(singleChoiceRestrictionValue); + + // Reads and displays values from a multi-select restriction entry, if available. + final String[] multiSelectValues = + mRestrictionsBundle.getStringArray(GetRestrictionsReceiver.KEY_MULTI_SELECT); + if (multiSelectValues == null || multiSelectValues.length == 0) { + mMultiEntryValue.setText(getString(R.string.na)); + } else { + String tempValue = ""; + for (String value : multiSelectValues) { + tempValue = tempValue + value + " "; + } + mMultiEntryValue.setText(tempValue); + } + } + + /** + * Saves custom app restriction to the shared preference. + * + * This flag is used by {@code GetRestrictionsReceiver} to determine if a custom app + * restriction activity should be used. + * + * @param view + */ + public void onCustomClicked(View view) { + final SharedPreferences.Editor editor = + PreferenceManager.getDefaultSharedPreferences(this).edit(); + editor.putBoolean(CUSTOM_CONFIG_KEY, mCustomConfig.isChecked()).commit(); + } +} diff --git a/samples/browseable/Basic/AndroidManifest.xml b/samples/browseable/Basic/AndroidManifest.xml new file mode 100644 index 000000000..332c055be --- /dev/null +++ b/samples/browseable/Basic/AndroidManifest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.actionbarcompat.basic" + android:versionCode="1" + android:versionName="1.0"> + + <!-- ActionBarCompat provides an Action Bar from API v7 onwards --> + <uses-sdk + android:minSdkVersion="7" + android:targetSdkVersion="17" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/Theme.AppCompat" + android:allowBackup="true"> + + <activity android:name=".MainActivity"> + <!-- Launcher Intent filter --> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/samples/browseable/Basic/_index.jd b/samples/browseable/Basic/_index.jd new file mode 100644 index 000000000..17f668db5 --- /dev/null +++ b/samples/browseable/Basic/_index.jd @@ -0,0 +1,15 @@ + + + +page.tags="Basic" +sample.group=UI +@jd:body + +<p> + + This sample shows you how to use ActionBarCompat to create a basic Activity which + displays action items. It covers inflating items from a menu resource, as well as adding + an item in code. Items that are not shown as action items on the Action Bar are + displayed in the action bar overflow. + + </p> diff --git a/samples/browseable/Basic/res/drawable-hdpi/ic_action_location.png b/samples/browseable/Basic/res/drawable-hdpi/ic_action_location.png Binary files differnew file mode 100644 index 000000000..a42b3ea9f --- /dev/null +++ b/samples/browseable/Basic/res/drawable-hdpi/ic_action_location.png diff --git a/samples/browseable/Basic/res/drawable-hdpi/ic_action_refresh.png b/samples/browseable/Basic/res/drawable-hdpi/ic_action_refresh.png Binary files differnew file mode 100644 index 000000000..c9d295d6b --- /dev/null +++ b/samples/browseable/Basic/res/drawable-hdpi/ic_action_refresh.png diff --git a/samples/browseable/Basic/res/drawable-hdpi/ic_action_settings.png b/samples/browseable/Basic/res/drawable-hdpi/ic_action_settings.png Binary files differnew file mode 100644 index 000000000..d3f981d09 --- /dev/null +++ b/samples/browseable/Basic/res/drawable-hdpi/ic_action_settings.png diff --git a/samples/browseable/Basic/res/drawable-hdpi/ic_launcher.png b/samples/browseable/Basic/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..5bb19fbe8 --- /dev/null +++ b/samples/browseable/Basic/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/Basic/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/Basic/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/Basic/res/drawable-mdpi/ic_action_location.png b/samples/browseable/Basic/res/drawable-mdpi/ic_action_location.png Binary files differnew file mode 100644 index 000000000..eaf97745c --- /dev/null +++ b/samples/browseable/Basic/res/drawable-mdpi/ic_action_location.png diff --git a/samples/browseable/Basic/res/drawable-mdpi/ic_action_refresh.png b/samples/browseable/Basic/res/drawable-mdpi/ic_action_refresh.png Binary files differnew file mode 100644 index 000000000..eef97e9ec --- /dev/null +++ b/samples/browseable/Basic/res/drawable-mdpi/ic_action_refresh.png diff --git a/samples/browseable/Basic/res/drawable-mdpi/ic_action_settings.png b/samples/browseable/Basic/res/drawable-mdpi/ic_action_settings.png Binary files differnew file mode 100644 index 000000000..fc2bf8c39 --- /dev/null +++ b/samples/browseable/Basic/res/drawable-mdpi/ic_action_settings.png diff --git a/samples/browseable/Basic/res/drawable-mdpi/ic_launcher.png b/samples/browseable/Basic/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..5737b3631 --- /dev/null +++ b/samples/browseable/Basic/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/Basic/res/drawable-xhdpi/ic_action_location.png b/samples/browseable/Basic/res/drawable-xhdpi/ic_action_location.png Binary files differnew file mode 100644 index 000000000..5f11cce1b --- /dev/null +++ b/samples/browseable/Basic/res/drawable-xhdpi/ic_action_location.png diff --git a/samples/browseable/Basic/res/drawable-xhdpi/ic_action_refresh.png b/samples/browseable/Basic/res/drawable-xhdpi/ic_action_refresh.png Binary files differnew file mode 100644 index 000000000..1027c9a47 --- /dev/null +++ b/samples/browseable/Basic/res/drawable-xhdpi/ic_action_refresh.png diff --git a/samples/browseable/Basic/res/drawable-xhdpi/ic_action_settings.png b/samples/browseable/Basic/res/drawable-xhdpi/ic_action_settings.png Binary files differnew file mode 100644 index 000000000..1b9acf26d --- /dev/null +++ b/samples/browseable/Basic/res/drawable-xhdpi/ic_action_settings.png diff --git a/samples/browseable/Basic/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/Basic/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..31df0432a --- /dev/null +++ b/samples/browseable/Basic/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/Basic/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/Basic/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..508743576 --- /dev/null +++ b/samples/browseable/Basic/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/Basic/res/layout/activity_main.xml b/samples/browseable/Basic/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/Basic/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/Basic/res/layout/sample_main.xml b/samples/browseable/Basic/res/layout/sample_main.xml new file mode 100644 index 000000000..ff589e127 --- /dev/null +++ b/samples/browseable/Basic/res/layout/sample_main.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="16dp" + android:text="@string/intro_message" + android:gravity="center" />
\ No newline at end of file diff --git a/samples/browseable/Basic/res/menu/main.xml b/samples/browseable/Basic/res/menu/main.xml new file mode 100644 index 000000000..a4dc5d146 --- /dev/null +++ b/samples/browseable/Basic/res/menu/main.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> + +<!-- + As we're using ActionBarCompat, any action item attributes come from ActionBarCompat's XML + namespace instead of the android namespace. Here we've added a new support namespace added to + the menu element allowing us to use the 'showAsAction' attribute in a backwards compatible way. + Any other action item attributes used should be referenced from this namespace too + (actionProviderClass, actionViewClass, actionLayout). +--> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:support="http://schemas.android.com/apk/res-auto" > + + <!-- + Here we create an item, setting support:showAsAction to display the item as an action if + there's room on the compatible Action Bar. + --> + <item + android:id="@+id/menu_refresh" + android:icon="@drawable/ic_action_refresh" + android:title="@string/menu_refresh" + support:showAsAction="ifRoom"/> + + <!-- Location item is added in onCreateOptionsMenu() --> + + <!-- + Here we set the settings item to always be in the overflow menu, by setting + support:showAsAction to never, so it is never displayed as an action item on the compatible + Action Bar. + --> + <item + android:id="@+id/menu_settings" + android:icon="@drawable/ic_action_settings" + android:title="@string/menu_settings" + support:showAsAction="never"/> + +</menu>
\ No newline at end of file diff --git a/samples/browseable/Basic/res/values-sw600dp/dimens.xml b/samples/browseable/Basic/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/Basic/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/Basic/res/values-sw600dp/styles.xml b/samples/browseable/Basic/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/Basic/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/Basic/res/values/base-strings.xml b/samples/browseable/Basic/res/values/base-strings.xml new file mode 100644 index 000000000..ff084ef86 --- /dev/null +++ b/samples/browseable/Basic/res/values/base-strings.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">Basic</string> + <string name="intro_message"> + <![CDATA[ + + + This sample shows you how to use ActionBarCompat to create a basic Activity which + displays action items. It covers inflating items from a menu resource, as well as adding + an item in code. Items that are not shown as action items on the Action Bar are + displayed in the action bar overflow. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/Basic/res/values/dimens.xml b/samples/browseable/Basic/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/Basic/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/Basic/res/values/ids.xml b/samples/browseable/Basic/res/values/ids.xml new file mode 100644 index 000000000..026981516 --- /dev/null +++ b/samples/browseable/Basic/res/values/ids.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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> + + <!-- + Generate an id which can be used when the location menu item is added in MainActivity + --> + <item name="menu_location" type="id"/> + +</resources>
\ No newline at end of file diff --git a/samples/browseable/Basic/res/values/strings.xml b/samples/browseable/Basic/res/values/strings.xml new file mode 100644 index 000000000..78674106e --- /dev/null +++ b/samples/browseable/Basic/res/values/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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="menu_refresh">Refresh</string> + <string name="menu_location">Location</string> + <string name="menu_settings">Settings</string> +</resources>
\ No newline at end of file diff --git a/samples/browseable/Basic/res/values/styles.xml b/samples/browseable/Basic/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/Basic/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/Basic/src/com.example.android.actionbarcompat.basic/MainActivity.java b/samples/browseable/Basic/src/com.example.android.actionbarcompat.basic/MainActivity.java new file mode 100644 index 000000000..8d3506fd4 --- /dev/null +++ b/samples/browseable/Basic/src/com.example.android.actionbarcompat.basic/MainActivity.java @@ -0,0 +1,87 @@ +/* + * 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.actionbarcompat.basic; + +import android.os.Bundle; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.app.ActionBarActivity; +import android.view.Menu; +import android.view.MenuItem; + +/** + * This sample shows you how to use ActionBarCompat to create a basic Activity which displays + * action items. It covers inflating items from a menu resource, as well as adding an item in code. + * + * This Activity extends from {@link ActionBarActivity}, which provides all of the function + * necessary to display a compatible Action Bar on devices running Android v2.1+. + */ +public class MainActivity extends ActionBarActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.sample_main); + } + + // BEGIN_INCLUDE(create_menu) + /** + * Use this method to instantiate your menu, and add your items to it. You + * should return true if you have added items to it and want the menu to be displayed. + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate our menu from the resources by using the menu inflater. + getMenuInflater().inflate(R.menu.main, menu); + + // It is also possible add items here. Use a generated id from + // resources (ids.xml) to ensure that all menu ids are distinct. + MenuItem locationItem = menu.add(0, R.id.menu_location, 0, R.string.menu_location); + locationItem.setIcon(R.drawable.ic_action_location); + + // Need to use MenuItemCompat methods to call any action item related methods + MenuItemCompat.setShowAsAction(locationItem, MenuItem.SHOW_AS_ACTION_IF_ROOM); + + return true; + } + // END_INCLUDE(create_menu) + + // BEGIN_INCLUDE(menu_item_selected) + /** + * This method is called when one of the menu items to selected. These items + * can be on the Action Bar, the overflow menu, or the standard options menu. You + * should return true if you handle the selection. + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_refresh: + // Here we might start a background refresh task + return true; + + case R.id.menu_location: + // Here we might call LocationManager.requestLocationUpdates() + return true; + + case R.id.menu_settings: + // Here we would open up our settings activity + return true; + } + + return super.onOptionsItemSelected(item); + } + // END_INCLUDE(menu_item_selected) +} diff --git a/samples/browseable/BasicAccessibility/AndroidManifest.xml b/samples/browseable/BasicAccessibility/AndroidManifest.xml new file mode 100644 index 000000000..d61d7899e --- /dev/null +++ b/samples/browseable/BasicAccessibility/AndroidManifest.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.basicaccessibility" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk + android:minSdkVersion="11" + android:targetSdkVersion="16" /> + + <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" > + <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/browseable/BasicAccessibility/_index.jd b/samples/browseable/BasicAccessibility/_index.jd new file mode 100644 index 000000000..b1c6dde0e --- /dev/null +++ b/samples/browseable/BasicAccessibility/_index.jd @@ -0,0 +1,12 @@ + + + +page.tags="BasicAccessibility" +sample.group=UI +@jd:body + +<p> + + This sample demonstrates how to create an accessible application, using a mix of different widgets demonstrating different ways of adding accessibility markup to a UI. + + </p> diff --git a/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_action_discard.png b/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_action_discard.png Binary files differnew file mode 100644 index 000000000..ece5ad8d6 --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_action_discard.png diff --git a/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_action_info.png b/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_action_info.png Binary files differnew file mode 100644 index 000000000..da65dea1d --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_action_info.png diff --git a/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6c0b5ee4e --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/BasicAccessibility/res/drawable-hdpi/partly_cloudy.png b/samples/browseable/BasicAccessibility/res/drawable-hdpi/partly_cloudy.png Binary files differnew file mode 100644 index 000000000..b1b380c96 --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/drawable-hdpi/partly_cloudy.png diff --git a/samples/browseable/BasicAccessibility/res/drawable-hdpi/tile.9.png b/samples/browseable/BasicAccessibility/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_action_discard.png b/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_action_discard.png Binary files differnew file mode 100644 index 000000000..93483b6cb --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_action_discard.png diff --git a/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_action_info.png b/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_action_info.png Binary files differnew file mode 100644 index 000000000..7f7e0a3dc --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_action_info.png diff --git a/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..4ce0b8226 --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_action_discard.png b/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_action_discard.png Binary files differnew file mode 100644 index 000000000..94f7c8c1c --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_action_discard.png diff --git a/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_action_info.png b/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_action_info.png Binary files differnew file mode 100644 index 000000000..4ede9ce3f --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_action_info.png diff --git a/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6ded70731 --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/BasicAccessibility/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicAccessibility/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..74ae891e3 --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/BasicAccessibility/res/layout/activity_main.xml b/samples/browseable/BasicAccessibility/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/BasicAccessibility/res/layout/sample_main.xml b/samples/browseable/BasicAccessibility/res/layout/sample_main.xml new file mode 100644 index 000000000..64f4f38ae --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/layout/sample_main.xml @@ -0,0 +1,206 @@ +<!-- +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:gravity="center_horizontal"> + <ScrollView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fillViewport="false"> + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + tools:context=".MainActivity" + > + + <!-- Notice the presence of nextFocusDown/nextFocusUp on the elements below. You can + also use nextFocusLeft/nextFocusRight. This tells the system in what order elements + should be navigated through. If not present, the system will make a guess based on + element location in the layout. --> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Buttons" + android:id="@+id/buttonsLabel" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:nextFocusDown="@+id/composeButton"/> + + <!-- This is a regular, text-based button. No contentDescription is needed, since the + text field sufficiently describes the action performed. --> + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/composeButtonLabel" + android:id="@+id/composeButton" + android:layout_below="@+id/buttonsLabel" + android:layout_alignLeft="@+id/buttonsLabel" + android:nextFocusUp="@+id/buttonsLabel" + android:nextFocusDown="@+id/checkboxesLabel" + /> + + <!-- The next two buttons are different types of image-based buttons. --> + + <!-- BEGIN_INCLUDE (image_content_description) --> + <!-- Adding a contentDescription is needed for accessibility, since no text is present. + Since the contentDescription is read verbatim, you may want to be a bit more + descriptive than usual, such as adding "button" to the end of your description, if + appropriate. --> + <ImageButton + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/discardButton" + android:layout_alignTop="@+id/composeButton" + android:layout_toRightOf="@+id/composeButton" + android:src="@drawable/ic_action_discard" + android:layout_alignBottom="@+id/composeButton" + android:contentDescription="@string/discardButtonDescription" + android:scaleType="fitCenter" + android:nextFocusUp="@+id/buttonsLabel" + android:nextFocusDown="@+id/checkboxesLabel" + /> + <!-- END_INCLUDE (image_content_description) --> + + <ImageButton + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/infoButton" + android:layout_alignTop="@+id/discardButton" + android:layout_toRightOf="@+id/discardButton" + android:src="@drawable/ic_action_info" + android:layout_alignBottom="@+id/discardButton" + android:layout_alignRight="@+id/hyperspaceCheckbox" + android:scaleType="fitCenter" + android:background="?android:selectableItemBackground" + android:padding="5dp" + android:contentDescription="@string/infoButtonDescription" + android:nextFocusUp="@+id/buttonsLabel" + android:nextFocusDown="@+id/checkboxesLabel" + /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/checkboxesLabel" + android:id="@+id/checkboxesLabel" + android:layout_below="@+id/composeButton" + android:layout_alignLeft="@+id/composeButton" + android:nextFocusUp="@+id/composeButton" + android:nextFocusDown="@+id/jetpackCheckbox" + /> + + <!-- Like a text-based button, checkboxes with text will often work correctly as-is. + If your checkboxes do not have a text attribute, you will need to add a + contentDescriptoin. --> + <CheckBox + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/jetpackCheckboxLabel" + android:id="@+id/jetpackCheckbox" + android:layout_below="@+id/checkboxesLabel" + android:layout_alignLeft="@+id/checkboxesLabel" + android:checked="false" + android:nextFocusUp="@+id/checkboxesLabel" + android:nextFocusDown="@+id/hyperspaceCheckbox" + /> + + <CheckBox + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/hyperspaceCheckboxLabel" + android:id="@+id/hyperspaceCheckbox" + android:layout_below="@+id/jetpackCheckbox" + android:layout_alignLeft="@+id/jetpackCheckbox" + android:checked="false" + android:nextFocusUp="@+id/jetpackCheckbox" + android:nextFocusDown="@+id/imagesAndTextLabel" + /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/imagesAndTextLabel" + android:id="@+id/imagesAndTextLabel" + android:layout_below="@+id/hyperspaceCheckbox" + android:layout_alignLeft="@+id/hyperspaceCheckbox" + android:nextFocusUp="@+id/hyperspaceCheckbox" + android:nextFocusDown="@+id/partlyCloudImage" + /> + + <!-- Images should have a contentDescription if they convey any meaningful + information. Images that are purely decorative may not need a contentDescription, + however. --> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/partlyCloudyImage" + android:layout_below="@+id/imagesAndTextLabel" + android:layout_alignLeft="@+id/imagesAndTextLabel" + android:src="@drawable/partly_cloudy" + android:contentDescription="@string/partlyCloudyDescription" + android:layout_alignRight="@+id/discardButton" + android:nextFocusUp="@+id/imagesAndTextLabel" + android:nextFocusDown="@+id/customViewLabel" + /> + + <!-- TextViews are typically self describing, so do not need extra modifications. --> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceLarge" + android:text="@string/temperature" + android:textSize="60sp" + android:id="@+id/temperatureText" + android:layout_alignTop="@+id/partlyCloudyImage" + android:layout_toRightOf="@+id/partlyCloudyImage" + android:layout_alignBottom="@+id/partlyCloudyImage" + android:gravity="center_vertical" + android:nextFocusUp="@+id/imagesAndTextLabel" + android:nextFocusDown="@+id/customViewLabel" + /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/customViewLabel" + android:id="@+id/customViewLabel" + android:layout_below="@+id/partlyCloudyImage" + android:layout_alignLeft="@+id/partlyCloudyImage" + android:nextFocusUp="@+id/partlyCloudImage" + android:nextFocusDown="@+id/dialView" + /> + + <!-- Custom views require additonal code changes. See DialView.java for more + details. --> + <com.example.android.basicaccessibility.DialView + android:layout_width="200dp" + android:layout_height="200dp" + android:id="@+id/dialView" + android:layout_below="@+id/customViewLabel" + android:layout_alignLeft="@+id/partlyCloudyImage" + android:nextFocusUp="@+id/customViewLabel" + /> + + </RelativeLayout> + </ScrollView> +</LinearLayout> diff --git a/samples/browseable/BasicAccessibility/res/values-sw600dp/dimens.xml b/samples/browseable/BasicAccessibility/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicAccessibility/res/values-sw600dp/styles.xml b/samples/browseable/BasicAccessibility/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/BasicAccessibility/res/values-sw720dp-land/dimens.xml b/samples/browseable/BasicAccessibility/res/values-sw720dp-land/dimens.xml new file mode 100644 index 000000000..2c2d43124 --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/values-sw720dp-land/dimens.xml @@ -0,0 +1,21 @@ +<!-- +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> + <!-- Customize dimensions originally defined in res/values/dimens.xml (such as + screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here. --> + <dimen name="activity_horizontal_margin">128dp</dimen> +</resources> diff --git a/samples/browseable/BasicAccessibility/res/values/base-strings.xml b/samples/browseable/BasicAccessibility/res/values/base-strings.xml new file mode 100644 index 000000000..20790c75d --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/values/base-strings.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">BasicAccessibility</string> + <string name="intro_message"> + <![CDATA[ + + + This sample demonstrates how to create an accessible application, using a mix of different widgets demonstrating different ways of adding accessibility markup to a UI. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/BasicAccessibility/res/values/dimens.xml b/samples/browseable/BasicAccessibility/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicAccessibility/res/values/strings.xml b/samples/browseable/BasicAccessibility/res/values/strings.xml new file mode 100644 index 000000000..834068297 --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/values/strings.xml @@ -0,0 +1,32 @@ +<?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="composeButtonPressed">(Compose button pressed.)</string> + <string name="discardButtonPressed">(Discard button pressed.)</string> + <string name="infoButtonPressed">(Info button pressed.)</string> + <string name="composeButtonLabel">Compose</string> + <string name="discardButtonDescription">Discard Button</string> + <string name="infoButtonDescription">Info Button</string> + <string name="partlyCloudyDescription">Partly Cloudy</string> + <string name="checkboxesLabel">Checkboxes</string> + <string name="jetpackCheckboxLabel">Enable Jetpack</string> + <string name="hyperspaceCheckboxLabel">Enable Hyperspace Engines</string> + <string name="imagesAndTextLabel">Images & Text</string> + <string name="temperature">53 °F</string> + <string name="customViewLabel">Custom View</string> +</resources> diff --git a/samples/browseable/BasicAccessibility/res/values/styles.xml b/samples/browseable/BasicAccessibility/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/BasicAccessibility/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/BasicAccessibility/src/com.example.android.basicaccessibility/DialView.java b/samples/browseable/BasicAccessibility/src/com.example.android.basicaccessibility/DialView.java new file mode 100644 index 000000000..efdb44985 --- /dev/null +++ b/samples/browseable/BasicAccessibility/src/com.example.android.basicaccessibility/DialView.java @@ -0,0 +1,237 @@ +/* + * 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.basicaccessibility; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.os.Build; +import android.util.AttributeSet; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; + +/** + * Custom view to demonstrate accessibility. + * + * <p>This view does not use any framework widgets, so does not get any accessibility features + * automatically. Instead, we use {@link android.view.accessibility.AccessibilityEvent} to provide accessibility hints to + * the OS. + * + * <p>For example, if TalkBack is enabled, users will be able to receive spoken feedback as they + * interact with this view. + * + * <p>More generally, this view renders a multi-position "dial" that can be used to select a value + * between 1 and 4. Each time the dial is clicked, the next position will be selected (modulo + * the maximum number of positions). + */ +public class DialView extends View { + private static int SELECTION_COUNT = 4; + + private static float FONT_SIZE = 40f; + private float mWidth; + private float mHeight; + private float mWidthPadded; + private float mHeightPadded; + private Paint mTextPaint; + private Paint mDialPaint; + private float mRadius; + private int mActiveSelection; + + /** + * Constructor that is called when inflating a view from XML. This is called + * when a view is being constructed from an XML file, supplying attributes + * that were specified in the XML file. + * + * <p>In our case, this constructor just calls init(). + * + * @param context The Context the view is running in, through which it can + * access the current theme, resources, etc. + * @param attrs The attributes of the XML tag that is inflating the view. + * @see #View(android.content.Context, android.util.AttributeSet, int) + */ + public DialView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + /** + * Helper method to initialize instance variables. Called by constructor. + */ + private void init() { + // Paint styles used for rendering are created here, rather than at render-time. This + // is a performance optimization, since onDraw() will get called frequently. + mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mTextPaint.setColor(Color.BLACK); + mTextPaint.setStyle(Paint.Style.FILL_AND_STROKE); + mTextPaint.setTextAlign(Paint.Align.CENTER); + mTextPaint.setTextSize(FONT_SIZE); + + mDialPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mDialPaint.setColor(Color.GRAY); + + // Initialize current selection. This will store where the dial's "indicator" is pointing. + mActiveSelection = 0; + + // Setup onClick listener for this view. Rotates between each of the different selection + // states on each click. + // + // Notice that we call sendAccessibilityEvent here. Some AccessibilityEvents are generated + // by the system. However, custom views will typically need to send events manually as the + // user interacts with the view. The type of event sent will vary, depending on the nature + // of the view and how the user interacts with it. + // + // In this case, we are sending TYPE_VIEW_SELECTED rather than TYPE_VIEW_CLICKED, because + // clicking on this view selects a new value. + // + // We will give our AccessibilityEvent further information about the state of the view in + // onPopulateAccessibilityEvent(), which will be called automatically by the system + // for each AccessibilityEvent. + setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + // Rotate selection to the next valid choice. + mActiveSelection = (mActiveSelection + 1) % SELECTION_COUNT; + // Send an AccessibilityEvent, since the user has interacted with the view. + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); + // Redraw the entire view. (Inefficient, but this is sufficient for demonstration + // purposes.) + invalidate(); + } + }); + } + + /** + * This is where a View should populate outgoing accessibility events with its text content. + * While this method is free to modify event attributes other than text content, doing so + * should normally be performed in + * {@link #onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)}. + * <p/> + * <p>Note that the behavior of this method will typically vary, depending on the type of + * accessibility event is passed into it. The allowed values also very, and are documented + * in {@link android.view.accessibility.AccessibilityEvent}. + * <p/> + * <p>Typically, this is where you'll describe the state of your custom view. You may also + * want to provide custom directions when the user has focused your view. + * + * @param event The accessibility event which to populate. + */ + // BEGIN_INCLUDE (on_populate_accessibility_event) + @Override + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) + public void onPopulateAccessibilityEvent(AccessibilityEvent event) { + super.onPopulateAccessibilityEvent(event); + + // Detect what type of accessibility event is being passed in. + int eventType = event.getEventType(); + + // Common case: The user has interacted with our view in some way. State may or may not + // have been changed. Read out the current status of the view. + // + // We also set some other metadata which is not used by TalkBack, but could be used by + // other TTS engines. + if (eventType == AccessibilityEvent.TYPE_VIEW_SELECTED || + eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) { + event.getText().add("Mode selected: " + Integer.toString(mActiveSelection + 1) + "."); + event.setItemCount(SELECTION_COUNT); + event.setCurrentItemIndex(mActiveSelection); + } + + // When a user first focuses on our view, we'll also read out some simple instructions to + // make it clear that this is an interactive element. + if (eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) { + event.getText().add("Tap to change."); + } + } + // END_INCLUDE (on_populate_accessibility_event) + + /** + * This is called during layout when the size of this view has changed. If + * you were just added to the view hierarchy, you're called with the old + * values of 0. + * + * <p>This is where we determine the drawing bounds for our custom view. + * + * @param w Current width of this view. + * @param h Current height of this view. + * @param oldw Old width of this view. + * @param oldh Old height of this view. + */ + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + // Account for padding + float xPadding = (float) (getPaddingLeft() + getPaddingRight()); + float yPadding = (float) (getPaddingTop() + getPaddingBottom()); + + // Compute available width/height + mWidth = w; + mHeight = h; + mWidthPadded = w - xPadding; + mHeightPadded = h - yPadding; + mRadius = (float) (Math.min(mWidth, mHeight) / 2 * 0.8); + } + + /** + * Render view content. + * + * <p>We render an outer grey circle to serve as our "dial", and then render a smaller black + * circle to server as our indicator. The position for the indicator is determined based + * on mActiveSelection. + * + * @param canvas the canvas on which the background will be drawn + */ + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + // Draw dial + canvas.drawCircle(mWidth / 2, mHeight / 2, (float) mRadius, mDialPaint); + + // Draw text labels + final float labelRadius = mRadius + 10; + for (int i = 0; i < SELECTION_COUNT; i++) { + float[] xyData = computeXYForPosition(i, labelRadius); + float x = xyData[0]; + float y = xyData[1]; + canvas.drawText(Integer.toString(i + 1), x, y, mTextPaint); + } + + // Draw indicator mark + final float markerRadius = mRadius - 35; + float[] xyData = computeXYForPosition(mActiveSelection, markerRadius); + float x = xyData[0]; + float y = xyData[1]; + canvas.drawCircle(x, y, 20, mTextPaint); + } + + /** + * Compute the X/Y-coordinates for a label or indicator, given the position number and radius + * where the label should be drawn. + * + * @param pos Zero based position index + * @param radius Radius where label/indicator is to be drawn. + * @return 2-element array. Element 0 is X-coordinate, element 1 is Y-coordinate. + */ + private float[] computeXYForPosition(final int pos, final float radius) { + float[] result = new float[2]; + Double startAngle = Math.PI * (9 / 8d); // Angles are in radiansq + Double angle = startAngle + (pos * (Math.PI / 4)); + result[0] = (float) (radius * Math.cos(angle)) + (mWidth / 2); + result[1] = (float) (radius * Math.sin(angle)) + (mHeight / 2); + return result; + } +} diff --git a/samples/browseable/BasicAccessibility/src/com.example.android.basicaccessibility/MainActivity.java b/samples/browseable/BasicAccessibility/src/com.example.android.basicaccessibility/MainActivity.java new file mode 100644 index 000000000..2777ad772 --- /dev/null +++ b/samples/browseable/BasicAccessibility/src/com.example.android.basicaccessibility/MainActivity.java @@ -0,0 +1,39 @@ +/* + * 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.basicaccessibility; + +import android.app.Activity; +import android.os.Bundle; + +/** + * Basic activity class. + * + * <p>Responsible for rendering layout, and displaying some toasts to give buttons feedback. + * There's nothing terribly interesting in this class. All the interesting stuff is in + * res/layout/activity_main.xml and {@link DialView}. + */ +public class MainActivity extends Activity { + + /** + * Standard onCreate() implementation. Sets R.layout.activity_main as the layout. + */ + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.sample_main); + } +} diff --git a/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/Log.java b/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/LogFragment.java b/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/LogNode.java b/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/LogView.java b/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/LogWrapper.java b/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/MessageOnlyLogFilter.java b/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/BasicAccessibility/src/com.example.android.common.logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/BasicAndroidKeyStore/AndroidManifest.xml b/samples/browseable/BasicAndroidKeyStore/AndroidManifest.xml new file mode 100644 index 000000000..28d256c2d --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/AndroidManifest.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.basicandroidkeystore" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk android:minSdkVersion="18" android:targetSdkVersion="19" /> + + <application android:allowBackup="true" + android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@style/AppTheme"> + + <activity android:name=".MainActivity" + android:label="@string/app_name" + android:uiOptions="splitActionBarWhenNarrow"> + <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/browseable/BasicAndroidKeyStore/_index.jd b/samples/browseable/BasicAndroidKeyStore/_index.jd new file mode 100644 index 000000000..b69787e1d --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/_index.jd @@ -0,0 +1,17 @@ + + + +page.tags="BasicAndroidKeyStore" +sample.group=Security +@jd:body + +<p> + + Welcome to the <b>Basic Android Key Store</b> sample!\n\n + This sample demonstrates how to use the Android Key Store to safely create and store + encryption keys that only your application can access. You can also sign data + using those keys.\n\n + To create a new KeyPair, click \"Create\".\n\n + To sign some data using a KeyPair, click \"Sign\".\n\n + To verify the data using the signature provided, click \"Verify\".\n\n + </p> diff --git a/samples/browseable/BasicAndroidKeyStore/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicAndroidKeyStore/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..b1efaf4b2 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/BasicAndroidKeyStore/res/drawable-hdpi/tile.9.png b/samples/browseable/BasicAndroidKeyStore/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/BasicAndroidKeyStore/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicAndroidKeyStore/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f5f9244f2 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/BasicAndroidKeyStore/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicAndroidKeyStore/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..5d07b3f06 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/BasicAndroidKeyStore/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicAndroidKeyStore/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6ef21e1f4 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/BasicAndroidKeyStore/res/layout/activity_main.xml b/samples/browseable/BasicAndroidKeyStore/res/layout/activity_main.xml new file mode 100644 index 000000000..bc5a57591 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/layout/activity_main.xml @@ -0,0 +1,38 @@ +<!-- + 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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:id="@+id/sample_main_layout"> + <TextView android:id="@+id/sample_output" + style="@style/Widget.SampleMessage" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:text="@string/intro_message" /> + <View + android:layout_width="fill_parent" + android:layout_height="1dp" + android:background="@android:color/darker_gray"/> + <fragment + android:name="com.example.android.common.logger.LogFragment" + android:id="@+id/log_fragment" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" /> +</LinearLayout> diff --git a/samples/browseable/BasicAndroidKeyStore/res/menu/main.xml b/samples/browseable/BasicAndroidKeyStore/res/menu/main.xml new file mode 100644 index 000000000..74435ca8f --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/menu/main.xml @@ -0,0 +1,30 @@ +<!-- + 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. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/btn_create_keys" + android:showAsAction="always" + android:title="@string/str_create_keys" /> + + <item android:id="@+id/btn_sign_data" + android:showAsAction="always" + android:title="@string/str_sign_data" /> + + <item android:id="@+id/btn_verify_data" + android:showAsAction="always" + android:title="@string/str_verify_data" /> + +</menu> diff --git a/samples/browseable/BasicAndroidKeyStore/res/values-sw600dp/dimens.xml b/samples/browseable/BasicAndroidKeyStore/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicAndroidKeyStore/res/values-sw600dp/styles.xml b/samples/browseable/BasicAndroidKeyStore/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/BasicAndroidKeyStore/res/values/base-strings.xml b/samples/browseable/BasicAndroidKeyStore/res/values/base-strings.xml new file mode 100644 index 000000000..0699a4aee --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/values/base-strings.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">BasicAndroidKeyStore</string> + <string name="intro_message"> + <![CDATA[ + + + Welcome to the <b>Basic Android Key Store</b> sample!\n\n + This sample demonstrates how to use the Android Key Store to safely create and store + encryption keys that only your application can access. You can also sign data + using those keys.\n\n + To create a new KeyPair, click \"Create\".\n\n + To sign some data using a KeyPair, click \"Sign\".\n\n + To verify the data using the signature provided, click \"Verify\".\n\n + + ]]> + </string> +</resources> diff --git a/samples/browseable/BasicAndroidKeyStore/res/values/dimens.xml b/samples/browseable/BasicAndroidKeyStore/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicAndroidKeyStore/res/values/strings.xml b/samples/browseable/BasicAndroidKeyStore/res/values/strings.xml new file mode 100644 index 000000000..25ad389ac --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/values/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="str_create_keys">Create</string> + <string name="str_sign_data">Sign</string> + <string name="str_verify_data">Verify</string> +</resources> diff --git a/samples/browseable/BasicAndroidKeyStore/res/values/styles.xml b/samples/browseable/BasicAndroidKeyStore/res/values/styles.xml new file mode 100644 index 000000000..d3f82ff64 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/values/styles.xml @@ -0,0 +1,51 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="AppTheme" parent="Theme.Base" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + + + <style name="Widget.SampleOutput"> + <item name="android:padding">@dimen/margin_medium</item> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Log" parent="Widget.SampleOutput"> + <item name="android:typeface">monospace</item> + </style> + +</resources> diff --git a/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/BasicAndroidKeyStoreFragment.java b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/BasicAndroidKeyStoreFragment.java new file mode 100644 index 000000000..12873e847 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/BasicAndroidKeyStoreFragment.java @@ -0,0 +1,327 @@ +/* +* 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.basicandroidkeystore; + +import android.content.Context; +import android.os.Bundle; +import android.security.KeyPairGeneratorSpec; +import android.support.v4.app.Fragment; +import android.util.Base64; +import android.view.MenuItem; + +import com.example.android.common.logger.Log; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.UnrecoverableEntryException; +import java.security.cert.CertificateException; +import java.util.Calendar; +import java.util.GregorianCalendar; + +import javax.security.auth.x500.X500Principal; + +public class BasicAndroidKeyStoreFragment extends Fragment { + + public static final String TAG = "BasicAndroidKeyStoreFragment"; + + // BEGIN_INCLUDE(values) + + public static final String SAMPLE_ALIAS = "myKey"; + + // Some sample data to sign, and later verify using the generated signature. + public static final String SAMPLE_INPUT="Hello, Android!"; + + // Just a handy place to store the signature in between signing and verifying. + public String mSignatureStr = null; + + // You can store multiple key pairs in the Key Store. The string used to refer to the Key you + // want to store, or later pull, is referred to as an "alias" in this case, because calling it + // a key, when you use it to retrieve a key, would just be irritating. + private String mAlias = null; + + // END_INCLUDE(values) + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + setAlias(SAMPLE_ALIAS); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.btn_create_keys: + try { + createKeys(getActivity()); + Log.d(TAG, "Keys created"); + return true; + } catch (NoSuchAlgorithmException e) { + Log.w(TAG, "RSA not supported", e); + } catch (InvalidAlgorithmParameterException e) { + Log.w(TAG, "No such provider: AndroidKeyStore"); + } catch (NoSuchProviderException e) { + Log.w(TAG, "Invalid Algorithm Parameter Exception", e); + } + return true; + case R.id.btn_sign_data: + try { + mSignatureStr = signData(SAMPLE_INPUT); + } catch (KeyStoreException e) { + Log.w(TAG, "KeyStore not Initialized", e); + } catch (UnrecoverableEntryException e) { + Log.w(TAG, "KeyPair not recovered", e); + } catch (NoSuchAlgorithmException e) { + Log.w(TAG, "RSA not supported", e); + } catch (InvalidKeyException e) { + Log.w(TAG, "Invalid Key", e); + } catch (SignatureException e) { + Log.w(TAG, "Invalid Signature", e); + } catch (IOException e) { + Log.w(TAG, "IO Exception", e); + } catch (CertificateException e) { + Log.w(TAG, "Error occurred while loading certificates", e); + } + Log.d(TAG, "Signature: " + mSignatureStr); + return true; + + case R.id.btn_verify_data: + boolean verified = false; + try { + if (mSignatureStr != null) { + verified = verifyData(SAMPLE_INPUT, mSignatureStr); + } + } catch (KeyStoreException e) { + Log.w(TAG, "KeyStore not Initialized", e); + } catch (CertificateException e) { + Log.w(TAG, "Error occurred while loading certificates", e); + } catch (NoSuchAlgorithmException e) { + Log.w(TAG, "RSA not supported", e); + } catch (IOException e) { + Log.w(TAG, "IO Exception", e); + } catch (UnrecoverableEntryException e) { + Log.w(TAG, "KeyPair not recovered", e); + } catch (InvalidKeyException e) { + Log.w(TAG, "Invalid Key", e); + } catch (SignatureException e) { + Log.w(TAG, "Invalid Signature", e); + } + if (verified) { + Log.d(TAG, "Data Signature Verified"); + } else { + Log.d(TAG, "Data not verified."); + } + return true; + } + return false; + } + + /** + * Creates a public and private key and stores it using the Android Key Store, so that only + * this application will be able to access the keys. + */ + public void createKeys(Context context) throws NoSuchProviderException, + NoSuchAlgorithmException, InvalidAlgorithmParameterException { + // BEGIN_INCLUDE(create_valid_dates) + // Create a start and end time, for the validity range of the key pair that's about to be + // generated. + Calendar start = new GregorianCalendar(); + Calendar end = new GregorianCalendar(); + end.add(1, Calendar.YEAR); + //END_INCLUDE(create_valid_dates) + + + // BEGIN_INCLUDE(create_spec) + // The KeyPairGeneratorSpec object is how parameters for your key pair are passed + // to the KeyPairGenerator. For a fun home game, count how many classes in this sample + // start with the phrase "KeyPair". + KeyPairGeneratorSpec spec = + new KeyPairGeneratorSpec.Builder(context) + // You'll use the alias later to retrieve the key. It's a key for the key! + .setAlias(mAlias) + // The subject used for the self-signed certificate of the generated pair + .setSubject(new X500Principal("CN=" + mAlias)) + // The serial number used for the self-signed certificate of the + // generated pair. + .setSerialNumber(BigInteger.valueOf(1337)) + // Date range of validity for the generated pair. + .setStartDate(start.getTime()) + .setEndDate(end.getTime()) + .build(); + // END_INCLUDE(create_spec) + + // BEGIN_INCLUDE(create_keypair) + // Initialize a KeyPair generator using the the intended algorithm (in this example, RSA + // and the KeyStore. This example uses the AndroidKeyStore. + KeyPairGenerator kpGenerator = KeyPairGenerator + .getInstance(SecurityConstants.TYPE_RSA, + SecurityConstants.KEYSTORE_PROVIDER_ANDROID_KEYSTORE); + kpGenerator.initialize(spec); + KeyPair kp = kpGenerator.generateKeyPair(); + Log.d(TAG, "Public Key is: " + kp.getPublic().toString()); + // END_INCLUDE(create_keypair) + } + + /** + * Signs the data using the key pair stored in the Android Key Store. This signature can be + * used with the data later to verify it was signed by this application. + * @return A string encoding of the data signature generated + */ + public String signData(String inputStr) throws KeyStoreException, + UnrecoverableEntryException, NoSuchAlgorithmException, InvalidKeyException, + SignatureException, IOException, CertificateException { + byte[] data = inputStr.getBytes(); + + // BEGIN_INCLUDE(sign_load_keystore) + KeyStore ks = KeyStore.getInstance(SecurityConstants.KEYSTORE_PROVIDER_ANDROID_KEYSTORE); + + // Weird artifact of Java API. If you don't have an InputStream to load, you still need + // to call "load", or it'll crash. + ks.load(null); + + // Load the key pair from the Android Key Store + KeyStore.Entry entry = ks.getEntry(mAlias, null); + + /* If the entry is null, keys were never stored under this alias. + * Debug steps in this situation would be: + * -Check the list of aliases by iterating over Keystore.aliases(), be sure the alias + * exists. + * -If that's empty, verify they were both stored and pulled from the same keystore + * "AndroidKeyStore" + */ + if (entry == null) { + Log.w(TAG, "No key found under alias: " + mAlias); + Log.w(TAG, "Exiting signData()..."); + return null; + } + + /* If entry is not a KeyStore.PrivateKeyEntry, it might have gotten stored in a previous + * iteration of your application that was using some other mechanism, or been overwritten + * by something else using the same keystore with the same alias. + * You can determine the type using entry.getClass() and debug from there. + */ + if (!(entry instanceof KeyStore.PrivateKeyEntry)) { + Log.w(TAG, "Not an instance of a PrivateKeyEntry"); + Log.w(TAG, "Exiting signData()..."); + return null; + } + // END_INCLUDE(sign_data) + + // BEGIN_INCLUDE(sign_create_signature) + // This class doesn't actually represent the signature, + // just the engine for creating/verifying signatures, using + // the specified algorithm. + Signature s = Signature.getInstance(SecurityConstants.SIGNATURE_SHA256withRSA); + + // Initialize Signature using specified private key + s.initSign(((KeyStore.PrivateKeyEntry) entry).getPrivateKey()); + + // Sign the data, store the result as a Base64 encoded String. + s.update(data); + byte[] signature = s.sign(); + String result = Base64.encodeToString(signature, Base64.DEFAULT); + // END_INCLUDE(sign_data) + + return result; + } + + /** + * Given some data and a signature, uses the key pair stored in the Android Key Store to verify + * that the data was signed by this application, using that key pair. + * @param input The data to be verified. + * @param signatureStr The signature provided for the data. + * @return A boolean value telling you whether the signature is valid or not. + */ + public boolean verifyData(String input, String signatureStr) throws KeyStoreException, + CertificateException, NoSuchAlgorithmException, IOException, + UnrecoverableEntryException, InvalidKeyException, SignatureException { + byte[] data = input.getBytes(); + byte[] signature; + // BEGIN_INCLUDE(decode_signature) + + // Make sure the signature string exists. If not, bail out, nothing to do. + + if (signatureStr == null) { + Log.w(TAG, "Invalid signature."); + Log.w(TAG, "Exiting verifyData()..."); + return false; + } + + try { + // The signature is going to be examined as a byte array, + // not as a base64 encoded string. + signature = Base64.decode(signatureStr, Base64.DEFAULT); + } catch (IllegalArgumentException e) { + // signatureStr wasn't null, but might not have been encoded properly. + // It's not a valid Base64 string. + return false; + } + // END_INCLUDE(decode_signature) + + KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); + + // Weird artifact of Java API. If you don't have an InputStream to load, you still need + // to call "load", or it'll crash. + ks.load(null); + + // Load the key pair from the Android Key Store + KeyStore.Entry entry = ks.getEntry(mAlias, null); + + if (entry == null) { + Log.w(TAG, "No key found under alias: " + mAlias); + Log.w(TAG, "Exiting verifyData()..."); + return false; + } + + if (!(entry instanceof KeyStore.PrivateKeyEntry)) { + Log.w(TAG, "Not an instance of a PrivateKeyEntry"); + return false; + } + + // This class doesn't actually represent the signature, + // just the engine for creating/verifying signatures, using + // the specified algorithm. + Signature s = Signature.getInstance(SecurityConstants.SIGNATURE_SHA256withRSA); + + // BEGIN_INCLUDE(verify_data) + // Verify the data. + s.initVerify(((KeyStore.PrivateKeyEntry) entry).getCertificate()); + s.update(data); + boolean valid = s.verify(signature); + return valid; + // END_INCLUDE(verify_data) + } + + public void setAlias(String alias) { + mAlias = alias; + } +} diff --git a/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/MainActivity.java b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/MainActivity.java new file mode 100644 index 000000000..9f165655b --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/MainActivity.java @@ -0,0 +1,78 @@ +/* +* 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.basicandroidkeystore; + +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.view.Menu; + +import com.example.android.common.activities.SampleActivityBase; +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogFragment; +import com.example.android.common.logger.LogWrapper; +import com.example.android.common.logger.MessageOnlyLogFilter; + +/** + * A simple launcher activity containing a summary sample description + * and a few action bar buttons. + */ +public class MainActivity extends SampleActivityBase { + + public static final String TAG = "MainActivity"; + + public static final String FRAGTAG = "BasicAndroidKeyStoreFragment"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + BasicAndroidKeyStoreFragment fragment = new BasicAndroidKeyStoreFragment(); + transaction.add(fragment, FRAGTAG); + transaction.commit(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + /** Create a chain of targets that will receive log data */ + @Override + public void initializeLogging() { + // Wraps Android's native log framework. + LogWrapper logWrapper = new LogWrapper(); + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + Log.setLogNode(logWrapper); + + // Filter strips out everything except the message text. + MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); + logWrapper.setNext(msgFilter); + + // On screen logging via a fragment with a TextView. + LogFragment logFragment = (LogFragment) getSupportFragmentManager() + .findFragmentById(R.id.log_fragment); + msgFilter.setNext(logFragment.getLogView()); + + Log.i(TAG, "Ready"); + } +}
\ No newline at end of file diff --git a/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/SecurityConstants.java b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/SecurityConstants.java new file mode 100644 index 000000000..ea5ee30bd --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/SecurityConstants.java @@ -0,0 +1,33 @@ +/* + * 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.basicandroidkeystore; + +/** + * Helper class, contains several constants used when encrypting/decrypting data on Android. + * This class should not be considered a complete list of the algorithms, keystore types, + * or signature types within the Android Platform, only the more common ones. + */ +public class SecurityConstants { + public static final String KEYSTORE_PROVIDER_ANDROID_KEYSTORE = "AndroidKeyStore"; + + public static final String TYPE_RSA = "RSA"; + public static final String TYPE_DSA = "DSA"; + public static final String TYPE_BKS = "BKS"; + + public static final String SIGNATURE_SHA256withRSA = "SHA256withRSA"; + public static final String SIGNATURE_SHA512withRSA = "SHA512withRSA"; +} diff --git a/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/activities/SampleActivityBase.java new file mode 100644 index 000000000..3228927b7 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/activities/SampleActivityBase.java @@ -0,0 +1,52 @@ +/* +* 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.common.activities; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogWrapper; + +/** + * Base launcher activity, to handle most of the common plumbing for samples. + */ +public class SampleActivityBase extends FragmentActivity { + + public static final String TAG = "SampleActivityBase"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + initializeLogging(); + } + + /** Set up targets to receive log data */ + public void initializeLogging() { + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/Log.java b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/LogNode.java b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/LogView.java b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.common/logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/BasicContactables/AndroidManifest.xml b/samples/browseable/BasicContactables/AndroidManifest.xml new file mode 100644 index 000000000..62b9812cc --- /dev/null +++ b/samples/browseable/BasicContactables/AndroidManifest.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.basiccontactables" + android:versionCode="1" + android:versionName="1.0" > + + <!-- BEGIN_INCLUDE(contacts_permission) --> + <uses-permission android:name="android.permission.READ_CONTACTS"/> + <!-- END_INCLUDE(contacts_permission) --> + <uses-sdk + android:minSdkVersion="18" + android:targetSdkVersion="18" /> + <permission android:name="android"></permission> + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/Theme.Sample" > + <activity + android:name="com.example.android.basiccontactables.MainActivity" + android:label="@string/app_name" + android:launchMode="singleTop"> + <meta-data + android:name="android.app.searchable" + android:resource="@xml/searchable" /> + <intent-filter> + <action android:name="android.intent.action.SEARCH" /> + </intent-filter> + <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/browseable/BasicContactables/_index.jd b/samples/browseable/BasicContactables/_index.jd new file mode 100644 index 000000000..a947004a2 --- /dev/null +++ b/samples/browseable/BasicContactables/_index.jd @@ -0,0 +1,14 @@ + + + +page.tags="BasicContactables" +sample.group=Content +@jd:body + +<p> + + This sample demonstrates how to use the Contactables table to search for contacts. + \n\nQuery strings sent to the Contactables table will match both contact names and phone numbers, + reducing the number of queries your application needs to use when searching the contacts database! + + </p> diff --git a/samples/browseable/BasicContactables/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicContactables/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..b1efaf4b2 --- /dev/null +++ b/samples/browseable/BasicContactables/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/BasicContactables/res/drawable-hdpi/ic_search_api_holo_light.png b/samples/browseable/BasicContactables/res/drawable-hdpi/ic_search_api_holo_light.png Binary files differnew file mode 100644 index 000000000..72e207bc5 --- /dev/null +++ b/samples/browseable/BasicContactables/res/drawable-hdpi/ic_search_api_holo_light.png diff --git a/samples/browseable/BasicContactables/res/drawable-hdpi/tile.9.png b/samples/browseable/BasicContactables/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/BasicContactables/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/BasicContactables/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicContactables/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f5f9244f2 --- /dev/null +++ b/samples/browseable/BasicContactables/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/BasicContactables/res/drawable-mdpi/ic_search_api_holo_light.png b/samples/browseable/BasicContactables/res/drawable-mdpi/ic_search_api_holo_light.png Binary files differnew file mode 100644 index 000000000..f2e26f883 --- /dev/null +++ b/samples/browseable/BasicContactables/res/drawable-mdpi/ic_search_api_holo_light.png diff --git a/samples/browseable/BasicContactables/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicContactables/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..5d07b3f06 --- /dev/null +++ b/samples/browseable/BasicContactables/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/BasicContactables/res/drawable-xhdpi/ic_search_api_holo_light.png b/samples/browseable/BasicContactables/res/drawable-xhdpi/ic_search_api_holo_light.png Binary files differnew file mode 100644 index 000000000..a4cdf1c79 --- /dev/null +++ b/samples/browseable/BasicContactables/res/drawable-xhdpi/ic_search_api_holo_light.png diff --git a/samples/browseable/BasicContactables/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicContactables/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6ef21e1f4 --- /dev/null +++ b/samples/browseable/BasicContactables/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/BasicContactables/res/layout/activity_main.xml b/samples/browseable/BasicContactables/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/BasicContactables/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/BasicContactables/res/layout/sample_main.xml b/samples/browseable/BasicContactables/res/layout/sample_main.xml new file mode 100755 index 000000000..52dc3111a --- /dev/null +++ b/samples/browseable/BasicContactables/res/layout/sample_main.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. + --> + +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView android:id="@+id/sample_output" + style="@style/Widget.SampleOutput" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/intro_message" /> + +</ScrollView> diff --git a/samples/browseable/BasicContactables/res/menu/main.xml b/samples/browseable/BasicContactables/res/menu/main.xml new file mode 100644 index 000000000..4a530f1ad --- /dev/null +++ b/samples/browseable/BasicContactables/res/menu/main.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/search" + android:title="@string/search_title" + android:icon="@drawable/ic_search_api_holo_light" + android:showAsAction="collapseActionView|ifRoom" + android:actionViewClass="android.widget.SearchView" /> +</menu> diff --git a/samples/browseable/BasicContactables/res/values-sw600dp/dimens.xml b/samples/browseable/BasicContactables/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/BasicContactables/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicContactables/res/values-sw600dp/styles.xml b/samples/browseable/BasicContactables/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/BasicContactables/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/BasicContactables/res/values/base-strings.xml b/samples/browseable/BasicContactables/res/values/base-strings.xml new file mode 100644 index 000000000..0fc07ef4a --- /dev/null +++ b/samples/browseable/BasicContactables/res/values/base-strings.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">BasicContactables</string> + <string name="intro_message"> + <![CDATA[ + + + This sample demonstrates how to use the Contactables table to search for contacts. + \n\nQuery strings sent to the Contactables table will match both contact names and phone numbers, + reducing the number of queries your application needs to use when searching the contacts database! + + + ]]> + </string> +</resources> diff --git a/samples/browseable/BasicContactables/res/values/dimens.xml b/samples/browseable/BasicContactables/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/BasicContactables/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicContactables/res/values/strings.xml b/samples/browseable/BasicContactables/res/values/strings.xml new file mode 100755 index 000000000..a499fd286 --- /dev/null +++ b/samples/browseable/BasicContactables/res/values/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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="sample_action">Sample action</string> + <string name="search_title">Search Contacts</string> +</resources> diff --git a/samples/browseable/BasicContactables/res/values/styles.xml b/samples/browseable/BasicContactables/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/BasicContactables/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/BasicContactables/res/xml/searchable.xml b/samples/browseable/BasicContactables/res/xml/searchable.xml new file mode 100644 index 000000000..32fe1cc09 --- /dev/null +++ b/samples/browseable/BasicContactables/res/xml/searchable.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. + --> + +<searchable xmlns:android="http://schemas.android.com/apk/res/android" + android:label="@string/app_name" + android:hint="@string/search_title" /> diff --git a/samples/browseable/BasicContactables/src/com.example.android.basiccontactables/ContactablesLoaderCallbacks.java b/samples/browseable/BasicContactables/src/com.example.android.basiccontactables/ContactablesLoaderCallbacks.java new file mode 100644 index 000000000..4fc4da785 --- /dev/null +++ b/samples/browseable/BasicContactables/src/com.example.android.basiccontactables/ContactablesLoaderCallbacks.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2012 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.basiccontactables; + +import android.app.Activity; +import android.app.LoaderManager; +import android.content.Context; +import android.content.CursorLoader; +import android.content.Loader; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.provider.ContactsContract.CommonDataKinds; +import android.util.Log; +import android.widget.TextView; + +/** + * Helper class to handle all the callbacks that occur when interacting with loaders. Most of the + * interesting code in this sample app will be in this file. + */ +public class ContactablesLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> { + + Context mContext; + + public static final String QUERY_KEY = "query"; + + public static final String TAG = "ContactablesLoaderCallbacks"; + + public ContactablesLoaderCallbacks(Context context) { + mContext = context; + } + + @Override + public Loader<Cursor> onCreateLoader(int loaderIndex, Bundle args) { + // Where the Contactables table excels is matching text queries, + // not just data dumps from Contacts db. One search term is used to query + // display name, email address and phone number. In this case, the query was extracted + // from an incoming intent in the handleIntent() method, via the + // intent.getStringExtra() method. + + // BEGIN_INCLUDE(uri_with_query) + String query = args.getString(QUERY_KEY); + Uri uri = Uri.withAppendedPath( + CommonDataKinds.Contactables.CONTENT_FILTER_URI, query); + // END_INCLUDE(uri_with_query) + + + // BEGIN_INCLUDE(cursor_loader) + // Easy way to limit the query to contacts with phone numbers. + String selection = + CommonDataKinds.Contactables.HAS_PHONE_NUMBER + " = " + 1; + + // Sort results such that rows for the same contact stay together. + String sortBy = CommonDataKinds.Contactables.LOOKUP_KEY; + + return new CursorLoader( + mContext, // Context + uri, // URI representing the table/resource to be queried + null, // projection - the list of columns to return. Null means "all" + selection, // selection - Which rows to return (condition rows must match) + null, // selection args - can be provided separately and subbed into selection. + sortBy); // string specifying sort order + // END_INCLUDE(cursor_loader) + } + + @Override + public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) { + TextView tv = (TextView) ((Activity)mContext).findViewById(R.id.sample_output); + if(tv == null) { + Log.e(TAG, "TextView is null?!"); + } else if (mContext == null) { + Log.e(TAG, "Context is null?"); + } else { + Log.e(TAG, "Nothing is null?!"); + } + + // Reset text in case of a previous query + tv.setText(mContext.getText(R.string.intro_message) + "\n\n"); + + if (cursor.getCount() == 0) { + return; + } + + // Pulling the relevant value from the cursor requires knowing the column index to pull + // it from. + // BEGIN_INCLUDE(get_columns) + int phoneColumnIndex = cursor.getColumnIndex(CommonDataKinds.Phone.NUMBER); + int emailColumnIndex = cursor.getColumnIndex(CommonDataKinds.Email.ADDRESS); + int nameColumnIndex = cursor.getColumnIndex(CommonDataKinds.Contactables.DISPLAY_NAME); + int lookupColumnIndex = cursor.getColumnIndex(CommonDataKinds.Contactables.LOOKUP_KEY); + int typeColumnIndex = cursor.getColumnIndex(CommonDataKinds.Contactables.MIMETYPE); + // END_INCLUDE(get_columns) + + cursor.moveToFirst(); + // Lookup key is the easiest way to verify a row of data is for the same + // contact as the previous row. + String lookupKey = ""; + do { + // BEGIN_INCLUDE(lookup_key) + String currentLookupKey = cursor.getString(lookupColumnIndex); + if (!lookupKey.equals(currentLookupKey)) { + String displayName = cursor.getString(nameColumnIndex); + tv.append(displayName + "\n"); + lookupKey = currentLookupKey; + } + // END_INCLUDE(lookup_key) + + // BEGIN_INCLUDE(retrieve_data) + // The data type can be determined using the mime type column. + String mimeType = cursor.getString(typeColumnIndex); + if (mimeType.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) { + tv.append("\tPhone Number: " + cursor.getString(phoneColumnIndex) + "\n"); + } else if (mimeType.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) { + tv.append("\tEmail Address: " + cursor.getString(emailColumnIndex) + "\n"); + } + // END_INCLUDE(retrieve_data) + + // Look at DDMS to see all the columns returned by a query to Contactables. + // Behold, the firehose! + for(String column : cursor.getColumnNames()) { + Log.d(TAG, column + column + ": " + + cursor.getString(cursor.getColumnIndex(column)) + "\n"); + } + } while (cursor.moveToNext()); + } + + @Override + public void onLoaderReset(Loader<Cursor> cursorLoader) { + } +} diff --git a/samples/browseable/BasicContactables/src/com.example.android.basiccontactables/MainActivity.java b/samples/browseable/BasicContactables/src/com.example.android.basiccontactables/MainActivity.java new file mode 100644 index 000000000..b8b074e85 --- /dev/null +++ b/samples/browseable/BasicContactables/src/com.example.android.basiccontactables/MainActivity.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2012 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.basiccontactables; + +import android.app.Activity; +import android.app.SearchManager; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.view.Menu; +import android.widget.SearchView; + +/** + * Simple one-activity app that takes a search term via the Action Bar + * and uses it as a query to search the contacts database via the Contactables + * table. + */ +public class MainActivity extends Activity { + + public static final int CONTACT_QUERY_LOADER = 0; + public static final String QUERY_KEY = "query"; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.sample_main); + + if (getIntent() != null) { + handleIntent(getIntent()); + } + } + + @Override + protected void onNewIntent(Intent intent) { + handleIntent(intent); + } + + /** + * Assuming this activity was started with a new intent, process the incoming information and + * react accordingly. + * @param intent + */ + private void handleIntent(Intent intent) { + // Special processing of the incoming intent only occurs if the if the action specified + // by the intent is ACTION_SEARCH. + if (Intent.ACTION_SEARCH.equals(intent.getAction())) { + // SearchManager.QUERY is the key that a SearchManager will use to send a query string + // to an Activity. + String query = intent.getStringExtra(SearchManager.QUERY); + + // We need to create a bundle containing the query string to send along to the + // LoaderManager, which will be handling querying the database and returning results. + Bundle bundle = new Bundle(); + bundle.putString(QUERY_KEY, query); + + ContactablesLoaderCallbacks loaderCallbacks = new ContactablesLoaderCallbacks(this); + + // Start the loader with the new query, and an object that will handle all callbacks. + getLoaderManager().restartLoader(CONTACT_QUERY_LOADER, bundle, loaderCallbacks); + } + } + + @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); + + // Associate searchable configuration with the SearchView + SearchManager searchManager = + (SearchManager) getSystemService(Context.SEARCH_SERVICE); + SearchView searchView = + (SearchView) menu.findItem(R.id.search).getActionView(); + searchView.setSearchableInfo( + searchManager.getSearchableInfo(getComponentName())); + + return true; + } +} diff --git a/samples/browseable/BasicContactables/src/com.example.android.common.logger/Log.java b/samples/browseable/BasicContactables/src/com.example.android.common.logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/BasicContactables/src/com.example.android.common.logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/BasicContactables/src/com.example.android.common.logger/LogFragment.java b/samples/browseable/BasicContactables/src/com.example.android.common.logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/BasicContactables/src/com.example.android.common.logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/BasicContactables/src/com.example.android.common.logger/LogNode.java b/samples/browseable/BasicContactables/src/com.example.android.common.logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/BasicContactables/src/com.example.android.common.logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/BasicContactables/src/com.example.android.common.logger/LogView.java b/samples/browseable/BasicContactables/src/com.example.android.common.logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/BasicContactables/src/com.example.android.common.logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/BasicContactables/src/com.example.android.common.logger/LogWrapper.java b/samples/browseable/BasicContactables/src/com.example.android.common.logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/BasicContactables/src/com.example.android.common.logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/BasicContactables/src/com.example.android.common.logger/MessageOnlyLogFilter.java b/samples/browseable/BasicContactables/src/com.example.android.common.logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/BasicContactables/src/com.example.android.common.logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/BasicGestureDetect/AndroidManifest.xml b/samples/browseable/BasicGestureDetect/AndroidManifest.xml new file mode 100644 index 000000000..1d7b3bdee --- /dev/null +++ b/samples/browseable/BasicGestureDetect/AndroidManifest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.basicgesturedetect" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="17" /> + + <application android:allowBackup="true" + android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@style/AppTheme"> + + <activity android:name=".MainActivity" + android:label="@string/app_name" + android:uiOptions="splitActionBarWhenNarrow"> + <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/browseable/BasicGestureDetect/_index.jd b/samples/browseable/BasicGestureDetect/_index.jd new file mode 100644 index 000000000..bdcd92505 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/_index.jd @@ -0,0 +1,13 @@ + + + +page.tags="BasicGestureDetect" +sample.group=Input +@jd:body + +<p> + + Welcome to Basic Gesture Detect! + In order to try this sample out, try dragging or tapping this text to see what happens! + + </p> diff --git a/samples/browseable/BasicGestureDetect/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicGestureDetect/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..b1efaf4b2 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/BasicGestureDetect/res/drawable-hdpi/tile.9.png b/samples/browseable/BasicGestureDetect/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/BasicGestureDetect/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicGestureDetect/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f5f9244f2 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/BasicGestureDetect/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicGestureDetect/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..5d07b3f06 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/BasicGestureDetect/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicGestureDetect/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6ef21e1f4 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/BasicGestureDetect/res/layout/activity_main.xml b/samples/browseable/BasicGestureDetect/res/layout/activity_main.xml new file mode 100755 index 000000000..bc5a57591 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/res/layout/activity_main.xml @@ -0,0 +1,38 @@ +<!-- + 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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:id="@+id/sample_main_layout"> + <TextView android:id="@+id/sample_output" + style="@style/Widget.SampleMessage" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:text="@string/intro_message" /> + <View + android:layout_width="fill_parent" + android:layout_height="1dp" + android:background="@android:color/darker_gray"/> + <fragment + android:name="com.example.android.common.logger.LogFragment" + android:id="@+id/log_fragment" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" /> +</LinearLayout> diff --git a/samples/browseable/BasicGestureDetect/res/menu/main.xml b/samples/browseable/BasicGestureDetect/res/menu/main.xml new file mode 100644 index 000000000..2c3515dd4 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/res/menu/main.xml @@ -0,0 +1,21 @@ +<!-- + 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. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/sample_action" + android:showAsAction="ifRoom|withText" + android:title="@string/sample_action" /> +</menu> diff --git a/samples/browseable/BasicGestureDetect/res/values-sw600dp/dimens.xml b/samples/browseable/BasicGestureDetect/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/BasicGestureDetect/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicGestureDetect/res/values-sw600dp/styles.xml b/samples/browseable/BasicGestureDetect/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/BasicGestureDetect/res/values/base-strings.xml b/samples/browseable/BasicGestureDetect/res/values/base-strings.xml new file mode 100644 index 000000000..e9ce7cd0d --- /dev/null +++ b/samples/browseable/BasicGestureDetect/res/values/base-strings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">BasicGestureDetect</string> + <string name="intro_message"> + <![CDATA[ + + + Welcome to Basic Gesture Detect! + In order to try this sample out, try dragging or tapping this text to see what happens! + + + ]]> + </string> +</resources> diff --git a/samples/browseable/BasicGestureDetect/res/values/dimens.xml b/samples/browseable/BasicGestureDetect/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/BasicGestureDetect/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicGestureDetect/res/values/strings.xml b/samples/browseable/BasicGestureDetect/res/values/strings.xml new file mode 100644 index 000000000..c047c4fcc --- /dev/null +++ b/samples/browseable/BasicGestureDetect/res/values/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="sample_action">Clear Text</string> +</resources> diff --git a/samples/browseable/BasicGestureDetect/res/values/styles.xml b/samples/browseable/BasicGestureDetect/res/values/styles.xml new file mode 100644 index 000000000..d3f82ff64 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/res/values/styles.xml @@ -0,0 +1,51 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="AppTheme" parent="Theme.Base" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + + + <style name="Widget.SampleOutput"> + <item name="android:padding">@dimen/margin_medium</item> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Log" parent="Widget.SampleOutput"> + <item name="android:typeface">monospace</item> + </style> + +</resources> diff --git a/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/BasicGestureDetectFragment.java b/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/BasicGestureDetectFragment.java new file mode 100644 index 000000000..820e97249 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/BasicGestureDetectFragment.java @@ -0,0 +1,81 @@ +/* +* 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.basicgesturedetect; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.GestureDetector; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogFragment; + +public class BasicGestureDetectFragment extends Fragment{ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + View gestureView = getActivity().findViewById(R.id.sample_output); + gestureView.setClickable(true); + gestureView.setFocusable(true); + + // BEGIN_INCLUDE(init_detector) + + // First create the GestureListener that will include all our callbacks. + // Then create the GestureDetector, which takes that listener as an argument. + GestureDetector.SimpleOnGestureListener gestureListener = new GestureListener(); + final GestureDetector gd = new GestureDetector(getActivity(), gestureListener); + + /* For the view where gestures will occur, create an onTouchListener that sends + * all motion events to the gesture detector. When the gesture detector + * actually detects an event, it will use the callbacks you created in the + * SimpleOnGestureListener to alert your application. + */ + + gestureView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + gd.onTouchEvent(motionEvent); + return false; + } + }); + // END_INCLUDE(init_detector) + } + + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.sample_action) { + clearLog(); + } + return true; + } + + public void clearLog() { + LogFragment logFragment = ((LogFragment) getActivity().getSupportFragmentManager() + .findFragmentById(R.id.log_fragment)); + logFragment.getLogView().setText(""); + } +} diff --git a/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/GestureListener.java b/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/GestureListener.java new file mode 100644 index 000000000..c2d2b6fb0 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/GestureListener.java @@ -0,0 +1,96 @@ +/* + * 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.basicgesturedetect; + +import android.view.GestureDetector; +import android.view.MotionEvent; + +import com.example.android.common.logger.Log; + +public class GestureListener extends GestureDetector.SimpleOnGestureListener { + + public static final String TAG = "GestureListener"; + + // BEGIN_INCLUDE(init_gestureListener) + @Override + public boolean onSingleTapUp(MotionEvent e) { + // Up motion completing a single tap occurred. + Log.i(TAG, "Single Tap Up"); + return false; + } + + @Override + public void onLongPress(MotionEvent e) { + // Touch has been long enough to indicate a long press. + // Does not indicate motion is complete yet (no up event necessarily) + Log.i(TAG, "Long Press"); + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, + float distanceY) { + // User attempted to scroll + Log.i(TAG, "Scroll"); + return false; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY) { + // Fling event occurred. Notification of this one happens after an "up" event. + Log.i(TAG, "Fling"); + return false; + } + + @Override + public void onShowPress(MotionEvent e) { + // User performed a down event, and hasn't moved yet. + Log.i(TAG, "Show Press"); + } + + @Override + public boolean onDown(MotionEvent e) { + // "Down" event - User touched the screen. + Log.i(TAG, "Down"); + return false; + } + + @Override + public boolean onDoubleTap(MotionEvent e) { + // User tapped the screen twice. + Log.i(TAG, "Double tap"); + return false; + } + + @Override + public boolean onDoubleTapEvent(MotionEvent e) { + // Since double-tap is actually several events which are considered one aggregate + // gesture, there's a separate callback for an individual event within the doubletap + // occurring. This occurs for down, up, and move. + Log.i(TAG, "Event within double tap"); + return false; + } + + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + // A confirmed single-tap event has occurred. Only called when the detector has + // determined that the first tap stands alone, and is not part of a double tap. + Log.i(TAG, "Single tap confirmed"); + return false; + } + // END_INCLUDE(init_gestureListener) +} diff --git a/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/MainActivity.java b/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/MainActivity.java new file mode 100644 index 000000000..154780760 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/MainActivity.java @@ -0,0 +1,80 @@ +/* +* 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.basicgesturedetect; + +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.view.Menu; + +import com.example.android.common.activities.SampleActivityBase; +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogFragment; +import com.example.android.common.logger.LogWrapper; +import com.example.android.common.logger.MessageOnlyLogFilter; + +/** + * A simple launcher activity containing a summary sample description + * and a few action bar buttons. + */ +public class MainActivity extends SampleActivityBase { + + public static final String TAG = "MainActivity"; + + public static final String FRAGTAG = "BasicGestureDetectFragment"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + if (getSupportFragmentManager().findFragmentByTag(FRAGTAG) == null ) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + BasicGestureDetectFragment fragment = new BasicGestureDetectFragment(); + transaction.add(fragment, FRAGTAG); + transaction.commit(); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + /** Create a chain of targets that will receive log data */ + @Override + public void initializeLogging() { + // Wraps Android's native log framework. + LogWrapper logWrapper = new LogWrapper(); + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + Log.setLogNode(logWrapper); + + // Filter strips out everything except the message text. + MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); + logWrapper.setNext(msgFilter); + + // On screen logging via a fragment with a TextView. + LogFragment logFragment = (LogFragment) getSupportFragmentManager() + .findFragmentById(R.id.log_fragment); + msgFilter.setNext(logFragment.getLogView()); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/BasicGestureDetect/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/BasicGestureDetect/src/com.example.android.common/activities/SampleActivityBase.java new file mode 100644 index 000000000..3228927b7 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/src/com.example.android.common/activities/SampleActivityBase.java @@ -0,0 +1,52 @@ +/* +* 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.common.activities; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogWrapper; + +/** + * Base launcher activity, to handle most of the common plumbing for samples. + */ +public class SampleActivityBase extends FragmentActivity { + + public static final String TAG = "SampleActivityBase"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + initializeLogging(); + } + + /** Set up targets to receive log data */ + public void initializeLogging() { + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/Log.java b/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/LogNode.java b/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/LogView.java b/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/BasicGestureDetect/src/com.example.android.common/logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/BasicImmersiveMode/AndroidManifest.xml b/samples/browseable/BasicImmersiveMode/AndroidManifest.xml new file mode 100644 index 000000000..00b4e3c64 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/AndroidManifest.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.basicimmersivemode" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" /> + + <application android:allowBackup="true" + android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@style/AppTheme"> + + <activity android:name=".MainActivity" + android:label="@string/app_name" + android:uiOptions="splitActionBarWhenNarrow"> + <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/browseable/BasicImmersiveMode/_index.jd b/samples/browseable/BasicImmersiveMode/_index.jd new file mode 100644 index 000000000..5394b6ba6 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/_index.jd @@ -0,0 +1,14 @@ + + + +page.tags="BasicImmersiveMode" +sample.group=UI +@jd:body + +<p> + + \"Immersive Mode\" is a new UI mode which improves \"hide full screen\" and + \"hide nav bar\" modes, by letting users swipe the bars in and out. This sample + demonstrates how to enable and disable immersive mode programmatically. + + </p> diff --git a/samples/browseable/BasicImmersiveMode/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicImmersiveMode/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..b1efaf4b2 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/BasicImmersiveMode/res/drawable-hdpi/tile.9.png b/samples/browseable/BasicImmersiveMode/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/BasicImmersiveMode/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicImmersiveMode/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f5f9244f2 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/BasicImmersiveMode/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicImmersiveMode/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..5d07b3f06 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/BasicImmersiveMode/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicImmersiveMode/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6ef21e1f4 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/BasicImmersiveMode/res/layout/activity_main.xml b/samples/browseable/BasicImmersiveMode/res/layout/activity_main.xml new file mode 100755 index 000000000..bc5a57591 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/res/layout/activity_main.xml @@ -0,0 +1,38 @@ +<!-- + 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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:id="@+id/sample_main_layout"> + <TextView android:id="@+id/sample_output" + style="@style/Widget.SampleMessage" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:text="@string/intro_message" /> + <View + android:layout_width="fill_parent" + android:layout_height="1dp" + android:background="@android:color/darker_gray"/> + <fragment + android:name="com.example.android.common.logger.LogFragment" + android:id="@+id/log_fragment" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" /> +</LinearLayout> diff --git a/samples/browseable/BasicImmersiveMode/res/menu/main.xml b/samples/browseable/BasicImmersiveMode/res/menu/main.xml new file mode 100644 index 000000000..2c3515dd4 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/res/menu/main.xml @@ -0,0 +1,21 @@ +<!-- + 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. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/sample_action" + android:showAsAction="ifRoom|withText" + android:title="@string/sample_action" /> +</menu> diff --git a/samples/browseable/BasicImmersiveMode/res/values-sw600dp/dimens.xml b/samples/browseable/BasicImmersiveMode/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicImmersiveMode/res/values-sw600dp/styles.xml b/samples/browseable/BasicImmersiveMode/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/BasicImmersiveMode/res/values/base-strings.xml b/samples/browseable/BasicImmersiveMode/res/values/base-strings.xml new file mode 100644 index 000000000..20a06479b --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/res/values/base-strings.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">BasicImmersiveMode</string> + <string name="intro_message"> + <![CDATA[ + + + \"Immersive Mode\" is a new UI mode which improves \"hide full screen\" and + \"hide nav bar\" modes, by letting users swipe the bars in and out. This sample + demonstrates how to enable and disable immersive mode programmatically. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/BasicImmersiveMode/res/values/dimens.xml b/samples/browseable/BasicImmersiveMode/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicImmersiveMode/res/values/strings.xml b/samples/browseable/BasicImmersiveMode/res/values/strings.xml new file mode 100644 index 000000000..e845261aa --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/res/values/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="sample_action">Toggle Immersive Mode!</string> +</resources> diff --git a/samples/browseable/BasicImmersiveMode/res/values/styles.xml b/samples/browseable/BasicImmersiveMode/res/values/styles.xml new file mode 100644 index 000000000..d3f82ff64 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/res/values/styles.xml @@ -0,0 +1,51 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="AppTheme" parent="Theme.Base" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + + + <style name="Widget.SampleOutput"> + <item name="android:padding">@dimen/margin_medium</item> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Log" parent="Widget.SampleOutput"> + <item name="android:typeface">monospace</item> + </style> + +</resources> diff --git a/samples/browseable/BasicImmersiveMode/src/com.example.android.basicimmersivemode/BasicImmersiveModeFragment.java b/samples/browseable/BasicImmersiveMode/src/com.example.android.basicimmersivemode/BasicImmersiveModeFragment.java new file mode 100644 index 000000000..a675e05be --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/src/com.example.android.basicimmersivemode/BasicImmersiveModeFragment.java @@ -0,0 +1,88 @@ +/* +* 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.basicimmersivemode; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.MenuItem; +import android.view.View; + +import com.example.android.common.logger.Log; + +public class BasicImmersiveModeFragment extends Fragment { + + public static final String TAG = "BasicImmersiveModeFragment"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + final View decorView = getActivity().getWindow().getDecorView(); + decorView.setOnSystemUiVisibilityChangeListener( + new View.OnSystemUiVisibilityChangeListener() { + @Override + public void onSystemUiVisibilityChange(int i) { + int height = decorView.getHeight(); + Log.i(TAG, "Current height: " + height); + } + }); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.sample_action) { + toggleHideyBar(); + } + return true; + } + + /** + * Detects and toggles immersive mode. + */ + public void toggleHideyBar() { + // BEGIN_INCLUDE (get_current_ui_flags) + // The UI options currently enabled are represented by a bitfield. + // getSystemUiVisibility() gives us that bitfield. + int uiOptions = getActivity().getWindow().getDecorView().getSystemUiVisibility(); + int newUiOptions = uiOptions; + // END_INCLUDE (get_current_ui_flags) + // BEGIN_INCLUDE (toggle_ui_flags) + boolean isImmersiveModeEnabled = + ((uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) == uiOptions); + if (isImmersiveModeEnabled) { + Log.i(TAG, "Turning immersive mode mode off. "); + } else { + Log.i(TAG, "Turning immersive mode mode on."); + } + + // Immersive mode: Backward compatible to KitKat (API 19). + // Note that this flag doesn't do anything by itself, it only augments the behavior + // of HIDE_NAVIGATION and FLAG_FULLSCREEN. For the purposes of this sample + // all three flags are being toggled together. + // This sample uses the "sticky" form of immersive mode, which will let the user swipe + // the bars back in again, but will automatically make them disappear a few seconds later. + newUiOptions ^= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + newUiOptions ^= View.SYSTEM_UI_FLAG_FULLSCREEN; + newUiOptions ^= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + getActivity().getWindow().getDecorView().setSystemUiVisibility(newUiOptions); + //END_INCLUDE (set_ui_flags) + } +} diff --git a/samples/browseable/BasicImmersiveMode/src/com.example.android.basicimmersivemode/MainActivity.java b/samples/browseable/BasicImmersiveMode/src/com.example.android.basicimmersivemode/MainActivity.java new file mode 100644 index 000000000..83e8f0eca --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/src/com.example.android.basicimmersivemode/MainActivity.java @@ -0,0 +1,80 @@ +/* +* 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.basicimmersivemode; + +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.view.Menu; + +import com.example.android.common.activities.SampleActivityBase; +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogFragment; +import com.example.android.common.logger.LogWrapper; +import com.example.android.common.logger.MessageOnlyLogFilter; + +/** + * A simple launcher activity containing a summary sample description + * and a few action bar buttons. + */ +public class MainActivity extends SampleActivityBase { + + public static final String TAG = "MainActivity"; + + public static final String FRAGTAG = "BasicImmersiveModeFragment"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + if (getSupportFragmentManager().findFragmentByTag(FRAGTAG) == null ) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + BasicImmersiveModeFragment fragment = new BasicImmersiveModeFragment(); + transaction.add(fragment, FRAGTAG); + transaction.commit(); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + /** Create a chain of targets that will receive log data */ + @Override + public void initializeLogging() { + // Wraps Android's native log framework. + LogWrapper logWrapper = new LogWrapper(); + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + Log.setLogNode(logWrapper); + + // Filter strips out everything except the message text. + MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); + logWrapper.setNext(msgFilter); + + // On screen logging via a fragment with a TextView. + LogFragment logFragment = (LogFragment) getSupportFragmentManager() + .findFragmentById(R.id.log_fragment); + msgFilter.setNext(logFragment.getLogView()); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/BasicImmersiveMode/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/BasicImmersiveMode/src/com.example.android.common/activities/SampleActivityBase.java new file mode 100644 index 000000000..3228927b7 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/src/com.example.android.common/activities/SampleActivityBase.java @@ -0,0 +1,52 @@ +/* +* 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.common.activities; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogWrapper; + +/** + * Base launcher activity, to handle most of the common plumbing for samples. + */ +public class SampleActivityBase extends FragmentActivity { + + public static final String TAG = "SampleActivityBase"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + initializeLogging(); + } + + /** Set up targets to receive log data */ + public void initializeLogging() { + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/Log.java b/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/LogNode.java b/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/LogView.java b/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/BasicImmersiveMode/src/com.example.android.common/logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/BasicMediaDecoder/AndroidManifest.xml b/samples/browseable/BasicMediaDecoder/AndroidManifest.xml new file mode 100644 index 000000000..d19149110 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/AndroidManifest.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.basicmediadecoder" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk android:minSdkVersion="17"/> + <application + android:label="@string/app_name" android:icon="@drawable/ic_launcher"> + <activity + android:name=".MainActivity" + android:screenOrientation="landscape" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/samples/browseable/BasicMediaDecoder/_index.jd b/samples/browseable/BasicMediaDecoder/_index.jd new file mode 100644 index 000000000..af8e5e4ea --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/_index.jd @@ -0,0 +1,13 @@ + + + +page.tags="BasicMediaDecoder" +sample.group=Media +@jd:body + +<p> + + This activity uses a TextureView to render the frames of a video decoded using the + MediaCodec API. + + </p> diff --git a/samples/browseable/BasicMediaDecoder/res/drawable-hdpi/ic_action_play.png b/samples/browseable/BasicMediaDecoder/res/drawable-hdpi/ic_action_play.png Binary files differnew file mode 100755 index 000000000..dbfd337a5 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/drawable-hdpi/ic_action_play.png diff --git a/samples/browseable/BasicMediaDecoder/res/drawable-hdpi/ic_action_play_disabled.png b/samples/browseable/BasicMediaDecoder/res/drawable-hdpi/ic_action_play_disabled.png Binary files differnew file mode 100755 index 000000000..e4310efc6 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/drawable-hdpi/ic_action_play_disabled.png diff --git a/samples/browseable/BasicMediaDecoder/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicMediaDecoder/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..9bc536b31 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/BasicMediaDecoder/res/drawable-hdpi/tile.9.png b/samples/browseable/BasicMediaDecoder/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/BasicMediaDecoder/res/drawable-mdpi/ic_action_play.png b/samples/browseable/BasicMediaDecoder/res/drawable-mdpi/ic_action_play.png Binary files differnew file mode 100755 index 000000000..a2f198aef --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/drawable-mdpi/ic_action_play.png diff --git a/samples/browseable/BasicMediaDecoder/res/drawable-mdpi/ic_action_play_disabled.png b/samples/browseable/BasicMediaDecoder/res/drawable-mdpi/ic_action_play_disabled.png Binary files differnew file mode 100755 index 000000000..d69107baa --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/drawable-mdpi/ic_action_play_disabled.png diff --git a/samples/browseable/BasicMediaDecoder/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicMediaDecoder/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..d656b2157 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/BasicMediaDecoder/res/drawable-xhdpi/ic_action_play.png b/samples/browseable/BasicMediaDecoder/res/drawable-xhdpi/ic_action_play.png Binary files differnew file mode 100755 index 000000000..9e63c90be --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/drawable-xhdpi/ic_action_play.png diff --git a/samples/browseable/BasicMediaDecoder/res/drawable-xhdpi/ic_action_play_disabled.png b/samples/browseable/BasicMediaDecoder/res/drawable-xhdpi/ic_action_play_disabled.png Binary files differnew file mode 100755 index 000000000..2ff8c3992 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/drawable-xhdpi/ic_action_play_disabled.png diff --git a/samples/browseable/BasicMediaDecoder/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicMediaDecoder/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..bbb9b1642 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/BasicMediaDecoder/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicMediaDecoder/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..4a5c33f10 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/BasicMediaDecoder/res/drawable/selector_play.xml b/samples/browseable/BasicMediaDecoder/res/drawable/selector_play.xml new file mode 100644 index 000000000..23071359e --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/drawable/selector_play.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="true" + android:drawable="@drawable/ic_action_play"/> + + <item android:state_enabled="false" + android:drawable="@drawable/ic_action_play_disabled"/> +</selector>
\ No newline at end of file diff --git a/samples/browseable/BasicMediaDecoder/res/layout/activity_main.xml b/samples/browseable/BasicMediaDecoder/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/BasicMediaDecoder/res/layout/sample_main.xml b/samples/browseable/BasicMediaDecoder/res/layout/sample_main.xml new file mode 100644 index 000000000..7543120a8 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/layout/sample_main.xml @@ -0,0 +1,35 @@ +<?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. +--> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + > + <TextureView + android:id="@+id/PlaybackView" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + <TextView + android:id="@+id/AttribView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="right|bottom" + android:visibility="gone" + android:textColor="@android:color/holo_blue_bright" + android:text="@string/app_video_attrib"/> +</FrameLayout> + diff --git a/samples/browseable/BasicMediaDecoder/res/menu/action_menu.xml b/samples/browseable/BasicMediaDecoder/res/menu/action_menu.xml new file mode 100644 index 000000000..2b31a8628 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/menu/action_menu.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. +--> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/menu_play" + android:icon="@drawable/selector_play" + android:title="Play" + android:showAsAction="ifRoom|withText" /> +</menu>
\ No newline at end of file diff --git a/samples/browseable/BasicMediaDecoder/res/raw/vid_bigbuckbunny.mp4 b/samples/browseable/BasicMediaDecoder/res/raw/vid_bigbuckbunny.mp4 Binary files differnew file mode 100644 index 000000000..81d11df5f --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/raw/vid_bigbuckbunny.mp4 diff --git a/samples/browseable/BasicMediaDecoder/res/values-sw600dp/dimens.xml b/samples/browseable/BasicMediaDecoder/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicMediaDecoder/res/values-sw600dp/styles.xml b/samples/browseable/BasicMediaDecoder/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/BasicMediaDecoder/res/values/base-strings.xml b/samples/browseable/BasicMediaDecoder/res/values/base-strings.xml new file mode 100644 index 000000000..94d02a896 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/values/base-strings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">BasicMediaDecoder</string> + <string name="intro_message"> + <![CDATA[ + + + This activity uses a TextureView to render the frames of a video decoded using the + MediaCodec API. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/BasicMediaDecoder/res/values/dimens.xml b/samples/browseable/BasicMediaDecoder/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicMediaDecoder/res/values/strings.xml b/samples/browseable/BasicMediaDecoder/res/values/strings.xml new file mode 100644 index 000000000..2cf79ab71 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/values/strings.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> + <string name="app_video_attrib">(c) copyright 2008, Blender Foundation / www.bigbuckbunny.org + </string> +</resources> diff --git a/samples/browseable/BasicMediaDecoder/res/values/styles.xml b/samples/browseable/BasicMediaDecoder/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/BasicMediaDecoder/src/com.example.android.basicmediadecoder/MainActivity.java b/samples/browseable/BasicMediaDecoder/src/com.example.android.basicmediadecoder/MainActivity.java new file mode 100644 index 000000000..cac5bf218 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/src/com.example.android.basicmediadecoder/MainActivity.java @@ -0,0 +1,189 @@ +/* + * 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.basicmediadecoder; + + +import android.animation.TimeAnimator; +import android.app.Activity; +import android.media.MediaCodec; +import android.media.MediaExtractor; +import android.net.Uri; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.Surface; +import android.view.TextureView; +import android.view.View; +import android.widget.TextView; + +import com.example.android.common.media.MediaCodecWrapper; + +import java.io.IOException; + +/** + * This activity uses a {@link android.view.TextureView} to render the frames of a video decoded using + * {@link android.media.MediaCodec} API. + */ +public class MainActivity extends Activity { + + private TextureView mPlaybackView; + private TimeAnimator mTimeAnimator = new TimeAnimator(); + + // A utility that wraps up the underlying input and output buffer processing operations + // into an east to use API. + private MediaCodecWrapper mCodecWrapper; + private MediaExtractor mExtractor = new MediaExtractor(); + TextView mAttribView = null; + + + /** + * Called when the activity is first created. + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.sample_main); + mPlaybackView = (TextureView) findViewById(R.id.PlaybackView); + mAttribView = (TextView)findViewById(R.id.AttribView); + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.action_menu, menu); + return true; + } + + @Override + protected void onPause() { + super.onPause(); + if(mTimeAnimator != null && mTimeAnimator.isRunning()) { + mTimeAnimator.end(); + } + + if (mCodecWrapper != null ) { + mCodecWrapper.stopAndRelease(); + mExtractor.release(); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.menu_play) { + mAttribView.setVisibility(View.VISIBLE); + startPlayback(); + item.setEnabled(false); + } + return true; + } + + + public void startPlayback() { + + // Construct a URI that points to the video resource that we want to play + Uri videoUri = Uri.parse("android.resource://" + + getPackageName() + "/" + + R.raw.vid_bigbuckbunny); + + try { + + // BEGIN_INCLUDE(initialize_extractor) + mExtractor.setDataSource(this, videoUri, null); + int nTracks = mExtractor.getTrackCount(); + + // Begin by unselecting all of the tracks in the extractor, so we won't see + // any tracks that we haven't explicitly selected. + for (int i = 0; i < nTracks; ++i) { + mExtractor.unselectTrack(i); + } + + + // Find the first video track in the stream. In a real-world application + // it's possible that the stream would contain multiple tracks, but this + // sample assumes that we just want to play the first one. + for (int i = 0; i < nTracks; ++i) { + // Try to create a video codec for this track. This call will return null if the + // track is not a video track, or not a recognized video format. Once it returns + // a valid MediaCodecWrapper, we can break out of the loop. + mCodecWrapper = MediaCodecWrapper.fromVideoFormat(mExtractor.getTrackFormat(i), + new Surface(mPlaybackView.getSurfaceTexture())); + if (mCodecWrapper != null) { + mExtractor.selectTrack(i); + break; + } + } + // END_INCLUDE(initialize_extractor) + + + + + // By using a {@link TimeAnimator}, we can sync our media rendering commands with + // the system display frame rendering. The animator ticks as the {@link Choreographer} + // recieves VSYNC events. + mTimeAnimator.setTimeListener(new TimeAnimator.TimeListener() { + @Override + public void onTimeUpdate(final TimeAnimator animation, + final long totalTime, + final long deltaTime) { + + boolean isEos = ((mExtractor.getSampleFlags() & MediaCodec + .BUFFER_FLAG_END_OF_STREAM) == MediaCodec.BUFFER_FLAG_END_OF_STREAM); + + // BEGIN_INCLUDE(write_sample) + if (!isEos) { + // Try to submit the sample to the codec and if successful advance the + // extractor to the next available sample to read. + boolean result = mCodecWrapper.writeSample(mExtractor, false, + mExtractor.getSampleTime(), mExtractor.getSampleFlags()); + + if (result) { + // Advancing the extractor is a blocking operation and it MUST be + // executed outside the main thread in real applications. + mExtractor.advance(); + } + } + // END_INCLUDE(write_sample) + + // Examine the sample at the head of the queue to see if its ready to be + // rendered and is not zero sized End-of-Stream record. + MediaCodec.BufferInfo out_bufferInfo = new MediaCodec.BufferInfo(); + mCodecWrapper.peekSample(out_bufferInfo); + + // BEGIN_INCLUDE(render_sample) + if (out_bufferInfo.size <= 0 && isEos) { + mTimeAnimator.end(); + mCodecWrapper.stopAndRelease(); + mExtractor.release(); + } else if (out_bufferInfo.presentationTimeUs / 1000 < totalTime) { + // Pop the sample off the queue and send it to {@link Surface} + mCodecWrapper.popSample(true); + } + // END_INCLUDE(render_sample) + + } + }); + + // We're all set. Kick off the animator to process buffers and render video frames as + // they become available + mTimeAnimator.start(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/samples/browseable/BasicMediaDecoder/src/com.example.android.common.media/CameraHelper.java b/samples/browseable/BasicMediaDecoder/src/com.example.android.common.media/CameraHelper.java new file mode 100644 index 000000000..1fa841675 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/src/com.example.android.common.media/CameraHelper.java @@ -0,0 +1,182 @@ +/* + * 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.common.media; + +import android.annotation.TargetApi; +import android.hardware.Camera; +import android.os.Build; +import android.os.Environment; +import android.util.Log; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +/** + * Camera related utilities. + */ +public class CameraHelper { + + public static final int MEDIA_TYPE_IMAGE = 1; + public static final int MEDIA_TYPE_VIDEO = 2; + + /** + * Iterate over supported camera preview sizes to see which one best fits the + * dimensions of the given view while maintaining the aspect ratio. If none can, + * be lenient with the aspect ratio. + * + * @param sizes Supported camera preview sizes. + * @param w The width of the view. + * @param h The height of the view. + * @return Best match camera preview size to fit in the view. + */ + public static Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) { + // Use a very small tolerance because we want an exact match. + final double ASPECT_TOLERANCE = 0.1; + double targetRatio = (double) w / h; + if (sizes == null) + return null; + + Camera.Size optimalSize = null; + + // Start with max value and refine as we iterate over available preview sizes. This is the + // minimum difference between view and camera height. + double minDiff = Double.MAX_VALUE; + + // Target view height + int targetHeight = h; + + // Try to find a preview size that matches aspect ratio and the target view size. + // Iterate over all available sizes and pick the largest size that can fit in the view and + // still maintain the aspect ratio. + for (Camera.Size size : sizes) { + double ratio = (double) size.width / size.height; + if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) + continue; + if (Math.abs(size.height - targetHeight) < minDiff) { + optimalSize = size; + minDiff = Math.abs(size.height - targetHeight); + } + } + + // Cannot find preview size that matches the aspect ratio, ignore the requirement + if (optimalSize == null) { + minDiff = Double.MAX_VALUE; + for (Camera.Size size : sizes) { + if (Math.abs(size.height - targetHeight) < minDiff) { + optimalSize = size; + minDiff = Math.abs(size.height - targetHeight); + } + } + } + return optimalSize; + } + + /** + * @return the default camera on the device. Return null if there is no camera on the device. + */ + public static Camera getDefaultCameraInstance() { + return Camera.open(); + } + + + /** + * @return the default rear/back facing camera on the device. Returns null if camera is not + * available. + */ + public static Camera getDefaultBackFacingCameraInstance() { + return getDefaultCamera(Camera.CameraInfo.CAMERA_FACING_BACK); + } + + /** + * @return the default front facing camera on the device. Returns null if camera is not + * available. + */ + public static Camera getDefaultFrontFacingCameraInstance() { + return getDefaultCamera(Camera.CameraInfo.CAMERA_FACING_FRONT); + } + + + /** + * + * @param position Physical position of the camera i.e Camera.CameraInfo.CAMERA_FACING_FRONT + * or Camera.CameraInfo.CAMERA_FACING_BACK. + * @return the default camera on the device. Returns null if camera is not available. + */ + @TargetApi(Build.VERSION_CODES.GINGERBREAD) + private static Camera getDefaultCamera(int position) { + // Find the total number of cameras available + int mNumberOfCameras = Camera.getNumberOfCameras(); + + // Find the ID of the back-facing ("default") camera + Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); + for (int i = 0; i < mNumberOfCameras; i++) { + Camera.getCameraInfo(i, cameraInfo); + if (cameraInfo.facing == position) { + return Camera.open(i); + + } + } + + return null; + } + + /** + * Creates a media file in the {@code Environment.DIRECTORY_PICTURES} directory. The directory + * is persistent and available to other applications like gallery. + * + * @param type Media type. Can be video or image. + * @return A file object pointing to the newly created file. + */ + public static File getOutputMediaFile(int type){ + // To be safe, you should check that the SDCard is mounted + // using Environment.getExternalStorageState() before doing this. + if (!Environment.getExternalStorageState().equalsIgnoreCase(Environment.MEDIA_MOUNTED)) { + return null; + } + + File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_PICTURES), "CameraSample"); + // This location works best if you want the created images to be shared + // between applications and persist after your app has been uninstalled. + + // Create the storage directory if it does not exist + if (! mediaStorageDir.exists()){ + if (! mediaStorageDir.mkdirs()) { + Log.d("CameraSample", "failed to create directory"); + return null; + } + } + + // Create a media file name + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); + File mediaFile; + if (type == MEDIA_TYPE_IMAGE){ + mediaFile = new File(mediaStorageDir.getPath() + File.separator + + "IMG_"+ timeStamp + ".jpg"); + } else if(type == MEDIA_TYPE_VIDEO) { + mediaFile = new File(mediaStorageDir.getPath() + File.separator + + "VID_"+ timeStamp + ".mp4"); + } else { + return null; + } + + return mediaFile; + } + +} diff --git a/samples/browseable/BasicMediaDecoder/src/com.example.android.common.media/MediaCodecWrapper.java b/samples/browseable/BasicMediaDecoder/src/com.example.android.common.media/MediaCodecWrapper.java new file mode 100644 index 000000000..a511221f5 --- /dev/null +++ b/samples/browseable/BasicMediaDecoder/src/com.example.android.common.media/MediaCodecWrapper.java @@ -0,0 +1,386 @@ +/* + * 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.common.media; + +import android.media.*; +import android.os.Handler; +import android.os.Looper; +import android.view.Surface; + +import java.nio.ByteBuffer; +import java.util.ArrayDeque; +import java.util.Queue; + +/** + * Simplifies the MediaCodec interface by wrapping around the buffer processing operations. + */ +public class MediaCodecWrapper { + + // Handler to use for {@code OutputSampleListener} and {code OutputFormatChangedListener} + // callbacks + private Handler mHandler; + + + // Callback when media output format changes. + public interface OutputFormatChangedListener { + void outputFormatChanged(MediaCodecWrapper sender, MediaFormat newFormat); + } + + private OutputFormatChangedListener mOutputFormatChangedListener = null; + + /** + * Callback for decodes frames. Observers can register a listener for optional stream + * of decoded data + */ + public interface OutputSampleListener { + void outputSample(MediaCodecWrapper sender, MediaCodec.BufferInfo info, ByteBuffer buffer); + } + + /** + * The {@link MediaCodec} that is managed by this class. + */ + private MediaCodec mDecoder; + + // References to the internal buffers managed by the codec. The codec + // refers to these buffers by index, never by reference so it's up to us + // to keep track of which buffer is which. + private ByteBuffer[] mInputBuffers; + private ByteBuffer[] mOutputBuffers; + + // Indices of the input buffers that are currently available for writing. We'll + // consume these in the order they were dequeued from the codec. + private Queue<Integer> mAvailableInputBuffers; + + // Indices of the output buffers that currently hold valid data, in the order + // they were produced by the codec. + private Queue<Integer> mAvailableOutputBuffers; + + // Information about each output buffer, by index. Each entry in this array + // is valid if and only if its index is currently contained in mAvailableOutputBuffers. + private MediaCodec.BufferInfo[] mOutputBufferInfo; + + // An (optional) stream that will receive decoded data. + private OutputSampleListener mOutputSampleListener; + + private MediaCodecWrapper(MediaCodec codec) { + mDecoder = codec; + codec.start(); + mInputBuffers = codec.getInputBuffers(); + mOutputBuffers = codec.getOutputBuffers(); + mOutputBufferInfo = new MediaCodec.BufferInfo[mOutputBuffers.length]; + mAvailableInputBuffers = new ArrayDeque<Integer>(mOutputBuffers.length); + mAvailableOutputBuffers = new ArrayDeque<Integer>(mInputBuffers.length); + } + + /** + * Releases resources and ends the encoding/decoding session. + */ + public void stopAndRelease() { + mDecoder.stop(); + mDecoder.release(); + mDecoder = null; + mHandler = null; + } + + /** + * Getter for the registered {@link OutputFormatChangedListener} + */ + public OutputFormatChangedListener getOutputFormatChangedListener() { + return mOutputFormatChangedListener; + } + + /** + * + * @param outputFormatChangedListener the listener for callback. + * @param handler message handler for posting the callback. + */ + public void setOutputFormatChangedListener(final OutputFormatChangedListener + outputFormatChangedListener, Handler handler) { + mOutputFormatChangedListener = outputFormatChangedListener; + + // Making sure we don't block ourselves due to a bad implementation of the callback by + // using a handler provided by client. + Looper looper; + mHandler = handler; + if (outputFormatChangedListener != null && mHandler == null) { + if ((looper = Looper.myLooper()) != null) { + mHandler = new Handler(); + } else { + throw new IllegalArgumentException( + "Looper doesn't exist in the calling thread"); + } + } + } + + /** + * Constructs the {@link MediaCodecWrapper} wrapper object around the video codec. + * The codec is created using the encapsulated information in the + * {@link MediaFormat} object. + * + * @param trackFormat The format of the media object to be decoded. + * @param surface Surface to render the decoded frames. + * @return + */ + public static MediaCodecWrapper fromVideoFormat(final MediaFormat trackFormat, + Surface surface) { + MediaCodecWrapper result = null; + MediaCodec videoCodec = null; + + // BEGIN_INCLUDE(create_codec) + final String mimeType = trackFormat.getString(MediaFormat.KEY_MIME); + + // Check to see if this is actually a video mime type. If it is, then create + // a codec that can decode this mime type. + if (mimeType.contains("video/")) { + videoCodec = MediaCodec.createDecoderByType(mimeType); + videoCodec.configure(trackFormat, surface, null, 0); + + } + + // If codec creation was successful, then create a wrapper object around the + // newly created codec. + if (videoCodec != null) { + result = new MediaCodecWrapper(videoCodec); + } + // END_INCLUDE(create_codec) + + return result; + } + + + /** + * Write a media sample to the decoder. + * + * A "sample" here refers to a single atomic access unit in the media stream. The definition + * of "access unit" is dependent on the type of encoding used, but it typically refers to + * a single frame of video or a few seconds of audio. {@link android.media.MediaExtractor} + * extracts data from a stream one sample at a time. + * + * @param input A ByteBuffer containing the input data for one sample. The buffer must be set + * up for reading, with its position set to the beginning of the sample data and its limit + * set to the end of the sample data. + * + * @param presentationTimeUs The time, relative to the beginning of the media stream, + * at which this buffer should be rendered. + * + * @param flags Flags to pass to the decoder. See {@link MediaCodec#queueInputBuffer(int, + * int, int, long, int)} + * + * @throws MediaCodec.CryptoException + */ + public boolean writeSample(final ByteBuffer input, + final MediaCodec.CryptoInfo crypto, + final long presentationTimeUs, + final int flags) throws MediaCodec.CryptoException, WriteException { + boolean result = false; + int size = input.remaining(); + + // check if we have dequed input buffers available from the codec + if (size > 0 && !mAvailableInputBuffers.isEmpty()) { + int index = mAvailableInputBuffers.remove(); + ByteBuffer buffer = mInputBuffers[index]; + + // we can't write our sample to a lesser capacity input buffer. + if (size > buffer.capacity()) { + throw new MediaCodecWrapper.WriteException(String.format( + "Insufficient capacity in MediaCodec buffer: " + + "tried to write %d, buffer capacity is %d.", + input.remaining(), + buffer.capacity())); + } + + buffer.clear(); + buffer.put(input); + + // Submit the buffer to the codec for decoding. The presentationTimeUs + // indicates the position (play time) for the current sample. + if (crypto == null) { + mDecoder.queueInputBuffer(index, 0, size, presentationTimeUs, flags); + } else { + mDecoder.queueSecureInputBuffer(index, 0, crypto, presentationTimeUs, flags); + } + result = true; + } + return result; + } + + static MediaCodec.CryptoInfo cryptoInfo= new MediaCodec.CryptoInfo(); + + /** + * Write a media sample to the decoder. + * + * A "sample" here refers to a single atomic access unit in the media stream. The definition + * of "access unit" is dependent on the type of encoding used, but it typically refers to + * a single frame of video or a few seconds of audio. {@link android.media.MediaExtractor} + * extracts data from a stream one sample at a time. + * + * @param extractor Instance of {@link android.media.MediaExtractor} wrapping the media. + * + * @param presentationTimeUs The time, relative to the beginning of the media stream, + * at which this buffer should be rendered. + * + * @param flags Flags to pass to the decoder. See {@link MediaCodec#queueInputBuffer(int, + * int, int, long, int)} + * + * @throws MediaCodec.CryptoException + */ + public boolean writeSample(final MediaExtractor extractor, + final boolean isSecure, + final long presentationTimeUs, + int flags) { + boolean result = false; + boolean isEos = false; + + if (!mAvailableInputBuffers.isEmpty()) { + int index = mAvailableInputBuffers.remove(); + ByteBuffer buffer = mInputBuffers[index]; + + // reads the sample from the file using extractor into the buffer + int size = extractor.readSampleData(buffer, 0); + if (size <= 0) { + flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM; + } + + // Submit the buffer to the codec for decoding. The presentationTimeUs + // indicates the position (play time) for the current sample. + if (!isSecure) { + mDecoder.queueInputBuffer(index, 0, size, presentationTimeUs, flags); + } else { + extractor.getSampleCryptoInfo(cryptoInfo); + mDecoder.queueSecureInputBuffer(index, 0, cryptoInfo, presentationTimeUs, flags); + } + + result = true; + } + return result; + } + + /** + * Performs a peek() operation in the queue to extract media info for the buffer ready to be + * released i.e. the head element of the queue. + * + * @param out_bufferInfo An output var to hold the buffer info. + * + * @return True, if the peek was successful. + */ + public boolean peekSample(MediaCodec.BufferInfo out_bufferInfo) { + // dequeue available buffers and synchronize our data structures with the codec. + update(); + boolean result = false; + if (!mAvailableOutputBuffers.isEmpty()) { + int index = mAvailableOutputBuffers.peek(); + MediaCodec.BufferInfo info = mOutputBufferInfo[index]; + // metadata of the sample + out_bufferInfo.set( + info.offset, + info.size, + info.presentationTimeUs, + info.flags); + result = true; + } + return result; + } + + /** + * Processes, releases and optionally renders the output buffer available at the head of the + * queue. All observers are notified with a callback. See {@link + * OutputSampleListener#outputSample(MediaCodecWrapper, android.media.MediaCodec.BufferInfo, + * java.nio.ByteBuffer)} + * + * @param render True, if the buffer is to be rendered on the {@link Surface} configured + * + */ + public void popSample(boolean render) { + // dequeue available buffers and synchronize our data structures with the codec. + update(); + if (!mAvailableOutputBuffers.isEmpty()) { + int index = mAvailableOutputBuffers.remove(); + + if (render && mOutputSampleListener != null) { + ByteBuffer buffer = mOutputBuffers[index]; + MediaCodec.BufferInfo info = mOutputBufferInfo[index]; + mOutputSampleListener.outputSample(this, info, buffer); + } + + // releases the buffer back to the codec + mDecoder.releaseOutputBuffer(index, render); + } + } + + /** + * Synchronize this object's state with the internal state of the wrapped + * MediaCodec. + */ + private void update() { + // BEGIN_INCLUDE(update_codec_state) + int index; + + // Get valid input buffers from the codec to fill later in the same order they were + // made available by the codec. + while ((index = mDecoder.dequeueInputBuffer(0)) != MediaCodec.INFO_TRY_AGAIN_LATER) { + mAvailableInputBuffers.add(index); + } + + + // Likewise with output buffers. If the output buffers have changed, start using the + // new set of output buffers. If the output format has changed, notify listeners. + MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); + while ((index = mDecoder.dequeueOutputBuffer(info, 0)) != MediaCodec.INFO_TRY_AGAIN_LATER) { + switch (index) { + case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: + mOutputBuffers = mDecoder.getOutputBuffers(); + mOutputBufferInfo = new MediaCodec.BufferInfo[mOutputBuffers.length]; + mAvailableOutputBuffers.clear(); + break; + case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: + if (mOutputFormatChangedListener != null) { + mHandler.post(new Runnable() { + @Override + public void run() { + mOutputFormatChangedListener + .outputFormatChanged(MediaCodecWrapper.this, + mDecoder.getOutputFormat()); + + } + }); + } + break; + default: + // Making sure the index is valid before adding to output buffers. We've already + // handled INFO_TRY_AGAIN_LATER, INFO_OUTPUT_FORMAT_CHANGED & + // INFO_OUTPUT_BUFFERS_CHANGED i.e all the other possible return codes but + // asserting index value anyways for future-proofing the code. + if(index >= 0) { + mOutputBufferInfo[index] = info; + mAvailableOutputBuffers.add(index); + } else { + throw new IllegalStateException("Unknown status from dequeueOutputBuffer"); + } + break; + } + + } + // END_INCLUDE(update_codec_state) + + } + + private class WriteException extends Throwable { + private WriteException(final String detailMessage) { + super(detailMessage); + } + } +} diff --git a/samples/browseable/BasicMediaRouter/AndroidManifest.xml b/samples/browseable/BasicMediaRouter/AndroidManifest.xml new file mode 100644 index 000000000..33c20d582 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/AndroidManifest.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.basicmediarouter" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk + android:minSdkVersion="17" + android:targetSdkVersion="17" /> + + <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" > + <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/browseable/BasicMediaRouter/_index.jd b/samples/browseable/BasicMediaRouter/_index.jd new file mode 100644 index 000000000..b97fcff85 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/_index.jd @@ -0,0 +1,17 @@ + + + +page.tags="BasicMediaRouter" +sample.group=Media +@jd:body + +<p> + + This sample demonstrates the use of the MediaRouter API to display + content on a secondary display.\n\nUse the "Media Route Action Item" in the ActionBar + to select an output device. If your device supports Miracast wireless displays, + you may need to enable "Wireless Display" functionality in the system settings. + Secondary screen simulation can also be enabled from the "Developer Options".\n\n +Once connected, use the "Change Color" button to change the background color of the secondary screen. + + </p> diff --git a/samples/browseable/BasicMediaRouter/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicMediaRouter/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..b1efaf4b2 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/BasicMediaRouter/res/drawable-hdpi/tile.9.png b/samples/browseable/BasicMediaRouter/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/BasicMediaRouter/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicMediaRouter/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f5f9244f2 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/BasicMediaRouter/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicMediaRouter/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..5d07b3f06 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/BasicMediaRouter/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicMediaRouter/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6ef21e1f4 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/BasicMediaRouter/res/layout/activity_main.xml b/samples/browseable/BasicMediaRouter/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/BasicMediaRouter/res/layout/display.xml b/samples/browseable/BasicMediaRouter/res/layout/display.xml new file mode 100644 index 000000000..47d5aa96b --- /dev/null +++ b/samples/browseable/BasicMediaRouter/res/layout/display.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/display_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center" + android:orientation="vertical" > + + <TextView + android:id="@+id/display_text" + style="@style/DisplayLargeText" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" /> + + <TextView + android:id="@+id/display_smalltext" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" + android:textAppearance="?android:attr/textAppearanceMedium" /> + +</LinearLayout>
\ No newline at end of file diff --git a/samples/browseable/BasicMediaRouter/res/layout/sample_main.xml b/samples/browseable/BasicMediaRouter/res/layout/sample_main.xml new file mode 100644 index 000000000..276851420 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/res/layout/sample_main.xml @@ -0,0 +1,32 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/LinearLayout1" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center" + android:orientation="vertical" + tools:context=".MainActivity" > + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="5dp" + android:text="@string/intro_message" /> + + <TextView + android:id="@+id/textStatus" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:text="@string/secondary_notconnected" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_margin="5dp" /> + + <Button + android:id="@+id/button1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:enabled="false" + android:text="@string/change_color" /> + +</LinearLayout>
\ No newline at end of file diff --git a/samples/browseable/BasicMediaRouter/res/menu/main.xml b/samples/browseable/BasicMediaRouter/res/menu/main.xml new file mode 100644 index 000000000..ebb628668 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/res/menu/main.xml @@ -0,0 +1,12 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android" > + + <!-- This ActionProvider is configured to --> + <item + android:id="@+id/menu_media_route" + android:actionProviderClass="android.app.MediaRouteActionProvider" + android:orderInCategory="1" + android:showAsAction="always" + android:title="@string/menu_present_to" + android:visible="true"/> + +</menu>
\ No newline at end of file diff --git a/samples/browseable/BasicMediaRouter/res/values-sw600dp/dimens.xml b/samples/browseable/BasicMediaRouter/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/BasicMediaRouter/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicMediaRouter/res/values-sw600dp/styles.xml b/samples/browseable/BasicMediaRouter/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/BasicMediaRouter/res/values/base-strings.xml b/samples/browseable/BasicMediaRouter/res/values/base-strings.xml new file mode 100644 index 000000000..211577118 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/res/values/base-strings.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">BasicMediaRouter</string> + <string name="intro_message"> + <![CDATA[ + + + This sample demonstrates the use of the MediaRouter API to display + content on a secondary display.\n\nUse the "Media Route Action Item" in the ActionBar + to select an output device. If your device supports Miracast wireless displays, + you may need to enable "Wireless Display" functionality in the system settings. + Secondary screen simulation can also be enabled from the "Developer Options".\n\n +Once connected, use the "Change Color" button to change the background color of the secondary screen. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/BasicMediaRouter/res/values/colors.xml b/samples/browseable/BasicMediaRouter/res/values/colors.xml new file mode 100644 index 000000000..521d9cd1b --- /dev/null +++ b/samples/browseable/BasicMediaRouter/res/values/colors.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <item name="blue" type="color">#FF33B5E5</item> + <item name="purple" type="color">#FFAA66CC</item> + <item name="green" type="color">#FF99CC00</item> + <item name="orange" type="color">#FFFFBB33</item> + <item name="red" type="color">#FFFF4444</item> + <item name="darkblue" type="color">#FF0099CC</item> + <item name="darkpurple" type="color">#FF9933CC</item> + <item name="darkgreen" type="color">#FF669900</item> + <item name="darkorange" type="color">#FFFF8800</item> + <item name="darkred" type="color">#FFCC0000</item> + + <integer-array name="androidcolors"> + <item>@color/blue</item> + <item>@color/purple</item> + <item>@color/green</item> + <item>@color/orange</item> + <item>@color/red</item> + <item>@color/darkblue</item> + <item>@color/darkpurple</item> + <item>@color/darkgreen</item> + <item>@color/darkorange</item> + <item>@color/darkred</item> + </integer-array> + +</resources>
\ No newline at end of file diff --git a/samples/browseable/BasicMediaRouter/res/values/dimens.xml b/samples/browseable/BasicMediaRouter/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/BasicMediaRouter/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicMediaRouter/res/values/strings.xml b/samples/browseable/BasicMediaRouter/res/values/strings.xml new file mode 100644 index 000000000..40c023a34 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/res/values/strings.xml @@ -0,0 +1,9 @@ +<resources> + <string name="menu_present_to">Present to</string> + <string name="title_activity_main">MainActivity</string> + <string name="secondary_connected">Connected to:\n%s</string> + <string name="secondary_notconnected">No secondary display connected.</string> + <string name="change_color">Change Color</string> + <string name="display_name">This display is: %s</string> + <string name="display_color">Background color: #%X</string> +</resources>
\ No newline at end of file diff --git a/samples/browseable/BasicMediaRouter/res/values/styles.xml b/samples/browseable/BasicMediaRouter/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/BasicMediaRouter/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/BasicMediaRouter/src/com.example.android.basicmediarouter/MainActivity.java b/samples/browseable/BasicMediaRouter/src/com.example.android.basicmediarouter/MainActivity.java new file mode 100644 index 000000000..23b270981 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/src/com.example.android.basicmediarouter/MainActivity.java @@ -0,0 +1,300 @@ +/* + * 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.basicmediarouter; + +import android.app.Activity; +import android.app.MediaRouteActionProvider; +import android.content.Context; +import android.content.DialogInterface; +import android.media.MediaRouter; +import android.media.MediaRouter.RouteInfo; +import android.os.Bundle; +import android.view.Display; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.TextView; + +/** + * <p> + * This sample demonstrates the use of the MediaRouter API to show content on a + * secondary display using a {@link android.app.Presentation}. + * </p> + * <p> + * The activity uses the {@link android.media.MediaRouter} API to automatically detect when a + * presentation display is available and to allow the user to control the media + * routes using a menu item provided by the {@link android.app.MediaRouteActionProvider}. + * When a presentation display is available a {@link android.app.Presentation} (implemented + * as a {@link SamplePresentation}) is shown on the preferred display. A button + * toggles the background color of the secondary screen to show the interaction + * between the primary and secondary screens. + * </p> + * <p> + * This sample requires an HDMI or Wifi display. Alternatively, the + * "Simulate secondary displays" feature in Development Settings can be enabled + * to simulate secondary displays. + * </p> + * + * @see android.app.Presentation + * @see android.media.MediaRouter + */ +public class MainActivity extends Activity { + + private MediaRouter mMediaRouter; + + // Active Presentation, set to null if no secondary screen is enabled + private SamplePresentation mPresentation; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.sample_main); + mTextStatus = (TextView) findViewById(R.id.textStatus); + + // get the list of background colors + mColors = getResources().getIntArray(R.array.androidcolors); + + // Enable clicks on the 'change color' button + mButton = (Button) findViewById(R.id.button1); + mButton.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + showNextColor(); + } + }); + + // BEGIN_INCLUDE(getMediaRouter) + // Get the MediaRouter service + mMediaRouter = (MediaRouter) getSystemService(Context.MEDIA_ROUTER_SERVICE); + // END_INCLUDE(getMediaRouter) + } + + /** + * Implementing a {@link android.media.MediaRouter.Callback} to update the displayed + * {@link android.app.Presentation} when a route is selected, unselected or the + * presentation display has changed. The provided stub implementation + * {@link android.media.MediaRouter.SimpleCallback} is extended and only + * {@link android.media.MediaRouter.SimpleCallback#onRouteSelected(android.media.MediaRouter, int, android.media.MediaRouter.RouteInfo)} + * , + * {@link android.media.MediaRouter.SimpleCallback#onRouteUnselected(android.media.MediaRouter, int, android.media.MediaRouter.RouteInfo)} + * and + * {@link android.media.MediaRouter.SimpleCallback#onRoutePresentationDisplayChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo)} + * are overridden to update the displayed {@link android.app.Presentation} in + * {@link #updatePresentation()}. These callbacks enable or disable the + * second screen presentation based on the routing provided by the + * {@link android.media.MediaRouter} for {@link android.media.MediaRouter#ROUTE_TYPE_LIVE_VIDEO} + * streams. @ + */ + private final MediaRouter.SimpleCallback mMediaRouterCallback = + new MediaRouter.SimpleCallback() { + + // BEGIN_INCLUDE(SimpleCallback) + /** + * A new route has been selected as active. Disable the current + * route and enable the new one. + */ + @Override + public void onRouteSelected(MediaRouter router, int type, RouteInfo info) { + updatePresentation(); + } + + /** + * The route has been unselected. + */ + @Override + public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) { + updatePresentation(); + + } + + /** + * The route's presentation display has changed. This callback + * is called when the presentation has been activated, removed + * or its properties have changed. + */ + @Override + public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) { + updatePresentation(); + } + // END_INCLUDE(SimpleCallback) + }; + + /** + * Updates the displayed presentation to enable a secondary screen if it has + * been selected in the {@link android.media.MediaRouter} for the + * {@link android.media.MediaRouter#ROUTE_TYPE_LIVE_VIDEO} type. If no screen has been + * selected by the {@link android.media.MediaRouter}, the current screen is disabled. + * Otherwise a new {@link SamplePresentation} is initialized and shown on + * the secondary screen. + */ + private void updatePresentation() { + + // BEGIN_INCLUDE(updatePresentationInit) + // Get the selected route for live video + RouteInfo selectedRoute = mMediaRouter.getSelectedRoute( + MediaRouter.ROUTE_TYPE_LIVE_VIDEO); + + // Get its Display if a valid route has been selected + Display selectedDisplay = null; + if (selectedRoute != null) { + selectedDisplay = selectedRoute.getPresentationDisplay(); + } + // END_INCLUDE(updatePresentationInit) + + // BEGIN_INCLUDE(updatePresentationDismiss) + /* + * Dismiss the current presentation if the display has changed or no new + * route has been selected + */ + if (mPresentation != null && mPresentation.getDisplay() != selectedDisplay) { + mPresentation.dismiss(); + mPresentation = null; + mButton.setEnabled(false); + mTextStatus.setText(R.string.secondary_notconnected); + } + // END_INCLUDE(updatePresentationDismiss) + + // BEGIN_INCLUDE(updatePresentationNew) + /* + * Show a new presentation if the previous one has been dismissed and a + * route has been selected. + */ + if (mPresentation == null && selectedDisplay != null) { + + // Initialise a new Presentation for the Display + mPresentation = new SamplePresentation(this, selectedDisplay); + mPresentation.setOnDismissListener(mOnDismissListener); + + // Try to show the presentation, this might fail if the display has + // gone away in the mean time + try { + mPresentation.show(); + mTextStatus.setText(getResources().getString(R.string.secondary_connected, + selectedRoute.getName(MainActivity.this))); + mButton.setEnabled(true); + showNextColor(); + } catch (WindowManager.InvalidDisplayException ex) { + // Couldn't show presentation - display was already removed + mPresentation = null; + } + } + // END_INCLUDE(updatePresentationNew) + + } + + @Override + protected void onResume() { + super.onResume(); + + // BEGIN_INCLUDE(addCallback) + // Register a callback for all events related to live video devices + mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_LIVE_VIDEO, mMediaRouterCallback); + // END_INCLUDE(addCallback) + + // Show the 'Not connected' status message + mButton.setEnabled(false); + mTextStatus.setText(R.string.secondary_notconnected); + + // Update the displays based on the currently active routes + updatePresentation(); + } + + @Override + protected void onPause() { + super.onPause(); + + // BEGIN_INCLUDE(onPause) + // Stop listening for changes to media routes. + mMediaRouter.removeCallback(mMediaRouterCallback); + // END_INCLUDE(onPause) + } + + @Override + protected void onStop() { + super.onStop(); + + // BEGIN_INCLUDE(onStop) + // Dismiss the presentation when the activity is not visible. + if (mPresentation != null) { + mPresentation.dismiss(); + mPresentation = null; + } + // BEGIN_INCLUDE(onStop) + } + + /** + * Inflates the ActionBar or options menu. The menu file defines an item for + * the {@link android.app.MediaRouteActionProvider}, which is registered here for all + * live video devices using {@link android.media.MediaRouter#ROUTE_TYPE_LIVE_VIDEO}. + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + + getMenuInflater().inflate(R.menu.main, menu); + + // BEGIN_INCLUDE(MediaRouteActionProvider) + // Configure the media router action provider + MenuItem mediaRouteMenuItem = menu.findItem(R.id.menu_media_route); + MediaRouteActionProvider mediaRouteActionProvider = + (MediaRouteActionProvider) mediaRouteMenuItem.getActionProvider(); + mediaRouteActionProvider.setRouteTypes(MediaRouter.ROUTE_TYPE_LIVE_VIDEO); + // BEGIN_INCLUDE(MediaRouteActionProvider) + + return true; + } + + /** + * Listens for dismissal of the {@link SamplePresentation} and removes its + * reference. + */ + private final DialogInterface.OnDismissListener mOnDismissListener = + new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + if (dialog == mPresentation) { + mPresentation = null; + } + } + }; + + // Views used to display status information on the primary screen + private TextView mTextStatus; + private Button mButton; + + // selected color index + private int mColor = 0; + + // background colors + public int[] mColors; + + /** + * Displays the next color on the secondary screen if it is activate. + */ + private void showNextColor() { + if (mPresentation != null) { + // a second screen is active and initialized, show the next color + mPresentation.setColor(mColors[mColor]); + mColor = (mColor + 1) % mColors.length; + } + } + +} diff --git a/samples/browseable/BasicMediaRouter/src/com.example.android.basicmediarouter/SamplePresentation.java b/samples/browseable/BasicMediaRouter/src/com.example.android.basicmediarouter/SamplePresentation.java new file mode 100644 index 000000000..ac1f40f29 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/src/com.example.android.basicmediarouter/SamplePresentation.java @@ -0,0 +1,78 @@ +/* + * 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.basicmediarouter; + +import android.app.Presentation; +import android.content.Context; +import android.os.Bundle; +import android.view.Display; +import android.widget.LinearLayout; +import android.widget.TextView; + +/** + * <p> + * A {@link android.app.Presentation} used to demonstrate interaction between primary and + * secondary screens. + * </p> + * <p> + * It displays the name of the display in which it has been embedded (see + * {@link android.app.Presentation#getDisplay()}) and exposes a facility to change its + * background color and display its text. + * </p> + */ +public class SamplePresentation extends Presentation { + + private LinearLayout mLayout; + private TextView mText; + + public SamplePresentation(Context outerContext, Display display) { + super(outerContext, display); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Set the content view to the custom layout + setContentView(R.layout.display); + + // Get the Views + mLayout = (LinearLayout) findViewById(R.id.display_layout); + mText = (TextView) findViewById(R.id.display_text); + + /* + * Show the name of the display this presentation was embedded in. + */ + TextView smallText = (TextView) findViewById(R.id.display_smalltext); + final String name = getDisplay().getName(); + smallText.setText(getResources().getString(R.string.display_name, name)); + } + + /** + * Set the background color of the layout and display the color as a String. + * + * @param color The background color + */ + public void setColor(int color) { + mLayout.setBackgroundColor(color); + + // Display the color as a string on screen + String s = getResources().getString(R.string.display_color, color); + mText.setText(s); + } + +} diff --git a/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/Log.java b/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/LogFragment.java b/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/LogNode.java b/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/LogView.java b/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/LogWrapper.java b/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/MessageOnlyLogFilter.java b/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/BasicMediaRouter/src/com.example.android.common.logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/BasicMultitouch/AndroidManifest.xml b/samples/browseable/BasicMultitouch/AndroidManifest.xml new file mode 100644 index 000000000..043345c19 --- /dev/null +++ b/samples/browseable/BasicMultitouch/AndroidManifest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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" + package="com.example.android.basicmultitouch" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk + android:minSdkVersion="8" + android:targetSdkVersion="17" /> + + <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" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/samples/browseable/BasicMultitouch/_index.jd b/samples/browseable/BasicMultitouch/_index.jd new file mode 100644 index 000000000..056ee54b0 --- /dev/null +++ b/samples/browseable/BasicMultitouch/_index.jd @@ -0,0 +1,15 @@ + + + +page.tags="BasicMultitouch" +sample.group=Input +@jd:body + +<p> + +This samples demonstrates the use of MotionEvent properties to keep track of individual touches +across multiple touch events. +\n\nTouch the screen with multiple fingers to show that the pointer id +(also represented by a colour) does not change as new touch events are received.</string> + + </p> diff --git a/samples/browseable/BasicMultitouch/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicMultitouch/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..b1efaf4b2 --- /dev/null +++ b/samples/browseable/BasicMultitouch/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/BasicMultitouch/res/drawable-hdpi/tile.9.png b/samples/browseable/BasicMultitouch/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/BasicMultitouch/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/BasicMultitouch/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicMultitouch/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f5f9244f2 --- /dev/null +++ b/samples/browseable/BasicMultitouch/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/BasicMultitouch/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicMultitouch/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..5d07b3f06 --- /dev/null +++ b/samples/browseable/BasicMultitouch/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/BasicMultitouch/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicMultitouch/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6ef21e1f4 --- /dev/null +++ b/samples/browseable/BasicMultitouch/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/BasicMultitouch/res/layout/activity_main.xml b/samples/browseable/BasicMultitouch/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/BasicMultitouch/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/BasicMultitouch/res/layout/layout_mainactivity.xml b/samples/browseable/BasicMultitouch/res/layout/layout_mainactivity.xml new file mode 100644 index 000000000..539a43ad8 --- /dev/null +++ b/samples/browseable/BasicMultitouch/res/layout/layout_mainactivity.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" > + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:padding="75dp" + android:text="@string/intro_message" /> + + <com.example.android.basicmultitouch.TouchDisplayView + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + +</FrameLayout>
\ No newline at end of file diff --git a/samples/browseable/BasicMultitouch/res/values-sw600dp/dimens.xml b/samples/browseable/BasicMultitouch/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/BasicMultitouch/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicMultitouch/res/values-sw600dp/styles.xml b/samples/browseable/BasicMultitouch/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/BasicMultitouch/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/BasicMultitouch/res/values-v11/styles.xml b/samples/browseable/BasicMultitouch/res/values-v11/styles.xml new file mode 100644 index 000000000..9daaa2614 --- /dev/null +++ b/samples/browseable/BasicMultitouch/res/values-v11/styles.xml @@ -0,0 +1,27 @@ +<!-- + 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> + + <!-- + 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>
\ No newline at end of file diff --git a/samples/browseable/BasicMultitouch/res/values-v14/styles.xml b/samples/browseable/BasicMultitouch/res/values-v14/styles.xml new file mode 100644 index 000000000..42ce19387 --- /dev/null +++ b/samples/browseable/BasicMultitouch/res/values-v14/styles.xml @@ -0,0 +1,28 @@ +<!-- + 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> + + <!-- + Base application theme for API 14+. This theme completely replaces + AppBaseTheme from BOTH res/values/styles.xml and + res/values-v11/styles.xml on API 14+ devices. + --> + <style name="AppBaseTheme" parent="android:Theme.Holo.Light.NoActionBar"> + <!-- API 14 theme customizations can go here. --> + </style> + +</resources>
\ No newline at end of file diff --git a/samples/browseable/BasicMultitouch/res/values/base-strings.xml b/samples/browseable/BasicMultitouch/res/values/base-strings.xml new file mode 100644 index 000000000..a5388a7d6 --- /dev/null +++ b/samples/browseable/BasicMultitouch/res/values/base-strings.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">BasicMultitouch</string> + <string name="intro_message"> + <![CDATA[ + + +This samples demonstrates the use of MotionEvent properties to keep track of individual touches +across multiple touch events. +\n\nTouch the screen with multiple fingers to show that the pointer id +(also represented by a colour) does not change as new touch events are received.</string> + + + ]]> + </string> +</resources> diff --git a/samples/browseable/BasicMultitouch/res/values/dimens.xml b/samples/browseable/BasicMultitouch/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/BasicMultitouch/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicMultitouch/res/values/strings.xml b/samples/browseable/BasicMultitouch/res/values/strings.xml new file mode 100644 index 000000000..5765548c6 --- /dev/null +++ b/samples/browseable/BasicMultitouch/res/values/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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> + +</resources>
\ No newline at end of file diff --git a/samples/browseable/BasicMultitouch/res/values/styles.xml b/samples/browseable/BasicMultitouch/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/BasicMultitouch/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/BasicMultitouch/src/com.example.android.basicmultitouch/MainActivity.java b/samples/browseable/BasicMultitouch/src/com.example.android.basicmultitouch/MainActivity.java new file mode 100644 index 000000000..fc95a607f --- /dev/null +++ b/samples/browseable/BasicMultitouch/src/com.example.android.basicmultitouch/MainActivity.java @@ -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. + */ + +package com.example.android.basicmultitouch; + +import android.app.Activity; +import android.os.Bundle; + +/** + * This is an example of keeping track of individual touches across multiple + * {@link android.view.MotionEvent}s. + * <p> + * This is illustrated by a View ({@link TouchDisplayView}) that responds to + * touch events and draws coloured circles for each pointer, stores the last + * positions of this pointer and draws them. This example shows the relationship + * between MotionEvent indices, pointer identifiers and actions. + * + * @see android.view.MotionEvent + */ +public class MainActivity extends Activity { + TouchDisplayView mView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.layout_mainactivity); + } + +} diff --git a/samples/browseable/BasicMultitouch/src/com.example.android.basicmultitouch/Pools.java b/samples/browseable/BasicMultitouch/src/com.example.android.basicmultitouch/Pools.java new file mode 100644 index 000000000..0eda0ee02 --- /dev/null +++ b/samples/browseable/BasicMultitouch/src/com.example.android.basicmultitouch/Pools.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.basicmultitouch; + +/** + * Helper class for crating pools of objects. An example use looks like this: + * <pre> + * public class MyPooledClass { + * + * private static final SynchronizedPool<MyPooledClass> sPool = + * new SynchronizedPool<MyPooledClass>(10); + * + * public static MyPooledClass obtain() { + * MyPooledClass instance = sPool.acquire(); + * return (instance != null) ? instance : new MyPooledClass(); + * } + * + * public void recycle() { + * // Clear state if needed. + * sPool.release(this); + * } + * + * . . . + * } + * </pre> + * + * @hide + */ +public final class Pools { + + /** + * Interface for managing a pool of objects. + * + * @param <T> The pooled type. + */ + public static interface Pool<T> { + + /** + * @return An instance from the pool if such, null otherwise. + */ + public T acquire(); + + /** + * Release an instance to the pool. + * + * @param instance The instance to release. + * @return Whether the instance was put in the pool. + * + * @throws IllegalStateException If the instance is already in the pool. + */ + public boolean release(T instance); + } + + private Pools() { + /* do nothing - hiding constructor */ + } + + /** + * Simple (non-synchronized) pool of objects. + * + * @param <T> The pooled type. + */ + public static class SimplePool<T> implements Pool<T> { + private final Object[] mPool; + + private int mPoolSize; + + /** + * Creates a new instance. + * + * @param maxPoolSize The max pool size. + * + * @throws IllegalArgumentException If the max pool size is less than zero. + */ + public SimplePool(int maxPoolSize) { + if (maxPoolSize <= 0) { + throw new IllegalArgumentException("The max pool size must be > 0"); + } + mPool = new Object[maxPoolSize]; + } + + @Override + @SuppressWarnings("unchecked") + public T acquire() { + if (mPoolSize > 0) { + final int lastPooledIndex = mPoolSize - 1; + T instance = (T) mPool[lastPooledIndex]; + mPool[lastPooledIndex] = null; + mPoolSize--; + return instance; + } + return null; + } + + @Override + public boolean release(T instance) { + if (isInPool(instance)) { + throw new IllegalStateException("Already in the pool!"); + } + if (mPoolSize < mPool.length) { + mPool[mPoolSize] = instance; + mPoolSize++; + return true; + } + return false; + } + + private boolean isInPool(T instance) { + for (int i = 0; i < mPoolSize; i++) { + if (mPool[i] == instance) { + return true; + } + } + return false; + } + } + + /** + * Synchronized) pool of objects. + * + * @param <T> The pooled type. + */ + public static class SynchronizedPool<T> extends SimplePool<T> { + private final Object mLock = new Object(); + + /** + * Creates a new instance. + * + * @param maxPoolSize The max pool size. + * + * @throws IllegalArgumentException If the max pool size is less than zero. + */ + public SynchronizedPool(int maxPoolSize) { + super(maxPoolSize); + } + + @Override + public T acquire() { + synchronized (mLock) { + return super.acquire(); + } + } + + @Override + public boolean release(T element) { + synchronized (mLock) { + return super.release(element); + } + } + } +} diff --git a/samples/browseable/BasicMultitouch/src/com.example.android.basicmultitouch/TouchDisplayView.java b/samples/browseable/BasicMultitouch/src/com.example.android.basicmultitouch/TouchDisplayView.java new file mode 100644 index 000000000..78e6abea5 --- /dev/null +++ b/samples/browseable/BasicMultitouch/src/com.example.android.basicmultitouch/TouchDisplayView.java @@ -0,0 +1,401 @@ +/* + * 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.basicmultitouch; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PointF; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.view.MotionEvent; +import android.view.View; + +import com.example.android.basicmultitouch.Pools.SimplePool; + +/** + * View that shows touch events and their history. This view demonstrates the + * use of {@link #onTouchEvent(android.view.MotionEvent)} and {@link android.view.MotionEvent}s to keep + * track of touch pointers across events. + */ +public class TouchDisplayView extends View { + + // Hold data for active touch pointer IDs + private SparseArray<TouchHistory> mTouches; + + // Is there an active touch? + private boolean mHasTouch = false; + + /** + * Holds data related to a touch pointer, including its current position, + * pressure and historical positions. Objects are allocated through an + * object pool using {@link #obtain()} and {@link #recycle()} to reuse + * existing objects. + */ + static final class TouchHistory { + + // number of historical points to store + public static final int HISTORY_COUNT = 20; + + public float x; + public float y; + public float pressure = 0f; + public String label = null; + + // current position in history array + public int historyIndex = 0; + public int historyCount = 0; + + // arrray of pointer position history + public PointF[] history = new PointF[HISTORY_COUNT]; + + private static final int MAX_POOL_SIZE = 10; + private static final SimplePool<TouchHistory> sPool = + new SimplePool<TouchHistory>(MAX_POOL_SIZE); + + public static TouchHistory obtain(float x, float y, float pressure) { + TouchHistory data = sPool.acquire(); + if (data == null) { + data = new TouchHistory(); + } + + data.setTouch(x, y, pressure); + + return data; + } + + public TouchHistory() { + + // initialise history array + for (int i = 0; i < HISTORY_COUNT; i++) { + history[i] = new PointF(); + } + } + + public void setTouch(float x, float y, float pressure) { + this.x = x; + this.y = y; + this.pressure = pressure; + } + + public void recycle() { + this.historyIndex = 0; + this.historyCount = 0; + sPool.release(this); + } + + /** + * Add a point to its history. Overwrites oldest point if the maximum + * number of historical points is already stored. + * + * @param point + */ + public void addHistory(float x, float y) { + PointF p = history[historyIndex]; + p.x = x; + p.y = y; + + historyIndex = (historyIndex + 1) % history.length; + + if (historyCount < HISTORY_COUNT) { + historyCount++; + } + } + + } + + public TouchDisplayView(Context context, AttributeSet attrs) { + super(context, attrs); + + // SparseArray for touch events, indexed by touch id + mTouches = new SparseArray<TouchHistory>(10); + + initialisePaint(); + } + + // BEGIN_INCLUDE(onTouchEvent) + @Override + public boolean onTouchEvent(MotionEvent event) { + + final int action = event.getAction(); + + /* + * Switch on the action. The action is extracted from the event by + * applying the MotionEvent.ACTION_MASK. Alternatively a call to + * event.getActionMasked() would yield in the action as well. + */ + switch (action & MotionEvent.ACTION_MASK) { + + case MotionEvent.ACTION_DOWN: { + // first pressed gesture has started + + /* + * Only one touch event is stored in the MotionEvent. Extract + * the pointer identifier of this touch from the first index + * within the MotionEvent object. + */ + int id = event.getPointerId(0); + + TouchHistory data = TouchHistory.obtain(event.getX(0), event.getY(0), + event.getPressure(0)); + data.label = "id: " + 0; + + /* + * Store the data under its pointer identifier. The pointer + * number stays consistent for the duration of a gesture, + * accounting for other pointers going up or down. + */ + mTouches.put(id, data); + + mHasTouch = true; + + break; + } + + case MotionEvent.ACTION_POINTER_DOWN: { + /* + * A non-primary pointer has gone down, after an event for the + * primary pointer (ACTION_DOWN) has already been received. + */ + + /* + * The MotionEvent object contains multiple pointers. Need to + * extract the index at which the data for this particular event + * is stored. + */ + int index = event.getActionIndex(); + int id = event.getPointerId(index); + + TouchHistory data = TouchHistory.obtain(event.getX(index), event.getY(index), + event.getPressure(index)); + data.label = "id: " + id; + + /* + * Store the data under its pointer identifier. The index of + * this pointer can change over multiple events, but this + * pointer is always identified by the same identifier for this + * active gesture. + */ + mTouches.put(id, data); + + break; + } + + case MotionEvent.ACTION_UP: { + /* + * Final pointer has gone up and has ended the last pressed + * gesture. + */ + + /* + * Extract the pointer identifier for the only event stored in + * the MotionEvent object and remove it from the list of active + * touches. + */ + int id = event.getPointerId(0); + TouchHistory data = mTouches.get(id); + mTouches.remove(id); + data.recycle(); + + mHasTouch = false; + + break; + } + + case MotionEvent.ACTION_POINTER_UP: { + /* + * A non-primary pointer has gone up and other pointers are + * still active. + */ + + /* + * The MotionEvent object contains multiple pointers. Need to + * extract the index at which the data for this particular event + * is stored. + */ + int index = event.getActionIndex(); + int id = event.getPointerId(index); + + TouchHistory data = mTouches.get(id); + mTouches.remove(id); + data.recycle(); + + break; + } + + case MotionEvent.ACTION_MOVE: { + /* + * A change event happened during a pressed gesture. (Between + * ACTION_DOWN and ACTION_UP or ACTION_POINTER_DOWN and + * ACTION_POINTER_UP) + */ + + /* + * Loop through all active pointers contained within this event. + * Data for each pointer is stored in a MotionEvent at an index + * (starting from 0 up to the number of active pointers). This + * loop goes through each of these active pointers, extracts its + * data (position and pressure) and updates its stored data. A + * pointer is identified by its pointer number which stays + * constant across touch events as long as it remains active. + * This identifier is used to keep track of a pointer across + * events. + */ + for (int index = 0; index < event.getPointerCount(); index++) { + // get pointer id for data stored at this index + int id = event.getPointerId(index); + + // get the data stored externally about this pointer. + TouchHistory data = mTouches.get(id); + + // add previous position to history and add new values + data.addHistory(data.x, data.y); + data.setTouch(event.getX(index), event.getY(index), + event.getPressure(index)); + + } + + break; + } + } + + // trigger redraw on UI thread + this.postInvalidate(); + + return true; + } + + // END_INCLUDE(onTouchEvent) + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + // Canvas background color depends on whether there is an active touch + if (mHasTouch) { + canvas.drawColor(BACKGROUND_ACTIVE); + } else { + // draw inactive border + canvas.drawRect(mBorderWidth, mBorderWidth, getWidth() - mBorderWidth, getHeight() + - mBorderWidth, mBorderPaint); + } + + // loop through all active touches and draw them + for (int i = 0; i < mTouches.size(); i++) { + + // get the pointer id and associated data for this index + int id = mTouches.keyAt(i); + TouchHistory data = mTouches.valueAt(i); + + // draw the data and its history to the canvas + drawCircle(canvas, id, data); + } + } + + /* + * Below are only helper methods and variables required for drawing. + */ + + // radius of active touch circle in dp + private static final float CIRCLE_RADIUS_DP = 75f; + // radius of historical circle in dp + private static final float CIRCLE_HISTORICAL_RADIUS_DP = 7f; + + // calculated radiuses in px + private float mCircleRadius; + private float mCircleHistoricalRadius; + + private Paint mCirclePaint = new Paint(); + private Paint mTextPaint = new Paint(); + + private static final int BACKGROUND_ACTIVE = Color.WHITE; + + // inactive border + private static final float INACTIVE_BORDER_DP = 15f; + private static final int INACTIVE_BORDER_COLOR = 0xFFffd060; + private Paint mBorderPaint = new Paint(); + private float mBorderWidth; + + public final int[] COLORS = { + 0xFF33B5E5, 0xFFAA66CC, 0xFF99CC00, 0xFFFFBB33, 0xFFFF4444, + 0xFF0099CC, 0xFF9933CC, 0xFF669900, 0xFFFF8800, 0xFFCC0000 + }; + + /** + * Sets up the required {@link android.graphics.Paint} objects for the screen density of this + * device. + */ + private void initialisePaint() { + + // Calculate radiuses in px from dp based on screen density + float density = getResources().getDisplayMetrics().density; + mCircleRadius = CIRCLE_RADIUS_DP * density; + mCircleHistoricalRadius = CIRCLE_HISTORICAL_RADIUS_DP * density; + + // Setup text paint for circle label + mTextPaint.setTextSize(27f); + mTextPaint.setColor(Color.BLACK); + + // Setup paint for inactive border + mBorderWidth = INACTIVE_BORDER_DP * density; + mBorderPaint.setStrokeWidth(mBorderWidth); + mBorderPaint.setColor(INACTIVE_BORDER_COLOR); + mBorderPaint.setStyle(Paint.Style.STROKE); + + } + + /** + * Draws the data encapsulated by a {@link TouchDisplayView.TouchHistory} object to a canvas. + * A large circle indicates the current position held by the + * {@link TouchDisplayView.TouchHistory} object, while a smaller circle is drawn for each + * entry in its history. The size of the large circle is scaled depending on + * its pressure, clamped to a maximum of <code>1.0</code>. + * + * @param canvas + * @param id + * @param data + */ + protected void drawCircle(Canvas canvas, int id, TouchHistory data) { + // select the color based on the id + int color = COLORS[id % COLORS.length]; + mCirclePaint.setColor(color); + + /* + * Draw the circle, size scaled to its pressure. Pressure is clamped to + * 1.0 max to ensure proper drawing. (Reported pressure values can + * exceed 1.0, depending on the calibration of the touch screen). + */ + float pressure = Math.min(data.pressure, 1f); + float radius = pressure * mCircleRadius; + + canvas.drawCircle(data.x, (data.y) - (radius / 2f), radius, + mCirclePaint); + + // draw all historical points with a lower alpha value + mCirclePaint.setAlpha(125); + for (int j = 0; j < data.history.length && j < data.historyCount; j++) { + PointF p = data.history[j]; + canvas.drawCircle(p.x, p.y, mCircleHistoricalRadius, mCirclePaint); + } + + // draw its label next to the main circle + canvas.drawText(data.label, data.x + radius, data.y + - radius, mTextPaint); + } + +} diff --git a/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/Log.java b/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/LogFragment.java b/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/LogNode.java b/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/LogView.java b/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/LogWrapper.java b/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/MessageOnlyLogFilter.java b/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/BasicMultitouch/src/com.example.android.common.logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/BasicNetworking/AndroidManifest.xml b/samples/browseable/BasicNetworking/AndroidManifest.xml new file mode 100644 index 000000000..48bfeac46 --- /dev/null +++ b/samples/browseable/BasicNetworking/AndroidManifest.xml @@ -0,0 +1,44 @@ +<!-- + 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" + package="com.example.android.basicnetworking" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" /> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + + <application + android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@style/Theme.Sample" + android:allowBackup="true"> + + <activity + android:name="com.example.android.basicnetworking.MainActivity" + android:label="@string/app_name" + android:uiOptions="splitActionBarWhenNarrow"> + + <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/browseable/BasicNetworking/_index.jd b/samples/browseable/BasicNetworking/_index.jd new file mode 100644 index 000000000..6f5479237 --- /dev/null +++ b/samples/browseable/BasicNetworking/_index.jd @@ -0,0 +1,15 @@ + + + +page.tags="BasicNetworking" +sample.group=Connectivity +@jd:body + +<p> + + This sample demonstrates how to use the ConnectivityManager to determine if you have + a network connection, and if so, what type of connection it is. + \n\nA "NetworkInfo" object is retrieved from the ConnectivityManager, which contains information + on the active connection, and then the connection type is printed to an on-screen console. + + </p> diff --git a/samples/browseable/BasicNetworking/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicNetworking/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..22ce60611 --- /dev/null +++ b/samples/browseable/BasicNetworking/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/BasicNetworking/res/drawable-hdpi/tile.9.png b/samples/browseable/BasicNetworking/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/BasicNetworking/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/BasicNetworking/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicNetworking/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..f21e17b65 --- /dev/null +++ b/samples/browseable/BasicNetworking/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/BasicNetworking/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicNetworking/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..64b805902 --- /dev/null +++ b/samples/browseable/BasicNetworking/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/BasicNetworking/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicNetworking/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..6b4434a87 --- /dev/null +++ b/samples/browseable/BasicNetworking/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/BasicNetworking/res/layout/activity_main.xml b/samples/browseable/BasicNetworking/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/BasicNetworking/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/BasicNetworking/res/layout/sample_main.xml b/samples/browseable/BasicNetworking/res/layout/sample_main.xml new file mode 100755 index 000000000..ccbe6ae31 --- /dev/null +++ b/samples/browseable/BasicNetworking/res/layout/sample_main.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + 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. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + <fragment + android:name="com.example.android.basicnetworking.SimpleTextFragment" + android:id="@+id/intro_fragment" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + <View + android:layout_width="fill_parent" + android:layout_height="1dp" + android:background="@android:color/darker_gray"/> + <fragment + android:name="com.example.android.common.logger.LogFragment" + android:id="@+id/log_fragment" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" /> +</LinearLayout> diff --git a/samples/browseable/BasicNetworking/res/menu/main.xml b/samples/browseable/BasicNetworking/res/menu/main.xml new file mode 100644 index 000000000..f05f16a58 --- /dev/null +++ b/samples/browseable/BasicNetworking/res/menu/main.xml @@ -0,0 +1,24 @@ +<!-- + 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. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/test_action" + android:showAsAction="ifRoom|withText" + android:title="@string/test_text" /> + <item android:id="@+id/clear_action" + android:showAsAction="ifRoom|withText" + android:title="@string/clear_text" /> +</menu> diff --git a/samples/browseable/BasicNetworking/res/values-sw600dp/dimens.xml b/samples/browseable/BasicNetworking/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/BasicNetworking/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicNetworking/res/values-sw600dp/styles.xml b/samples/browseable/BasicNetworking/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/BasicNetworking/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/BasicNetworking/res/values/base-strings.xml b/samples/browseable/BasicNetworking/res/values/base-strings.xml new file mode 100644 index 000000000..668bcdfb6 --- /dev/null +++ b/samples/browseable/BasicNetworking/res/values/base-strings.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">BasicNetworking</string> + <string name="intro_message"> + <![CDATA[ + + + This sample demonstrates how to use the ConnectivityManager to determine if you have + a network connection, and if so, what type of connection it is. + \n\nA "NetworkInfo" object is retrieved from the ConnectivityManager, which contains information + on the active connection, and then the connection type is printed to an on-screen console. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/BasicNetworking/res/values/dimens.xml b/samples/browseable/BasicNetworking/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/BasicNetworking/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicNetworking/res/values/strings.xml b/samples/browseable/BasicNetworking/res/values/strings.xml new file mode 100755 index 000000000..d39460a4b --- /dev/null +++ b/samples/browseable/BasicNetworking/res/values/strings.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. + --> + +<resources> + <string name="test_text">Test</string> + <string name="clear_text">Clear</string> + <string name="wifi_connection">The active connection is wifi.</string> + <string name="mobile_connection">The active connection is mobile.</string> + <string name="no_wifi_or_mobile">No wireless or mobile connection.</string> +</resources> diff --git a/samples/browseable/BasicNetworking/res/values/styles.xml b/samples/browseable/BasicNetworking/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/BasicNetworking/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/BasicNetworking/src/com.example.android.basicnetworking/MainActivity.java b/samples/browseable/BasicNetworking/src/com.example.android.basicnetworking/MainActivity.java new file mode 100755 index 000000000..39ed4b1b0 --- /dev/null +++ b/samples/browseable/BasicNetworking/src/com.example.android.basicnetworking/MainActivity.java @@ -0,0 +1,131 @@ +/* + * 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.basicnetworking; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.util.TypedValue; +import android.view.Menu; +import android.view.MenuItem; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogFragment; +import com.example.android.common.logger.LogWrapper; +import com.example.android.common.logger.MessageOnlyLogFilter; + +/** + * Sample application demonstrating how to test whether a device is connected, + * and if so, whether the connection happens to be wifi or mobile (it could be + * something else). + * + * This sample uses the logging framework to display log output in the log + * fragment (LogFragment). + */ +public class MainActivity extends FragmentActivity { + + public static final String TAG = "Basic Network Demo"; + // Whether there is a Wi-Fi connection. + private static boolean wifiConnected = false; + // Whether there is a mobile connection. + private static boolean mobileConnected = false; + + // Reference to the fragment showing events, so we can clear it with a button + // as necessary. + private LogFragment mLogFragment; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.sample_main); + + // Initialize text fragment that displays intro text. + SimpleTextFragment introFragment = (SimpleTextFragment) + getSupportFragmentManager().findFragmentById(R.id.intro_fragment); + introFragment.setText(R.string.intro_message); + introFragment.getTextView().setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16.0f); + + // Initialize the logging framework. + initializeLogging(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + // When the user clicks TEST, display the connection status. + case R.id.test_action: + checkNetworkConnection(); + return true; + // Clear the log view fragment. + case R.id.clear_action: + mLogFragment.getLogView().setText(""); + return true; + } + return false; + } + + /** + * Check whether the device is connected, and if so, whether the connection + * is wifi or mobile (it could be something else). + */ + private void checkNetworkConnection() { + // BEGIN_INCLUDE(connect) + ConnectivityManager connMgr = + (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo activeInfo = connMgr.getActiveNetworkInfo(); + if (activeInfo != null && activeInfo.isConnected()) { + wifiConnected = activeInfo.getType() == ConnectivityManager.TYPE_WIFI; + mobileConnected = activeInfo.getType() == ConnectivityManager.TYPE_MOBILE; + if(wifiConnected) { + Log.i(TAG, getString(R.string.wifi_connection)); + } else if (mobileConnected){ + Log.i(TAG, getString(R.string.mobile_connection)); + } + } else { + Log.i(TAG, getString(R.string.no_wifi_or_mobile)); + } + // END_INCLUDE(connect) + } + + /** Create a chain of targets that will receive log data */ + public void initializeLogging() { + + // Using Log, front-end to the logging chain, emulates + // android.util.log method signatures. + + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + // A filter that strips out everything except the message text. + MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); + logWrapper.setNext(msgFilter); + + // On screen logging via a fragment with a TextView. + mLogFragment = + (LogFragment) getSupportFragmentManager().findFragmentById(R.id.log_fragment); + msgFilter.setNext(mLogFragment.getLogView()); + } +} diff --git a/samples/browseable/BasicNetworking/src/com.example.android.basicnetworking/SimpleTextFragment.java b/samples/browseable/BasicNetworking/src/com.example.android.basicnetworking/SimpleTextFragment.java new file mode 100644 index 000000000..c6d409cbb --- /dev/null +++ b/samples/browseable/BasicNetworking/src/com.example.android.basicnetworking/SimpleTextFragment.java @@ -0,0 +1,99 @@ +/* + * 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.basicnetworking; + +import android.os.Bundle; + +import android.support.v4.app.Fragment; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +/** + * Simple fragment containing only a TextView. Used by TextPagerAdapter to create + * tutorial-style pages for apps. + */ +public class SimpleTextFragment extends Fragment { + + // Contains the text that will be displayed by this Fragment + String mText; + + // Contains a resource ID for the text that will be displayed by this fragment. + int mTextId = -1; + + // Keys which will be used to store/retrieve text passed in via setArguments. + public static final String TEXT_KEY = "text"; + public static final String TEXT_ID_KEY = "text_id"; + + // For situations where the app wants to modify text at Runtime, exposing the TextView. + private TextView mTextView; + + public SimpleTextFragment() { + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Before initializing the textView, check if any arguments were provided via setArguments. + processArguments(); + + // Create a new TextView and set its text to whatever was provided. + mTextView = new TextView(getActivity()); + mTextView.setGravity(Gravity.CENTER); + + if (mText != null) { + mTextView.setText(mText); + Log.i("SimpleTextFragment", mText); + } + return mTextView; + } + + public TextView getTextView() { + return mTextView; + } + + /** + * Changes the text for this TextView, according to the resource ID provided. + * @param stringId A resource ID representing the text content for this Fragment's TextView. + */ + public void setText(int stringId) { + getTextView().setText(getActivity().getString(stringId)); + } + + /** + * Processes the arguments passed into this Fragment via setArguments method. + * Currently the method only looks for text or a textID, nothing else. + */ + public void processArguments() { + // For most objects we'd handle the multiple possibilities for initialization variables + // as multiple constructors. For Fragments, however, it's customary to use + // setArguments / getArguments. + if (getArguments() != null) { + Bundle args = getArguments(); + if (args.containsKey(TEXT_KEY)) { + mText = args.getString(TEXT_KEY); + Log.d("Constructor", "Added Text."); + } else if (args.containsKey(TEXT_ID_KEY)) { + mTextId = args.getInt(TEXT_ID_KEY); + mText = getString(mTextId); + } + } + } +}
\ No newline at end of file diff --git a/samples/browseable/BasicNetworking/src/com.example.android.common.logger/Log.java b/samples/browseable/BasicNetworking/src/com.example.android.common.logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/BasicNetworking/src/com.example.android.common.logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/BasicNetworking/src/com.example.android.common.logger/LogFragment.java b/samples/browseable/BasicNetworking/src/com.example.android.common.logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/BasicNetworking/src/com.example.android.common.logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/BasicNetworking/src/com.example.android.common.logger/LogNode.java b/samples/browseable/BasicNetworking/src/com.example.android.common.logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/BasicNetworking/src/com.example.android.common.logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/BasicNetworking/src/com.example.android.common.logger/LogView.java b/samples/browseable/BasicNetworking/src/com.example.android.common.logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/BasicNetworking/src/com.example.android.common.logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/BasicNetworking/src/com.example.android.common.logger/LogWrapper.java b/samples/browseable/BasicNetworking/src/com.example.android.common.logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/BasicNetworking/src/com.example.android.common.logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/BasicNetworking/src/com.example.android.common.logger/MessageOnlyLogFilter.java b/samples/browseable/BasicNetworking/src/com.example.android.common.logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/BasicNetworking/src/com.example.android.common.logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/BasicNotifications/AndroidManifest.xml b/samples/browseable/BasicNotifications/AndroidManifest.xml new file mode 100644 index 000000000..1e37d0bf6 --- /dev/null +++ b/samples/browseable/BasicNotifications/AndroidManifest.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.basicnotifications" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk + android:minSdkVersion="8" + android:targetSdkVersion="17"/> + + <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"> + <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/browseable/BasicNotifications/_index.jd b/samples/browseable/BasicNotifications/_index.jd new file mode 100644 index 000000000..d545068e7 --- /dev/null +++ b/samples/browseable/BasicNotifications/_index.jd @@ -0,0 +1,14 @@ + + + +page.tags="BasicNotifications" +sample.group=UI +@jd:body + +<p> + + This sample demonstrates how to display events in the system\'s notification bar. The + NotificationCompat API is used for compatibility with older devices, running Android + 2.2 (Froyo) or newer. + + </p> diff --git a/samples/browseable/BasicNotifications/res/drawable-hdpi-v11/ic_stat_notification.png b/samples/browseable/BasicNotifications/res/drawable-hdpi-v11/ic_stat_notification.png Binary files differnew file mode 100644 index 000000000..604d3a394 --- /dev/null +++ b/samples/browseable/BasicNotifications/res/drawable-hdpi-v11/ic_stat_notification.png diff --git a/samples/browseable/BasicNotifications/res/drawable-hdpi-v9/ic_stat_notification.png b/samples/browseable/BasicNotifications/res/drawable-hdpi-v9/ic_stat_notification.png Binary files differnew file mode 100644 index 000000000..5c86ffd03 --- /dev/null +++ b/samples/browseable/BasicNotifications/res/drawable-hdpi-v9/ic_stat_notification.png diff --git a/samples/browseable/BasicNotifications/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicNotifications/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..743382b99 --- /dev/null +++ b/samples/browseable/BasicNotifications/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/BasicNotifications/res/drawable-hdpi/ic_stat_notification.png b/samples/browseable/BasicNotifications/res/drawable-hdpi/ic_stat_notification.png Binary files differnew file mode 100644 index 000000000..f39c3b337 --- /dev/null +++ b/samples/browseable/BasicNotifications/res/drawable-hdpi/ic_stat_notification.png diff --git a/samples/browseable/BasicNotifications/res/drawable-hdpi/tile.9.png b/samples/browseable/BasicNotifications/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/BasicNotifications/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/BasicNotifications/res/drawable-ldpi-v11/ic_stat_notification.png b/samples/browseable/BasicNotifications/res/drawable-ldpi-v11/ic_stat_notification.png Binary files differnew file mode 100644 index 000000000..b919a32e0 --- /dev/null +++ b/samples/browseable/BasicNotifications/res/drawable-ldpi-v11/ic_stat_notification.png diff --git a/samples/browseable/BasicNotifications/res/drawable-ldpi-v9/ic_stat_notification.png b/samples/browseable/BasicNotifications/res/drawable-ldpi-v9/ic_stat_notification.png Binary files differnew file mode 100644 index 000000000..1ce3e7e88 --- /dev/null +++ b/samples/browseable/BasicNotifications/res/drawable-ldpi-v9/ic_stat_notification.png diff --git a/samples/browseable/BasicNotifications/res/drawable-mdpi-v11/ic_stat_notification.png b/samples/browseable/BasicNotifications/res/drawable-mdpi-v11/ic_stat_notification.png Binary files differnew file mode 100644 index 000000000..3f4d4a6f1 --- /dev/null +++ b/samples/browseable/BasicNotifications/res/drawable-mdpi-v11/ic_stat_notification.png diff --git a/samples/browseable/BasicNotifications/res/drawable-mdpi-v9/ic_stat_notification.png b/samples/browseable/BasicNotifications/res/drawable-mdpi-v9/ic_stat_notification.png Binary files differnew file mode 100644 index 000000000..1dbd17c3e --- /dev/null +++ b/samples/browseable/BasicNotifications/res/drawable-mdpi-v9/ic_stat_notification.png diff --git a/samples/browseable/BasicNotifications/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicNotifications/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..46a51a45d --- /dev/null +++ b/samples/browseable/BasicNotifications/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/BasicNotifications/res/drawable-mdpi/ic_stat_notification.png b/samples/browseable/BasicNotifications/res/drawable-mdpi/ic_stat_notification.png Binary files differnew file mode 100644 index 000000000..eafbbd12c --- /dev/null +++ b/samples/browseable/BasicNotifications/res/drawable-mdpi/ic_stat_notification.png diff --git a/samples/browseable/BasicNotifications/res/drawable-xhdpi-v11/ic_stat_notification.png b/samples/browseable/BasicNotifications/res/drawable-xhdpi-v11/ic_stat_notification.png Binary files differnew file mode 100644 index 000000000..d2317d7ba --- /dev/null +++ b/samples/browseable/BasicNotifications/res/drawable-xhdpi-v11/ic_stat_notification.png diff --git a/samples/browseable/BasicNotifications/res/drawable-xhdpi-v9/ic_stat_notification.png b/samples/browseable/BasicNotifications/res/drawable-xhdpi-v9/ic_stat_notification.png Binary files differnew file mode 100644 index 000000000..7011de877 --- /dev/null +++ b/samples/browseable/BasicNotifications/res/drawable-xhdpi-v9/ic_stat_notification.png diff --git a/samples/browseable/BasicNotifications/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicNotifications/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f97cef381 --- /dev/null +++ b/samples/browseable/BasicNotifications/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/BasicNotifications/res/drawable-xhdpi/ic_stat_notification.png b/samples/browseable/BasicNotifications/res/drawable-xhdpi/ic_stat_notification.png Binary files differnew file mode 100644 index 000000000..1f0d6523e --- /dev/null +++ b/samples/browseable/BasicNotifications/res/drawable-xhdpi/ic_stat_notification.png diff --git a/samples/browseable/BasicNotifications/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicNotifications/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..17fa1306a --- /dev/null +++ b/samples/browseable/BasicNotifications/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/BasicNotifications/res/layout/activity_main.xml b/samples/browseable/BasicNotifications/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/BasicNotifications/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/BasicNotifications/res/layout/sample_layout.xml b/samples/browseable/BasicNotifications/res/layout/sample_layout.xml new file mode 100644 index 000000000..4b0adf611 --- /dev/null +++ b/samples/browseable/BasicNotifications/res/layout/sample_layout.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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. +--> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/coreLayout" + android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/description" + android:id="@+id/description" + android:scrollbars="vertical" + android:layout_gravity="fill_vertical" + android:layout_weight="1"/> + + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Do it!" + android:id="@+id/button" + android:layout_gravity="center" + android:onClick="sendNotification"/> + +</LinearLayout> diff --git a/samples/browseable/BasicNotifications/res/values-sw600dp/dimens.xml b/samples/browseable/BasicNotifications/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/BasicNotifications/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicNotifications/res/values-sw600dp/styles.xml b/samples/browseable/BasicNotifications/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/BasicNotifications/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/BasicNotifications/res/values/base-strings.xml b/samples/browseable/BasicNotifications/res/values/base-strings.xml new file mode 100644 index 000000000..15dafec61 --- /dev/null +++ b/samples/browseable/BasicNotifications/res/values/base-strings.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">BasicNotifications</string> + <string name="intro_message"> + <![CDATA[ + + + This sample demonstrates how to display events in the system\'s notification bar. The + NotificationCompat API is used for compatibility with older devices, running Android + 2.2 (Froyo) or newer. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/BasicNotifications/res/values/dimens.xml b/samples/browseable/BasicNotifications/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/BasicNotifications/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicNotifications/res/values/strings.xml b/samples/browseable/BasicNotifications/res/values/strings.xml new file mode 100644 index 000000000..075f681f4 --- /dev/null +++ b/samples/browseable/BasicNotifications/res/values/strings.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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="description"> + Tap the button below to send a notification.\n\nThe notification\'s icon will immediately + appear in the notification bar. Drag the notification bar open to see the full + notification. Depending on which version of Android you\'re running, the full + notification will display an icon and two or three lines of text. + \n\nTap the notification to execute the notification\'s \"action,\" which is an intent + that we associate with the notification when it\'s created. This sample notification\'s + action is to send a browse intent with the url of the Notification docs on + developer.android.com. + \n\nThis sample uses the NotificationCompat API for maximum compatibility with versions + of Android from Froyo (Android 2.2) to the present. Try it on devices or AVDs that are + running different versions of Android. You\'ll see that while the presentation varies + slightly, the basic functionality is the same. NotificationCompat automatically takes + advantage of the expanded notification format in newer versions of Android, and + falls back gracefully to a reduced set of functionality on earlier versions. + </string> + +</resources> diff --git a/samples/browseable/BasicNotifications/res/values/styles.xml b/samples/browseable/BasicNotifications/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/BasicNotifications/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/BasicNotifications/src/com.example.android.basicnotifications/MainActivity.java b/samples/browseable/BasicNotifications/src/com.example.android.basicnotifications/MainActivity.java new file mode 100644 index 000000000..3e78c3425 --- /dev/null +++ b/samples/browseable/BasicNotifications/src/com.example.android.basicnotifications/MainActivity.java @@ -0,0 +1,101 @@ +package com.example.android.basicnotifications; + +import android.app.Activity; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Intent; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.NotificationCompat; +import android.view.View; + +/** + * The entry point to the BasicNotification sample. + */ +public class MainActivity extends Activity { + /** + * A numeric value that identifies the notification that we'll be sending. + * This value needs to be unique within this app, but it doesn't need to be + * unique system-wide. + */ + public static final int NOTIFICATION_ID = 1; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.sample_layout); + + } + + /** + * Send a sample notification using the NotificationCompat API. + */ + public void sendNotification(View view) { + + // BEGIN_INCLUDE(build_action) + /** Create an intent that will be fired when the user clicks the notification. + * The intent needs to be packaged into a {@link android.app.PendingIntent} so that the + * notification service can fire it on our behalf. + */ + Intent intent = new Intent(Intent.ACTION_VIEW, + Uri.parse("http://developer.android.com/reference/android/app/Notification.html")); + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0); + // END_INCLUDE(build_action) + + // BEGIN_INCLUDE (build_notification) + /** + * Use NotificationCompat.Builder to set up our notification. + */ + NotificationCompat.Builder builder = new NotificationCompat.Builder(this); + + /** Set the icon that will appear in the notification bar. This icon also appears + * in the lower right hand corner of the notification itself. + * + * Important note: although you can use any drawable as the small icon, Android + * design guidelines state that the icon should be simple and monochrome. Full-color + * bitmaps or busy images don't render well on smaller screens and can end up + * confusing the user. + */ + builder.setSmallIcon(R.drawable.ic_stat_notification); + + // Set the intent that will fire when the user taps the notification. + builder.setContentIntent(pendingIntent); + + // Set the notification to auto-cancel. This means that the notification will disappear + // after the user taps it, rather than remaining until it's explicitly dismissed. + builder.setAutoCancel(true); + + /** + *Build the notification's appearance. + * Set the large icon, which appears on the left of the notification. In this + * sample we'll set the large icon to be the same as our app icon. The app icon is a + * reasonable default if you don't have anything more compelling to use as an icon. + */ + builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher)); + + /** + * Set the text of the notification. This sample sets the three most commononly used + * text areas: + * 1. The content title, which appears in large type at the top of the notification + * 2. The content text, which appears in smaller text below the title + * 3. The subtext, which appears under the text on newer devices. Devices running + * versions of Android prior to 4.2 will ignore this field, so don't use it for + * anything vital! + */ + builder.setContentTitle("BasicNotifications Sample"); + builder.setContentText("Time to learn about notifications!"); + builder.setSubText("Tap to view documentation about notifications."); + + // END_INCLUDE (build_notification) + + // BEGIN_INCLUDE(send_notification) + /** + * Send the notification. This will immediately display the notification icon in the + * notification bar. + */ + NotificationManager notificationManager = (NotificationManager) getSystemService( + NOTIFICATION_SERVICE); + notificationManager.notify(NOTIFICATION_ID, builder.build()); + // END_INCLUDE(send_notification) + } +} diff --git a/samples/browseable/BasicSyncAdapter/AndroidManifest.xml b/samples/browseable/BasicSyncAdapter/AndroidManifest.xml new file mode 100644 index 000000000..dd59b066a --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/AndroidManifest.xml @@ -0,0 +1,102 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.basicsyncadapter" + android:versionCode="1" + android:versionName="1.0"> + + <!-- SyncAdapters are available in API 5 and above. We use API 7 as a baseline for samples. --> + <uses-sdk + android:minSdkVersion="7" + android:targetSdkVersion="17" /> + + <!-- Required for fetching feed data. --> + <uses-permission android:name="android.permission.INTERNET"/> + <!-- Required to register a SyncStatusObserver to display a "syncing..." progress indicator. --> + <uses-permission android:name="android.permission.READ_SYNC_STATS"/> + <!-- Required to enable our SyncAdapter after it's created. --> + <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/> + <!-- Required because we're manually creating a new account. --> + <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/> + + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + + <!-- Main activity, responsible for showing a list of feed entries. --> + <activity + android:name=".EntryListActivity" + android:label="@string/app_name" > + <!-- This intent filter places this activity in the system's app launcher. --> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <!-- ContentProvider to store feed data. + + The "authorities" here are defined as part of a ContentProvider interface. It's used here + as an attachment point for the SyncAdapter. See res/xml/syncadapter.xml and + SyncService.java. + + Since this ContentProvider is not exported, it will not be accessible outside of this app's + package. --> + <provider + android:name=".provider.FeedProvider" + android:authorities="com.example.android.basicsyncadapter" + android:exported="false" /> + + <!-- This service implements our SyncAdapter. It needs to be exported, so that the system + sync framework can access it. --> + <service android:name=".SyncService" + android:exported="true"> + <!-- This intent filter is required. It allows the system to launch our sync service + as needed. --> + <intent-filter> + <action android:name="android.content.SyncAdapter" /> + </intent-filter> + <!-- This points to a required XML file which describes our SyncAdapter. --> + <meta-data android:name="android.content.SyncAdapter" + android:resource="@xml/syncadapter" /> + </service> + + <!-- This implements the account we'll use as an attachment point for our SyncAdapter. Since + our SyncAdapter doesn't need to authenticate the current user (it just fetches a public RSS + feed), this account's implementation is largely empty. + + It's also possible to attach a SyncAdapter to an existing account provided by another + package. In that case, this element could be omitted here. --> + <service android:name="com.example.android.common.accounts.GenericAccountService"> + <!-- Required filter used by the system to launch our account service. --> + <intent-filter> + <action android:name="android.accounts.AccountAuthenticator" /> + </intent-filter> + <!-- This points to an XMLf ile which describes our account service. --> + <meta-data android:name="android.accounts.AccountAuthenticator" + android:resource="@xml/authenticator" /> + </service> + + </application> + +</manifest> diff --git a/samples/browseable/BasicSyncAdapter/_index.jd b/samples/browseable/BasicSyncAdapter/_index.jd new file mode 100644 index 000000000..20f5572ba --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/_index.jd @@ -0,0 +1,17 @@ + + + +page.tags="BasicSyncAdapter" +sample.group=Connectivity +@jd:body + +<p> + + This sample demonstrates using SyncAdapter to fetch background data for an app that + doesn\'t require a user-visible account type or 2-way synchronization. + + \n\nThis sample periodically downloads the feed from the Android Developer Blog and + caches the data in a content provider. At runtime, the cached feed data is displayed + inside a ListView. + + </p> diff --git a/samples/browseable/BasicSyncAdapter/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicSyncAdapter/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..a0f7005a3 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/BasicSyncAdapter/res/drawable-hdpi/tile.9.png b/samples/browseable/BasicSyncAdapter/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/BasicSyncAdapter/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicSyncAdapter/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..a085462c2 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/BasicSyncAdapter/res/drawable-xhdpi/ic_action_refresh.png b/samples/browseable/BasicSyncAdapter/res/drawable-xhdpi/ic_action_refresh.png Binary files differnew file mode 100644 index 000000000..4f5d2558f --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/drawable-xhdpi/ic_action_refresh.png diff --git a/samples/browseable/BasicSyncAdapter/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicSyncAdapter/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..4f78eb846 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/BasicSyncAdapter/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicSyncAdapter/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..b198ee3e9 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/BasicSyncAdapter/res/layout/actionbar_indeterminate_progress.xml b/samples/browseable/BasicSyncAdapter/res/layout/actionbar_indeterminate_progress.xml new file mode 100644 index 000000000..b25401306 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/layout/actionbar_indeterminate_progress.xml @@ -0,0 +1,25 @@ +<!-- + Copyright 2012 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. + --> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="wrap_content" + android:layout_width="@dimen/action_button_min_width" + android:minWidth="@dimen/action_button_min_width"> + + <ProgressBar android:layout_width="@dimen/indeterminate_progress_size" + android:layout_height="@dimen/indeterminate_progress_size" + android:layout_gravity="center" + style="?indeterminateProgressStyle" /> +</FrameLayout> diff --git a/samples/browseable/BasicSyncAdapter/res/layout/activity_entry_list.xml b/samples/browseable/BasicSyncAdapter/res/layout/activity_entry_list.xml new file mode 100644 index 000000000..3c8c901c4 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/layout/activity_entry_list.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> + +<fragment xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/entry_list" + android:name="com.example.android.basicsyncadapter.EntryListFragment" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginLeft="16dp" + android:layout_marginRight="16dp" + tools:context=".EntryListActivity" + tools:layout="@android:layout/list_content" /> diff --git a/samples/browseable/BasicSyncAdapter/res/layout/activity_main.xml b/samples/browseable/BasicSyncAdapter/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/BasicSyncAdapter/res/menu/main.xml b/samples/browseable/BasicSyncAdapter/res/menu/main.xml new file mode 100644 index 000000000..63ad3d152 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/menu/main.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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. +--> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/menu_refresh" + android:icon="@drawable/ic_action_refresh" + android:title="@string/description_refresh" + android:orderInCategory="1" + android:showAsAction="always" /> +</menu>
\ No newline at end of file diff --git a/samples/browseable/BasicSyncAdapter/res/values-sw600dp/dimens.xml b/samples/browseable/BasicSyncAdapter/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicSyncAdapter/res/values-sw600dp/styles.xml b/samples/browseable/BasicSyncAdapter/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/BasicSyncAdapter/res/values/attrs.xml b/samples/browseable/BasicSyncAdapter/res/values/attrs.xml new file mode 100644 index 000000000..6c1550469 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/values/attrs.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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> + <!-- Specifies a style resource to use for an indeterminate progress spinner. --> + <attr name="indeterminateProgressStyle" format="reference"/> +</resources>
\ No newline at end of file diff --git a/samples/browseable/BasicSyncAdapter/res/values/base-strings.xml b/samples/browseable/BasicSyncAdapter/res/values/base-strings.xml new file mode 100644 index 000000000..26704848d --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/values/base-strings.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">BasicSyncAdapter</string> + <string name="intro_message"> + <![CDATA[ + + + This sample demonstrates using SyncAdapter to fetch background data for an app that + doesn\'t require a user-visible account type or 2-way synchronization. + + \n\nThis sample periodically downloads the feed from the Android Developer Blog and + caches the data in a content provider. At runtime, the cached feed data is displayed + inside a ListView. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/BasicSyncAdapter/res/values/dimen.xml b/samples/browseable/BasicSyncAdapter/res/values/dimen.xml new file mode 100644 index 000000000..d838c696d --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/values/dimen.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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> + <dimen name="action_button_min_width">56dp</dimen> + <dimen name="indeterminate_progress_size">32dp</dimen> +</resources>
\ No newline at end of file diff --git a/samples/browseable/BasicSyncAdapter/res/values/dimens.xml b/samples/browseable/BasicSyncAdapter/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BasicSyncAdapter/res/values/strings.xml b/samples/browseable/BasicSyncAdapter/res/values/strings.xml new file mode 100644 index 000000000..f4cade9e3 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/values/strings.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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="account_name">FeedSync Service</string> + <string name="title_entry_detail">Entry Detail</string> + <string name="loading">Waiting for sync...</string> + <string name="description_refresh">Refresh</string> +</resources>
\ No newline at end of file diff --git a/samples/browseable/BasicSyncAdapter/res/values/styles.xml b/samples/browseable/BasicSyncAdapter/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/BasicSyncAdapter/res/xml/authenticator.xml b/samples/browseable/BasicSyncAdapter/res/xml/authenticator.xml new file mode 100644 index 000000000..8b969077c --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/xml/authenticator.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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. +--> + +<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android" + android:accountType="com.example.android.basicsyncadapter.account" + android:icon="@drawable/ic_launcher" + android:smallIcon="@drawable/ic_launcher" + android:label="@string/app_name" + /> diff --git a/samples/browseable/BasicSyncAdapter/res/xml/syncadapter.xml b/samples/browseable/BasicSyncAdapter/res/xml/syncadapter.xml new file mode 100644 index 000000000..6e12882c6 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/res/xml/syncadapter.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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. +--> + +<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android" + android:contentAuthority="com.example.android.basicsyncadapter" + android:accountType="com.example.android.basicsyncadapter.account" + android:userVisible="false" + android:supportsUploading="false" + android:allowParallelSyncs="false" + android:isAlwaysSyncable="true" + /> diff --git a/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/EntryListActivity.java b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/EntryListActivity.java new file mode 100644 index 000000000..9d8cb7799 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/EntryListActivity.java @@ -0,0 +1,16 @@ +package com.example.android.basicsyncadapter; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +/** + * Activity for holding EntryListFragment. + */ +public class EntryListActivity extends FragmentActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_entry_list); + } +} diff --git a/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/EntryListFragment.java b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/EntryListFragment.java new file mode 100644 index 000000000..83e240afb --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/EntryListFragment.java @@ -0,0 +1,356 @@ +/* + * 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.basicsyncadapter; + +import android.accounts.Account; +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.ContentResolver; +import android.content.Intent; +import android.content.SyncStatusObserver; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v4.widget.SimpleCursorAdapter; +import android.text.format.Time; +import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.ListView; +import android.widget.TextView; + +import com.example.android.common.accounts.GenericAccountService; +import com.example.android.basicsyncadapter.provider.FeedContract; + +/** + * List fragment containing a list of Atom entry objects (articles) stored in the local database. + * + * <p>Database access is mediated by a content provider, specified in + * {@link com.example.android.basicsyncadapter.provider.FeedProvider}. This content + * provider is + * automatically populated by {@link SyncService}. + * + * <p>Selecting an item from the displayed list displays the article in the default browser. + * + * <p>If the content provider doesn't return any data, then the first sync hasn't run yet. This sync + * adapter assumes data exists in the provider once a sync has run. If your app doesn't work like + * this, you should add a flag that notes if a sync has run, so you can differentiate between "no + * available data" and "no initial sync", and display this in the UI. + * + * <p>The ActionBar displays a "Refresh" button. When the user clicks "Refresh", the sync adapter + * runs immediately. An indeterminate ProgressBar element is displayed, showing that the sync is + * occurring. + */ +public class EntryListFragment extends ListFragment + implements LoaderManager.LoaderCallbacks<Cursor> { + + private static final String TAG = "EntryListFragment"; + + /** + * Cursor adapter for controlling ListView results. + */ + private SimpleCursorAdapter mAdapter; + + /** + * Handle to a SyncObserver. The ProgressBar element is visible until the SyncObserver reports + * that the sync is complete. + * + * <p>This allows us to delete our SyncObserver once the application is no longer in the + * foreground. + */ + private Object mSyncObserverHandle; + + /** + * Options menu used to populate ActionBar. + */ + private Menu mOptionsMenu; + + /** + * Projection for querying the content provider. + */ + private static final String[] PROJECTION = new String[]{ + FeedContract.Entry._ID, + FeedContract.Entry.COLUMN_NAME_TITLE, + FeedContract.Entry.COLUMN_NAME_LINK, + FeedContract.Entry.COLUMN_NAME_PUBLISHED + }; + + // Column indexes. The index of a column in the Cursor is the same as its relative position in + // the projection. + /** Column index for _ID */ + private static final int COLUMN_ID = 0; + /** Column index for title */ + private static final int COLUMN_TITLE = 1; + /** Column index for link */ + private static final int COLUMN_URL_STRING = 2; + /** Column index for published */ + private static final int COLUMN_PUBLISHED = 3; + + /** + * List of Cursor columns to read from when preparing an adapter to populate the ListView. + */ + private static final String[] FROM_COLUMNS = new String[]{ + FeedContract.Entry.COLUMN_NAME_TITLE, + FeedContract.Entry.COLUMN_NAME_PUBLISHED + }; + + /** + * List of Views which will be populated by Cursor data. + */ + private static final int[] TO_FIELDS = new int[]{ + android.R.id.text1, + android.R.id.text2}; + + /** + * Mandatory empty constructor for the fragment manager to instantiate the + * fragment (e.g. upon screen orientation changes). + */ + public EntryListFragment() {} + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + /** + * Create SyncAccount at launch, if needed. + * + * <p>This will create a new account with the system for our application, register our + * {@link SyncService} with it, and establish a sync schedule. + */ + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + // Create account, if needed + SyncUtils.CreateSyncAccount(activity); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + mAdapter = new SimpleCursorAdapter( + getActivity(), // Current context + android.R.layout.simple_list_item_activated_2, // Layout for individual rows + null, // Cursor + FROM_COLUMNS, // Cursor columns to use + TO_FIELDS, // Layout fields to use + 0 // No flags + ); + mAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() { + @Override + public boolean setViewValue(View view, Cursor cursor, int i) { + if (i == COLUMN_PUBLISHED) { + // Convert timestamp to human-readable date + Time t = new Time(); + t.set(cursor.getLong(i)); + ((TextView) view).setText(t.format("%Y-%m-%d %H:%M")); + return true; + } else { + // Let SimpleCursorAdapter handle other fields automatically + return false; + } + } + }); + setListAdapter(mAdapter); + setEmptyText(getText(R.string.loading)); + getLoaderManager().initLoader(0, null, this); + } + + @Override + public void onResume() { + super.onResume(); + mSyncStatusObserver.onStatusChanged(0); + + // Watch for sync state changes + final int mask = ContentResolver.SYNC_OBSERVER_TYPE_PENDING | + ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE; + mSyncObserverHandle = ContentResolver.addStatusChangeListener(mask, mSyncStatusObserver); + } + + @Override + public void onPause() { + super.onPause(); + if (mSyncObserverHandle != null) { + ContentResolver.removeStatusChangeListener(mSyncObserverHandle); + mSyncObserverHandle = null; + } + } + + /** + * Query the content provider for data. + * + * <p>Loaders do queries in a background thread. They also provide a ContentObserver that is + * triggered when data in the content provider changes. When the sync adapter updates the + * content provider, the ContentObserver responds by resetting the loader and then reloading + * it. + */ + @Override + public Loader<Cursor> onCreateLoader(int i, Bundle bundle) { + // We only have one loader, so we can ignore the value of i. + // (It'll be '0', as set in onCreate().) + return new CursorLoader(getActivity(), // Context + FeedContract.Entry.CONTENT_URI, // URI + PROJECTION, // Projection + null, // Selection + null, // Selection args + FeedContract.Entry.COLUMN_NAME_PUBLISHED + " desc"); // Sort + } + + /** + * Move the Cursor returned by the query into the ListView adapter. This refreshes the existing + * UI with the data in the Cursor. + */ + @Override + public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) { + mAdapter.changeCursor(cursor); + } + + /** + * Called when the ContentObserver defined for the content provider detects that data has + * changed. The ContentObserver resets the loader, and then re-runs the loader. In the adapter, + * set the Cursor value to null. This removes the reference to the Cursor, allowing it to be + * garbage-collected. + */ + @Override + public void onLoaderReset(Loader<Cursor> cursorLoader) { + mAdapter.changeCursor(null); + } + + /** + * Create the ActionBar. + */ + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + mOptionsMenu = menu; + inflater.inflate(R.menu.main, menu); + } + + /** + * Respond to user gestures on the ActionBar. + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + // If the user clicks the "Refresh" button. + case R.id.menu_refresh: + SyncUtils.TriggerRefresh(); + return true; + } + return super.onOptionsItemSelected(item); + } + + /** + * Load an article in the default browser when selected by the user. + */ + @Override + public void onListItemClick(ListView listView, View view, int position, long id) { + super.onListItemClick(listView, view, position, id); + + // Get a URI for the selected item, then start an Activity that displays the URI. Any + // Activity that filters for ACTION_VIEW and a URI can accept this. In most cases, this will + // be a browser. + + // Get the item at the selected position, in the form of a Cursor. + Cursor c = (Cursor) mAdapter.getItem(position); + // Get the link to the article represented by the item. + String articleUrlString = c.getString(COLUMN_URL_STRING); + if (articleUrlString == null) { + Log.e(TAG, "Attempt to launch entry with null link"); + return; + } + + Log.i(TAG, "Opening URL: " + articleUrlString); + // Get a Uri object for the URL string + Uri articleURL = Uri.parse(articleUrlString); + Intent i = new Intent(Intent.ACTION_VIEW, articleURL); + startActivity(i); + } + + /** + * Set the state of the Refresh button. If a sync is active, turn on the ProgressBar widget. + * Otherwise, turn it off. + * + * @param refreshing True if an active sync is occuring, false otherwise + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public void setRefreshActionButtonState(boolean refreshing) { + if (mOptionsMenu == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + return; + } + + final MenuItem refreshItem = mOptionsMenu.findItem(R.id.menu_refresh); + if (refreshItem != null) { + if (refreshing) { + refreshItem.setActionView(R.layout.actionbar_indeterminate_progress); + } else { + refreshItem.setActionView(null); + } + } + } + + /** + * Crfate a new anonymous SyncStatusObserver. It's attached to the app's ContentResolver in + * onResume(), and removed in onPause(). If status changes, it sets the state of the Refresh + * button. If a sync is active or pending, the Refresh button is replaced by an indeterminate + * ProgressBar; otherwise, the button itself is displayed. + */ + private SyncStatusObserver mSyncStatusObserver = new SyncStatusObserver() { + /** Callback invoked with the sync adapter status changes. */ + @Override + public void onStatusChanged(int which) { + getActivity().runOnUiThread(new Runnable() { + /** + * The SyncAdapter runs on a background thread. To update the UI, onStatusChanged() + * runs on the UI thread. + */ + @Override + public void run() { + // Create a handle to the account that was created by + // SyncService.CreateSyncAccount(). This will be used to query the system to + // see how the sync status has changed. + Account account = GenericAccountService.GetAccount(SyncUtils.ACCOUNT_TYPE); + if (account == null) { + // GetAccount() returned an invalid value. This shouldn't happen, but + // we'll set the status to "not refreshing". + setRefreshActionButtonState(false); + return; + } + + // Test the ContentResolver to see if the sync adapter is active or pending. + // Set the state of the refresh button accordingly. + boolean syncActive = ContentResolver.isSyncActive( + account, FeedContract.CONTENT_AUTHORITY); + boolean syncPending = ContentResolver.isSyncPending( + account, FeedContract.CONTENT_AUTHORITY); + setRefreshActionButtonState(syncActive || syncPending); + } + }); + } + }; + +}
\ No newline at end of file diff --git a/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/SyncAdapter.java b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/SyncAdapter.java new file mode 100644 index 000000000..da6710769 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/SyncAdapter.java @@ -0,0 +1,306 @@ +/* + * 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.basicsyncadapter; + +import android.accounts.Account; +import android.annotation.TargetApi; +import android.content.AbstractThreadedSyncAdapter; +import android.content.ContentProviderClient; +import android.content.ContentProviderOperation; +import android.content.ContentResolver; +import android.content.Context; +import android.content.OperationApplicationException; +import android.content.SyncResult; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.RemoteException; +import android.util.Log; + +import com.example.android.basicsyncadapter.net.FeedParser; +import com.example.android.basicsyncadapter.provider.FeedContract; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * Define a sync adapter for the app. + * + * <p>This class is instantiated in {@link SyncService}, which also binds SyncAdapter to the system. + * SyncAdapter should only be initialized in SyncService, never anywhere else. + * + * <p>The system calls onPerformSync() via an RPC call through the IBinder object supplied by + * SyncService. + */ +class SyncAdapter extends AbstractThreadedSyncAdapter { + public static final String TAG = "SyncAdapter"; + + /** + * URL to fetch content from during a sync. + * + * <p>This points to the Android Developers Blog. (Side note: We highly recommend reading the + * Android Developer Blog to stay up to date on the latest Android platform developments!) + */ + private static final String FEED_URL = "http://android-developers.blogspot.com/atom.xml"; + + /** + * Network connection timeout, in milliseconds. + */ + private static final int NET_CONNECT_TIMEOUT_MILLIS = 15000; // 15 seconds + + /** + * Network read timeout, in milliseconds. + */ + private static final int NET_READ_TIMEOUT_MILLIS = 10000; // 10 seconds + + /** + * Content resolver, for performing database operations. + */ + private final ContentResolver mContentResolver; + + /** + * Project used when querying content provider. Returns all known fields. + */ + private static final String[] PROJECTION = new String[] { + FeedContract.Entry._ID, + FeedContract.Entry.COLUMN_NAME_ENTRY_ID, + FeedContract.Entry.COLUMN_NAME_TITLE, + FeedContract.Entry.COLUMN_NAME_LINK, + FeedContract.Entry.COLUMN_NAME_PUBLISHED}; + + // Constants representing column positions from PROJECTION. + public static final int COLUMN_ID = 0; + public static final int COLUMN_ENTRY_ID = 1; + public static final int COLUMN_TITLE = 2; + public static final int COLUMN_LINK = 3; + public static final int COLUMN_PUBLISHED = 4; + + /** + * Constructor. Obtains handle to content resolver for later use. + */ + public SyncAdapter(Context context, boolean autoInitialize) { + super(context, autoInitialize); + mContentResolver = context.getContentResolver(); + } + + /** + * Constructor. Obtains handle to content resolver for later use. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public SyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) { + super(context, autoInitialize, allowParallelSyncs); + mContentResolver = context.getContentResolver(); + } + + /** + * Called by the Android system in response to a request to run the sync adapter. The work + * required to read data from the network, parse it, and store it in the content provider is + * done here. Extending AbstractThreadedSyncAdapter ensures that all methods within SyncAdapter + * run on a background thread. For this reason, blocking I/O and other long-running tasks can be + * run <em>in situ</em>, and you don't have to set up a separate thread for them. + . + * + * <p>This is where we actually perform any work required to perform a sync. + * {@link android.content.AbstractThreadedSyncAdapter} guarantees that this will be called on a non-UI thread, + * so it is safe to peform blocking I/O here. + * + * <p>The syncResult argument allows you to pass information back to the method that triggered + * the sync. + */ + @Override + public void onPerformSync(Account account, Bundle extras, String authority, + ContentProviderClient provider, SyncResult syncResult) { + Log.i(TAG, "Beginning network synchronization"); + try { + final URL location = new URL(FEED_URL); + InputStream stream = null; + + try { + Log.i(TAG, "Streaming data from network: " + location); + stream = downloadUrl(location); + updateLocalFeedData(stream, syncResult); + // Makes sure that the InputStream is closed after the app is + // finished using it. + } finally { + if (stream != null) { + stream.close(); + } + } + } catch (MalformedURLException e) { + Log.e(TAG, "Feed URL is malformed", e); + syncResult.stats.numParseExceptions++; + return; + } catch (IOException e) { + Log.e(TAG, "Error reading from network: " + e.toString()); + syncResult.stats.numIoExceptions++; + return; + } catch (XmlPullParserException e) { + Log.e(TAG, "Error parsing feed: " + e.toString()); + syncResult.stats.numParseExceptions++; + return; + } catch (ParseException e) { + Log.e(TAG, "Error parsing feed: " + e.toString()); + syncResult.stats.numParseExceptions++; + return; + } catch (RemoteException e) { + Log.e(TAG, "Error updating database: " + e.toString()); + syncResult.databaseError = true; + return; + } catch (OperationApplicationException e) { + Log.e(TAG, "Error updating database: " + e.toString()); + syncResult.databaseError = true; + return; + } + Log.i(TAG, "Network synchronization complete"); + } + + /** + * Read XML from an input stream, storing it into the content provider. + * + * <p>This is where incoming data is persisted, committing the results of a sync. In order to + * minimize (expensive) disk operations, we compare incoming data with what's already in our + * database, and compute a merge. Only changes (insert/update/delete) will result in a database + * write. + * + * <p>As an additional optimization, we use a batch operation to perform all database writes at + * once. + * + * <p>Merge strategy: + * 1. Get cursor to all items in feed<br/> + * 2. For each item, check if it's in the incoming data.<br/> + * a. YES: Remove from "incoming" list. Check if data has mutated, if so, perform + * database UPDATE.<br/> + * b. NO: Schedule DELETE from database.<br/> + * (At this point, incoming database only contains missing items.)<br/> + * 3. For any items remaining in incoming list, ADD to database. + */ + public void updateLocalFeedData(final InputStream stream, final SyncResult syncResult) + throws IOException, XmlPullParserException, RemoteException, + OperationApplicationException, ParseException { + final FeedParser feedParser = new FeedParser(); + final ContentResolver contentResolver = getContext().getContentResolver(); + + Log.i(TAG, "Parsing stream as Atom feed"); + final List<FeedParser.Entry> entries = feedParser.parse(stream); + Log.i(TAG, "Parsing complete. Found " + entries.size() + " entries"); + + + ArrayList<ContentProviderOperation> batch = new ArrayList<ContentProviderOperation>(); + + // Build hash table of incoming entries + HashMap<String, FeedParser.Entry> entryMap = new HashMap<String, FeedParser.Entry>(); + for (FeedParser.Entry e : entries) { + entryMap.put(e.id, e); + } + + // Get list of all items + Log.i(TAG, "Fetching local entries for merge"); + Uri uri = FeedContract.Entry.CONTENT_URI; // Get all entries + Cursor c = contentResolver.query(uri, PROJECTION, null, null, null); + assert c != null; + Log.i(TAG, "Found " + c.getCount() + " local entries. Computing merge solution..."); + + // Find stale data + int id; + String entryId; + String title; + String link; + long published; + while (c.moveToNext()) { + syncResult.stats.numEntries++; + id = c.getInt(COLUMN_ID); + entryId = c.getString(COLUMN_ENTRY_ID); + title = c.getString(COLUMN_TITLE); + link = c.getString(COLUMN_LINK); + published = c.getLong(COLUMN_PUBLISHED); + FeedParser.Entry match = entryMap.get(entryId); + if (match != null) { + // Entry exists. Remove from entry map to prevent insert later. + entryMap.remove(entryId); + // Check to see if the entry needs to be updated + Uri existingUri = FeedContract.Entry.CONTENT_URI.buildUpon() + .appendPath(Integer.toString(id)).build(); + if ((match.title != null && !match.title.equals(title)) || + (match.link != null && !match.link.equals(link)) || + (match.published != published)) { + // Update existing record + Log.i(TAG, "Scheduling update: " + existingUri); + batch.add(ContentProviderOperation.newUpdate(existingUri) + .withValue(FeedContract.Entry.COLUMN_NAME_TITLE, title) + .withValue(FeedContract.Entry.COLUMN_NAME_LINK, link) + .withValue(FeedContract.Entry.COLUMN_NAME_PUBLISHED, published) + .build()); + syncResult.stats.numUpdates++; + } else { + Log.i(TAG, "No action: " + existingUri); + } + } else { + // Entry doesn't exist. Remove it from the database. + Uri deleteUri = FeedContract.Entry.CONTENT_URI.buildUpon() + .appendPath(Integer.toString(id)).build(); + Log.i(TAG, "Scheduling delete: " + deleteUri); + batch.add(ContentProviderOperation.newDelete(deleteUri).build()); + syncResult.stats.numDeletes++; + } + } + c.close(); + + // Add new items + for (FeedParser.Entry e : entryMap.values()) { + Log.i(TAG, "Scheduling insert: entry_id=" + e.id); + batch.add(ContentProviderOperation.newInsert(FeedContract.Entry.CONTENT_URI) + .withValue(FeedContract.Entry.COLUMN_NAME_ENTRY_ID, e.id) + .withValue(FeedContract.Entry.COLUMN_NAME_TITLE, e.title) + .withValue(FeedContract.Entry.COLUMN_NAME_LINK, e.link) + .withValue(FeedContract.Entry.COLUMN_NAME_PUBLISHED, e.published) + .build()); + syncResult.stats.numInserts++; + } + Log.i(TAG, "Merge solution ready. Applying batch update"); + mContentResolver.applyBatch(FeedContract.CONTENT_AUTHORITY, batch); + mContentResolver.notifyChange( + FeedContract.Entry.CONTENT_URI, // URI where data was modified + null, // No local observer + false); // IMPORTANT: Do not sync to network + // This sample doesn't support uploads, but if *your* code does, make sure you set + // syncToNetwork=false in the line above to prevent duplicate syncs. + } + + /** + * Given a string representation of a URL, sets up a connection and gets an input stream. + */ + private InputStream downloadUrl(final URL url) throws IOException { + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setReadTimeout(NET_READ_TIMEOUT_MILLIS /* milliseconds */); + conn.setConnectTimeout(NET_CONNECT_TIMEOUT_MILLIS /* milliseconds */); + conn.setRequestMethod("GET"); + conn.setDoInput(true); + // Starts the query + conn.connect(); + return conn.getInputStream(); + } +} diff --git a/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/SyncService.java b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/SyncService.java new file mode 100644 index 000000000..41e9c030d --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/SyncService.java @@ -0,0 +1,76 @@ +/* + * Copyright 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. + */ + +package com.example.android.basicsyncadapter; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.util.Log; + +/** Service to handle sync requests. + * + * <p>This service is invoked in response to Intents with action android.content.SyncAdapter, and + * returns a Binder connection to SyncAdapter. + * + * <p>For performance, only one sync adapter will be initialized within this application's context. + * + * <p>Note: The SyncService itself is not notified when a new sync occurs. It's role is to + * manage the lifecycle of our {@link SyncAdapter} and provide a handle to said SyncAdapter to the + * OS on request. + */ +public class SyncService extends Service { + private static final String TAG = "SyncService"; + + private static final Object sSyncAdapterLock = new Object(); + private static SyncAdapter sSyncAdapter = null; + + /** + * Thread-safe constructor, creates static {@link SyncAdapter} instance. + */ + @Override + public void onCreate() { + super.onCreate(); + Log.i(TAG, "Service created"); + synchronized (sSyncAdapterLock) { + if (sSyncAdapter == null) { + sSyncAdapter = new SyncAdapter(getApplicationContext(), true); + } + } + } + + @Override + /** + * Logging-only destructor. + */ + public void onDestroy() { + super.onDestroy(); + Log.i(TAG, "Service destroyed"); + } + + /** + * Return Binder handle for IPC communication with {@link SyncAdapter}. + * + * <p>New sync requests will be sent directly to the SyncAdapter using this channel. + * + * @param intent Calling intent + * @return Binder handle for {@link SyncAdapter} + */ + @Override + public IBinder onBind(Intent intent) { + return sSyncAdapter.getSyncAdapterBinder(); + } +} diff --git a/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/SyncUtils.java b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/SyncUtils.java new file mode 100644 index 000000000..b327c7287 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/SyncUtils.java @@ -0,0 +1,99 @@ +/* + * Copyright 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. + */ + +package com.example.android.basicsyncadapter; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.annotation.TargetApi; +import android.content.ContentResolver; +import android.content.Context; +import android.os.Build; +import android.os.Bundle; +import android.preference.PreferenceManager; + +import com.example.android.common.accounts.GenericAccountService; +import com.example.android.basicsyncadapter.provider.FeedContract; + +/** + * Static helper methods for working with the sync framework. + */ +public class SyncUtils { + private static final long SYNC_FREQUENCY = 60 * 60; // 1 hour (in seconds) + private static final String CONTENT_AUTHORITY = FeedContract.CONTENT_AUTHORITY; + private static final String PREF_SETUP_COMPLETE = "setup_complete"; + // Value below must match the account type specified in res/xml/syncadapter.xml + public static final String ACCOUNT_TYPE = "com.example.android.basicsyncadapter.account"; + + /** + * Create an entry for this application in the system account list, if it isn't already there. + * + * @param context Context + */ + @TargetApi(Build.VERSION_CODES.FROYO) + public static void CreateSyncAccount(Context context) { + boolean newAccount = false; + boolean setupComplete = PreferenceManager + .getDefaultSharedPreferences(context).getBoolean(PREF_SETUP_COMPLETE, false); + + // Create account, if it's missing. (Either first run, or user has deleted account.) + Account account = GenericAccountService.GetAccount(ACCOUNT_TYPE); + AccountManager accountManager = + (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE); + if (accountManager.addAccountExplicitly(account, null, null)) { + // Inform the system that this account supports sync + ContentResolver.setIsSyncable(account, CONTENT_AUTHORITY, 1); + // Inform the system that this account is eligible for auto sync when the network is up + ContentResolver.setSyncAutomatically(account, CONTENT_AUTHORITY, true); + // Recommend a schedule for automatic synchronization. The system may modify this based + // on other scheduled syncs and network utilization. + ContentResolver.addPeriodicSync( + account, CONTENT_AUTHORITY, new Bundle(),SYNC_FREQUENCY); + newAccount = true; + } + + // Schedule an initial sync if we detect problems with either our account or our local + // data has been deleted. (Note that it's possible to clear app data WITHOUT affecting + // the account list, so wee need to check both.) + if (newAccount || !setupComplete) { + TriggerRefresh(); + PreferenceManager.getDefaultSharedPreferences(context).edit() + .putBoolean(PREF_SETUP_COMPLETE, true).commit(); + } + } + + /** + * Helper method to trigger an immediate sync ("refresh"). + * + * <p>This should only be used when we need to preempt the normal sync schedule. Typically, this + * means the user has pressed the "refresh" button. + * + * Note that SYNC_EXTRAS_MANUAL will cause an immediate sync, without any optimization to + * preserve battery life. If you know new data is available (perhaps via a GCM notification), + * but the user is not actively waiting for that data, you should omit this flag; this will give + * the OS additional freedom in scheduling your sync request. + */ + public static void TriggerRefresh() { + Bundle b = new Bundle(); + // Disable sync backoff and ignore sync preferences. In other words...perform sync NOW! + b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); + b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); + ContentResolver.requestSync( + GenericAccountService.GetAccount(ACCOUNT_TYPE), // Sync account + FeedContract.CONTENT_AUTHORITY, // Content authority + b); // Extras + } +} diff --git a/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/net/FeedParser.java b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/net/FeedParser.java new file mode 100644 index 000000000..a778390ab --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/net/FeedParser.java @@ -0,0 +1,278 @@ +/* + * 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.basicsyncadapter.net; + +import android.text.format.Time; +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStream; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +/** + * This class parses generic Atom feeds. + * + * <p>Given an InputStream representation of a feed, it returns a List of entries, + * where each list element represents a single entry (post) in the XML feed. + * + * <p>An example of an Atom feed can be found at: + * http://en.wikipedia.org/w/index.php?title=Atom_(standard)&oldid=560239173#Example_of_an_Atom_1.0_feed + */ +public class FeedParser { + + // Constants indicting XML element names that we're interested in + private static final int TAG_ID = 1; + private static final int TAG_TITLE = 2; + private static final int TAG_PUBLISHED = 3; + private static final int TAG_LINK = 4; + + // We don't use XML namespaces + private static final String ns = null; + + /** Parse an Atom feed, returning a collection of Entry objects. + * + * @param in Atom feed, as a stream. + * @return List of {@link com.example.android.basicsyncadapter.net.FeedParser.Entry} objects. + * @throws org.xmlpull.v1.XmlPullParserException on error parsing feed. + * @throws java.io.IOException on I/O error. + */ + public List<Entry> parse(InputStream in) + throws XmlPullParserException, IOException, ParseException { + try { + XmlPullParser parser = Xml.newPullParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); + parser.setInput(in, null); + parser.nextTag(); + return readFeed(parser); + } finally { + in.close(); + } + } + + /** + * Decode a feed attached to an XmlPullParser. + * + * @param parser Incoming XMl + * @return List of {@link com.example.android.basicsyncadapter.net.FeedParser.Entry} objects. + * @throws org.xmlpull.v1.XmlPullParserException on error parsing feed. + * @throws java.io.IOException on I/O error. + */ + private List<Entry> readFeed(XmlPullParser parser) + throws XmlPullParserException, IOException, ParseException { + List<Entry> entries = new ArrayList<Entry>(); + + // Search for <feed> tags. These wrap the beginning/end of an Atom document. + // + // Example: + // <?xml version="1.0" encoding="utf-8"?> + // <feed xmlns="http://www.w3.org/2005/Atom"> + // ... + // </feed> + parser.require(XmlPullParser.START_TAG, ns, "feed"); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + // Starts by looking for the <entry> tag. This tag repeates inside of <feed> for each + // article in the feed. + // + // Example: + // <entry> + // <title>Article title</title> + // <link rel="alternate" type="text/html" href="http://example.com/article/1234"/> + // <link rel="edit" href="http://example.com/admin/article/1234"/> + // <id>urn:uuid:218AC159-7F68-4CC6-873F-22AE6017390D</id> + // <published>2003-06-27T12:00:00Z</published> + // <updated>2003-06-28T12:00:00Z</updated> + // <summary>Article summary goes here.</summary> + // <author> + // <name>Rick Deckard</name> + // <email>deckard@example.com</email> + // </author> + // </entry> + if (name.equals("entry")) { + entries.add(readEntry(parser)); + } else { + skip(parser); + } + } + return entries; + } + + /** + * Parses the contents of an entry. If it encounters a title, summary, or link tag, hands them + * off to their respective "read" methods for processing. Otherwise, skips the tag. + */ + private Entry readEntry(XmlPullParser parser) + throws XmlPullParserException, IOException, ParseException { + parser.require(XmlPullParser.START_TAG, ns, "entry"); + String id = null; + String title = null; + String link = null; + long publishedOn = 0; + + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + if (name.equals("id")){ + // Example: <id>urn:uuid:218AC159-7F68-4CC6-873F-22AE6017390D</id> + id = readTag(parser, TAG_ID); + } else if (name.equals("title")) { + // Example: <title>Article title</title> + title = readTag(parser, TAG_TITLE); + } else if (name.equals("link")) { + // Example: <link rel="alternate" type="text/html" href="http://example.com/article/1234"/> + // + // Multiple link types can be included. readAlternateLink() will only return + // non-null when reading an "alternate"-type link. Ignore other responses. + String tempLink = readTag(parser, TAG_LINK); + if (tempLink != null) { + link = tempLink; + } + } else if (name.equals("published")) { + // Example: <published>2003-06-27T12:00:00Z</published> + Time t = new Time(); + t.parse3339(readTag(parser, TAG_PUBLISHED)); + publishedOn = t.toMillis(false); + } else { + skip(parser); + } + } + return new Entry(id, title, link, publishedOn); + } + + /** + * Process an incoming tag and read the selected value from it. + */ + private String readTag(XmlPullParser parser, int tagType) + throws IOException, XmlPullParserException { + String tag = null; + String endTag = null; + + switch (tagType) { + case TAG_ID: + return readBasicTag(parser, "id"); + case TAG_TITLE: + return readBasicTag(parser, "title"); + case TAG_PUBLISHED: + return readBasicTag(parser, "published"); + case TAG_LINK: + return readAlternateLink(parser); + default: + throw new IllegalArgumentException("Unknown tag type: " + tagType); + } + } + + /** + * Reads the body of a basic XML tag, which is guaranteed not to contain any nested elements. + * + * <p>You probably want to call readTag(). + * + * @param parser Current parser object + * @param tag XML element tag name to parse + * @return Body of the specified tag + * @throws java.io.IOException + * @throws org.xmlpull.v1.XmlPullParserException + */ + private String readBasicTag(XmlPullParser parser, String tag) + throws IOException, XmlPullParserException { + parser.require(XmlPullParser.START_TAG, ns, tag); + String result = readText(parser); + parser.require(XmlPullParser.END_TAG, ns, tag); + return result; + } + + /** + * Processes link tags in the feed. + */ + private String readAlternateLink(XmlPullParser parser) + throws IOException, XmlPullParserException { + String link = null; + parser.require(XmlPullParser.START_TAG, ns, "link"); + String tag = parser.getName(); + String relType = parser.getAttributeValue(null, "rel"); + if (relType.equals("alternate")) { + link = parser.getAttributeValue(null, "href"); + } + while (true) { + if (parser.nextTag() == XmlPullParser.END_TAG) break; + // Intentionally break; consumes any remaining sub-tags. + } + return link; + } + + /** + * For the tags title and summary, extracts their text values. + */ + private String readText(XmlPullParser parser) throws IOException, XmlPullParserException { + String result = null; + if (parser.next() == XmlPullParser.TEXT) { + result = parser.getText(); + parser.nextTag(); + } + return result; + } + + /** + * Skips tags the parser isn't interested in. Uses depth to handle nested tags. i.e., + * if the next tag after a START_TAG isn't a matching END_TAG, it keeps going until it + * finds the matching END_TAG (as indicated by the value of "depth" being 0). + */ + private void skip(XmlPullParser parser) throws XmlPullParserException, IOException { + if (parser.getEventType() != XmlPullParser.START_TAG) { + throw new IllegalStateException(); + } + int depth = 1; + while (depth != 0) { + switch (parser.next()) { + case XmlPullParser.END_TAG: + depth--; + break; + case XmlPullParser.START_TAG: + depth++; + break; + } + } + } + + /** + * This class represents a single entry (post) in the XML feed. + * + * <p>It includes the data members "title," "link," and "summary." + */ + public static class Entry { + public final String id; + public final String title; + public final String link; + public final long published; + + Entry(String id, String title, String link, long published) { + this.id = id; + this.title = title; + this.link = link; + this.published = published; + } + } +} diff --git a/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/provider/FeedContract.java b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/provider/FeedContract.java new file mode 100644 index 000000000..e29ec4823 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/provider/FeedContract.java @@ -0,0 +1,89 @@ +/* + * 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.basicsyncadapter.provider; + +import android.content.ContentResolver; +import android.net.Uri; +import android.provider.BaseColumns; + +/** + * Field and table name constants for + * {@link com.example.android.basicsyncadapter.provider.FeedProvider}. + */ +public class FeedContract { + private FeedContract() { + } + + /** + * Content provider authority. + */ + public static final String CONTENT_AUTHORITY = "com.example.android.basicsyncadapter"; + + /** + * Base URI. (content://com.example.android.basicsyncadapter) + */ + public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY); + + /** + * Path component for "entry"-type resources.. + */ + private static final String PATH_ENTRIES = "entries"; + + /** + * Columns supported by "entries" records. + */ + public static class Entry implements BaseColumns { + /** + * MIME type for lists of entries. + */ + public static final String CONTENT_TYPE = + ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd.basicsyncadapter.entries"; + /** + * MIME type for individual entries. + */ + public static final String CONTENT_ITEM_TYPE = + ContentResolver.CURSOR_ITEM_BASE_TYPE + "/vnd.basicsyncadapter.entry"; + + /** + * Fully qualified URI for "entry" resources. + */ + public static final Uri CONTENT_URI = + BASE_CONTENT_URI.buildUpon().appendPath(PATH_ENTRIES).build(); + + /** + * Table name where records are stored for "entry" resources. + */ + public static final String TABLE_NAME = "entry"; + /** + * Atom ID. (Note: Not to be confused with the database primary key, which is _ID. + */ + public static final String COLUMN_NAME_ENTRY_ID = "entry_id"; + /** + * Article title + */ + public static final String COLUMN_NAME_TITLE = "title"; + /** + * Article hyperlink. Corresponds to the rel="alternate" link in the + * Atom spec. + */ + public static final String COLUMN_NAME_LINK = "link"; + /** + * Date article was published. + */ + public static final String COLUMN_NAME_PUBLISHED = "published"; + } +}
\ No newline at end of file diff --git a/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/provider/FeedProvider.java b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/provider/FeedProvider.java new file mode 100644 index 000000000..80bf1d3d5 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/provider/FeedProvider.java @@ -0,0 +1,252 @@ +/* + * 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.basicsyncadapter.provider; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.Context; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.net.Uri; + +import com.example.android.common.db.SelectionBuilder; + +public class FeedProvider extends ContentProvider { + FeedDatabase mDatabaseHelper; + + /** + * Content authority for this provider. + */ + private static final String AUTHORITY = FeedContract.CONTENT_AUTHORITY; + + // The constants below represent individual URI routes, as IDs. Every URI pattern recognized by + // this ContentProvider is defined using sUriMatcher.addURI(), and associated with one of these + // IDs. + // + // When a incoming URI is run through sUriMatcher, it will be tested against the defined + // URI patterns, and the corresponding route ID will be returned. + /** + * URI ID for route: /entries + */ + public static final int ROUTE_ENTRIES = 1; + + /** + * URI ID for route: /entries/{ID} + */ + public static final int ROUTE_ENTRIES_ID = 2; + + /** + * UriMatcher, used to decode incoming URIs. + */ + private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); + static { + sUriMatcher.addURI(AUTHORITY, "entries", ROUTE_ENTRIES); + sUriMatcher.addURI(AUTHORITY, "entries/*", ROUTE_ENTRIES_ID); + } + + @Override + public boolean onCreate() { + mDatabaseHelper = new FeedDatabase(getContext()); + return true; + } + + /** + * Determine the mime type for entries returned by a given URI. + */ + @Override + public String getType(Uri uri) { + final int match = sUriMatcher.match(uri); + switch (match) { + case ROUTE_ENTRIES: + return FeedContract.Entry.CONTENT_TYPE; + case ROUTE_ENTRIES_ID: + return FeedContract.Entry.CONTENT_ITEM_TYPE; + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + } + + /** + * Perform a database query by URI. + * + * <p>Currently supports returning all entries (/entries) and individual entries by ID + * (/entries/{ID}). + */ + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + SQLiteDatabase db = mDatabaseHelper.getReadableDatabase(); + SelectionBuilder builder = new SelectionBuilder(); + int uriMatch = sUriMatcher.match(uri); + switch (uriMatch) { + case ROUTE_ENTRIES_ID: + // Return a single entry, by ID. + String id = uri.getLastPathSegment(); + builder.where(FeedContract.Entry._ID + "=?", id); + case ROUTE_ENTRIES: + // Return all known entries. + builder.table(FeedContract.Entry.TABLE_NAME) + .where(selection, selectionArgs); + Cursor c = builder.query(db, projection, sortOrder); + // Note: Notification URI must be manually set here for loaders to correctly + // register ContentObservers. + Context ctx = getContext(); + assert ctx != null; + c.setNotificationUri(ctx.getContentResolver(), uri); + return c; + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + } + + /** + * Insert a new entry into the database. + */ + @Override + public Uri insert(Uri uri, ContentValues values) { + final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); + assert db != null; + final int match = sUriMatcher.match(uri); + Uri result; + switch (match) { + case ROUTE_ENTRIES: + long id = db.insertOrThrow(FeedContract.Entry.TABLE_NAME, null, values); + result = Uri.parse(FeedContract.Entry.CONTENT_URI + "/" + id); + break; + case ROUTE_ENTRIES_ID: + throw new UnsupportedOperationException("Insert not supported on URI: " + uri); + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + // Send broadcast to registered ContentObservers, to refresh UI. + Context ctx = getContext(); + assert ctx != null; + ctx.getContentResolver().notifyChange(uri, null, false); + return result; + } + + /** + * Delete an entry by database by URI. + */ + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + SelectionBuilder builder = new SelectionBuilder(); + final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); + final int match = sUriMatcher.match(uri); + int count; + switch (match) { + case ROUTE_ENTRIES: + count = builder.table(FeedContract.Entry.TABLE_NAME) + .where(selection, selectionArgs) + .delete(db); + break; + case ROUTE_ENTRIES_ID: + String id = uri.getLastPathSegment(); + count = builder.table(FeedContract.Entry.TABLE_NAME) + .where(FeedContract.Entry._ID + "=?", id) + .where(selection, selectionArgs) + .delete(db); + break; + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + // Send broadcast to registered ContentObservers, to refresh UI. + Context ctx = getContext(); + assert ctx != null; + ctx.getContentResolver().notifyChange(uri, null, false); + return count; + } + + /** + * Update an etry in the database by URI. + */ + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + SelectionBuilder builder = new SelectionBuilder(); + final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); + final int match = sUriMatcher.match(uri); + int count; + switch (match) { + case ROUTE_ENTRIES: + count = builder.table(FeedContract.Entry.TABLE_NAME) + .where(selection, selectionArgs) + .update(db, values); + break; + case ROUTE_ENTRIES_ID: + String id = uri.getLastPathSegment(); + count = builder.table(FeedContract.Entry.TABLE_NAME) + .where(FeedContract.Entry._ID + "=?", id) + .where(selection, selectionArgs) + .update(db, values); + break; + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + Context ctx = getContext(); + assert ctx != null; + ctx.getContentResolver().notifyChange(uri, null, false); + return count; + } + + /** + * SQLite backend for @{link FeedProvider}. + * + * Provides access to an disk-backed, SQLite datastore which is utilized by FeedProvider. This + * database should never be accessed by other parts of the application directly. + */ + static class FeedDatabase extends SQLiteOpenHelper { + /** Schema version. */ + public static final int DATABASE_VERSION = 1; + /** Filename for SQLite file. */ + public static final String DATABASE_NAME = "feed.db"; + + private static final String TYPE_TEXT = " TEXT"; + private static final String TYPE_INTEGER = " INTEGER"; + private static final String COMMA_SEP = ","; + /** SQL statement to create "entry" table. */ + private static final String SQL_CREATE_ENTRIES = + "CREATE TABLE " + FeedContract.Entry.TABLE_NAME + " (" + + FeedContract.Entry._ID + " INTEGER PRIMARY KEY," + + FeedContract.Entry.COLUMN_NAME_ENTRY_ID + TYPE_TEXT + COMMA_SEP + + FeedContract.Entry.COLUMN_NAME_TITLE + TYPE_TEXT + COMMA_SEP + + FeedContract.Entry.COLUMN_NAME_LINK + TYPE_TEXT + COMMA_SEP + + FeedContract.Entry.COLUMN_NAME_PUBLISHED + TYPE_INTEGER + ")"; + + /** SQL statement to drop "entry" table. */ + private static final String SQL_DELETE_ENTRIES = + "DROP TABLE IF EXISTS " + FeedContract.Entry.TABLE_NAME; + + public FeedDatabase(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL(SQL_CREATE_ENTRIES); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + // This database is only a cache for online data, so its upgrade policy is + // to simply to discard the data and start over + db.execSQL(SQL_DELETE_ENTRIES); + onCreate(db); + } + } +} diff --git a/samples/browseable/BasicSyncAdapter/src/com.example.android.common/accounts/GenericAccountService.java b/samples/browseable/BasicSyncAdapter/src/com.example.android.common/accounts/GenericAccountService.java new file mode 100644 index 000000000..0cd499a88 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/src/com.example.android.common/accounts/GenericAccountService.java @@ -0,0 +1,128 @@ +/* + * 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.common.accounts; + +import android.accounts.AbstractAccountAuthenticator; +import android.accounts.Account; +import android.accounts.AccountAuthenticatorResponse; +import android.accounts.NetworkErrorException; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; +import android.util.Log; + +public class GenericAccountService extends Service { + private static final String TAG = "GenericAccountService"; + public static final String ACCOUNT_NAME = "Account"; + private Authenticator mAuthenticator; + + /** + * Obtain a handle to the {@link android.accounts.Account} used for sync in this application. + * + * <p>It is important that the accountType specified here matches the value in your sync adapter + * configuration XML file for android.accounts.AccountAuthenticator (often saved in + * res/xml/syncadapter.xml). If this is not set correctly, you'll receive an error indicating + * that "caller uid XXXXX is different than the authenticator's uid". + * + * @param accountType AccountType defined in the configuration XML file for + * android.accounts.AccountAuthenticator (e.g. res/xml/syncadapter.xml). + * @return Handle to application's account (not guaranteed to resolve unless CreateSyncAccount() + * has been called) + */ + public static Account GetAccount(String accountType) { + // Note: Normally the account name is set to the user's identity (username or email + // address). However, since we aren't actually using any user accounts, it makes more sense + // to use a generic string in this case. + // + // This string should *not* be localized. If the user switches locale, we would not be + // able to locate the old account, and may erroneously register multiple accounts. + final String accountName = ACCOUNT_NAME; + return new Account(accountName, accountType); + } + + @Override + public void onCreate() { + Log.i(TAG, "Service created"); + mAuthenticator = new Authenticator(this); + } + + @Override + public void onDestroy() { + Log.i(TAG, "Service destroyed"); + } + + @Override + public IBinder onBind(Intent intent) { + return mAuthenticator.getIBinder(); + } + + public class Authenticator extends AbstractAccountAuthenticator { + public Authenticator(Context context) { + super(context); + } + + @Override + public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse, + String s) { + throw new UnsupportedOperationException(); + } + + @Override + public Bundle addAccount(AccountAuthenticatorResponse accountAuthenticatorResponse, + String s, String s2, String[] strings, Bundle bundle) + throws NetworkErrorException { + return null; + } + + @Override + public Bundle confirmCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, + Account account, Bundle bundle) + throws NetworkErrorException { + return null; + } + + @Override + public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse, + Account account, String s, Bundle bundle) + throws NetworkErrorException { + throw new UnsupportedOperationException(); + } + + @Override + public String getAuthTokenLabel(String s) { + throw new UnsupportedOperationException(); + } + + @Override + public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, + Account account, String s, Bundle bundle) + throws NetworkErrorException { + throw new UnsupportedOperationException(); + } + + @Override + public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse, + Account account, String[] strings) + throws NetworkErrorException { + throw new UnsupportedOperationException(); + } + } + +} + diff --git a/samples/browseable/BasicSyncAdapter/src/com.example.android.common/db/SelectionBuilder.java b/samples/browseable/BasicSyncAdapter/src/com.example.android.common/db/SelectionBuilder.java new file mode 100644 index 000000000..a1964c5f3 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/src/com.example.android.common/db/SelectionBuilder.java @@ -0,0 +1,356 @@ +/* + * 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. + */ + +/* + * Modifications: + * -Imported from AOSP frameworks/base/core/java/com/android/internal/content + * -Changed package name + */ + +package com.example.android.common.db; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.text.TextUtils; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Helper for building selection clauses for {@link SQLiteDatabase}. + * + * <p>This class provides a convenient frontend for working with SQL. Instead of composing statements + * manually using string concatenation, method calls are used to construct the statement one + * clause at a time. These methods can be chained together. + * + * <p>If multiple where() statements are provided, they're combined using {@code AND}. + * + * <p>Example: + * + * <pre> + * SelectionBuilder builder = new SelectionBuilder(); + * Cursor c = builder.table(FeedContract.Entry.TABLE_NAME) // String TABLE_NAME = "entry" + * .where(FeedContract.Entry._ID + "=?", id); // String _ID = "_ID" + * .query(db, projection, sortOrder) + * + * </pre> + * + * <p>In this example, the table name and filters ({@code WHERE} clauses) are both explicitly + * specified via method call. SelectionBuilder takes care of issuing a "query" command to the + * database, and returns the resulting {@link Cursor} object. + * + * <p>Inner {@code JOIN}s can be accomplished using the mapToTable() function. The map() function + * can be used to create new columns based on arbitrary (SQL-based) criteria. In advanced usage, + * entire subqueries can be passed into the map() function. + * + * <p>Advanced example: + * + * <pre> + * // String SESSIONS_JOIN_BLOCKS_ROOMS = "sessions " + * // + "LEFT OUTER JOIN blocks ON sessions.block_id=blocks.block_id " + * // + "LEFT OUTER JOIN rooms ON sessions.room_id=rooms.room_id"; + * + * // String Subquery.BLOCK_NUM_STARRED_SESSIONS = + * // "(SELECT COUNT(1) FROM " + * // + Tables.SESSIONS + " WHERE " + Qualified.SESSIONS_BLOCK_ID + "=" + * // + Qualified.BLOCKS_BLOCK_ID + " AND " + Qualified.SESSIONS_STARRED + "=1)"; + * + * String Subqery.BLOCK_SESSIONS_COUNT = + * Cursor c = builder.table(Tables.SESSIONS_JOIN_BLOCKS_ROOMS) + * .map(Blocks.NUM_STARRED_SESSIONS, Subquery.BLOCK_NUM_STARRED_SESSIONS) + * .mapToTable(Sessions._ID, Tables.SESSIONS) + * .mapToTable(Sessions.SESSION_ID, Tables.SESSIONS) + * .mapToTable(Sessions.BLOCK_ID, Tables.SESSIONS) + * .mapToTable(Sessions.ROOM_ID, Tables.SESSIONS) + * .where(Qualified.SESSIONS_BLOCK_ID + "=?", blockId); + * </pre> + * + * <p>In this example, we have two different types of {@code JOIN}s: a left outer join using a + * modified table name (since this class doesn't directly support these), and an inner join using + * the mapToTable() function. The map() function is used to insert a count based on specific + * criteria, executed as a sub-query. + * + * This class is <em>not</em> thread safe. + */ +public class SelectionBuilder { + private static final String TAG = "basicsyncadapter"; + + private String mTable = null; + private Map<String, String> mProjectionMap = new HashMap<String, String>(); + private StringBuilder mSelection = new StringBuilder(); + private ArrayList<String> mSelectionArgs = new ArrayList<String>(); + + /** + * Reset any internal state, allowing this builder to be recycled. + * + * <p>Calling this method is more efficient than creating a new SelectionBuilder object. + * + * @return Fluent interface + */ + public SelectionBuilder reset() { + mTable = null; + mSelection.setLength(0); + mSelectionArgs.clear(); + return this; + } + + /** + * Append the given selection clause to the internal state. Each clause is + * surrounded with parenthesis and combined using {@code AND}. + * + * <p>In the most basic usage, simply provide a selection in SQL {@code WHERE} statement format. + * + * <p>Example: + * + * <pre> + * .where("blog_posts.category = 'PROGRAMMING'); + * </pre> + * + * <p>User input should never be directly supplied as as part of the selection statement. + * Instead, use positional parameters in your selection statement, then pass the user input + * in via the selectionArgs parameter. This prevents SQL escape characters in user input from + * causing unwanted side effects. (Failure to follow this convention may have security + * implications.) + * + * <p>Positional parameters are specified using the '?' character. + * + * <p>Example: + * <pre> + * .where("blog_posts.title contains ?, userSearchString); + * </pre> + * + * @param selection SQL where statement + * @param selectionArgs Values to substitute for positional parameters ('?' characters in + * {@code selection} statement. Will be automatically escaped. + * @return Fluent interface + */ + public SelectionBuilder where(String selection, String... selectionArgs) { + if (TextUtils.isEmpty(selection)) { + if (selectionArgs != null && selectionArgs.length > 0) { + throw new IllegalArgumentException( + "Valid selection required when including arguments="); + } + + // Shortcut when clause is empty + return this; + } + + if (mSelection.length() > 0) { + mSelection.append(" AND "); + } + + mSelection.append("(").append(selection).append(")"); + if (selectionArgs != null) { + Collections.addAll(mSelectionArgs, selectionArgs); + } + + return this; + } + + /** + * Table name to use for SQL {@code FROM} statement. + * + * <p>This method may only be called once. If multiple tables are required, concatenate them + * in SQL-format (typically comma-separated). + * + * <p>If you need to do advanced {@code JOIN}s, they can also be specified here. + * + * See also: mapToTable() + * + * @param table Table name + * @return Fluent interface + */ + public SelectionBuilder table(String table) { + mTable = table; + return this; + } + + /** + * Verify that a table name has been supplied using table(). + * + * @throws IllegalStateException if table not set + */ + private void assertTable() { + if (mTable == null) { + throw new IllegalStateException("Table not specified"); + } + } + + /** + * Perform an inner join. + * + * <p>Map columns from a secondary table onto the current result set. References to the column + * specified in {@code column} will be replaced with {@code table.column} in the SQL {@code + * SELECT} clause. + * + * @param column Column name to join on. Must be the same in both tables. + * @param table Secondary table to join. + * @return Fluent interface + */ + public SelectionBuilder mapToTable(String column, String table) { + mProjectionMap.put(column, table + "." + column); + return this; + } + + /** + * Create a new column based on custom criteria (such as aggregate functions). + * + * <p>This adds a new column to the result set, based upon custom criteria in SQL format. This + * is equivalent to the SQL statement: {@code SELECT toClause AS fromColumn} + * + * <p>This method is useful for executing SQL sub-queries. + * + * @param fromColumn Name of column for mapping + * @param toClause SQL string representing data to be mapped + * @return Fluent interface + */ + public SelectionBuilder map(String fromColumn, String toClause) { + mProjectionMap.put(fromColumn, toClause + " AS " + fromColumn); + return this; + } + + /** + * Return selection string based on current internal state. + * + * @return Current selection as a SQL statement + * @see #getSelectionArgs() + */ + public String getSelection() { + return mSelection.toString(); + + } + + /** + * Return selection arguments based on current internal state. + * + * @see #getSelection() + */ + public String[] getSelectionArgs() { + return mSelectionArgs.toArray(new String[mSelectionArgs.size()]); + } + + /** + * Process user-supplied projection (column list). + * + * <p>In cases where a column is mapped to another data source (either another table, or an + * SQL sub-query), the column name will be replaced with a more specific, SQL-compatible + * representation. + * + * Assumes that incoming columns are non-null. + * + * <p>See also: map(), mapToTable() + * + * @param columns User supplied projection (column list). + */ + private void mapColumns(String[] columns) { + for (int i = 0; i < columns.length; i++) { + final String target = mProjectionMap.get(columns[i]); + if (target != null) { + columns[i] = target; + } + } + } + + /** + * Return a description of this builder's state. Does NOT output SQL. + * + * @return Human-readable internal state + */ + @Override + public String toString() { + return "SelectionBuilder[table=" + mTable + ", selection=" + getSelection() + + ", selectionArgs=" + Arrays.toString(getSelectionArgs()) + "]"; + } + + /** + * Execute query (SQL {@code SELECT}) against specified database. + * + * <p>Using a null projection (column list) is not supported. + * + * @param db Database to query. + * @param columns Database projection (column list) to return, must be non-NULL. + * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause (excluding the + * ORDER BY itself). Passing null will use the default sort order, which may be + * unordered. + * @return A {@link Cursor} object, which is positioned before the first entry. Note that + * {@link Cursor}s are not synchronized, see the documentation for more details. + */ + public Cursor query(SQLiteDatabase db, String[] columns, String orderBy) { + return query(db, columns, null, null, orderBy, null); + } + + /** + * Execute query ({@code SELECT}) against database. + * + * <p>Using a null projection (column list) is not supported. + * + * @param db Database to query. + * @param columns Database projection (column list) to return, must be non-null. + * @param groupBy A filter declaring how to group rows, formatted as an SQL GROUP BY clause + * (excluding the GROUP BY itself). Passing null will cause the rows to not be + * grouped. + * @param having A filter declare which row groups to include in the cursor, if row grouping is + * being used, formatted as an SQL HAVING clause (excluding the HAVING itself). + * Passing null will cause all row groups to be included, and is required when + * row grouping is not being used. + * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause (excluding the + * ORDER BY itself). Passing null will use the default sort order, which may be + * unordered. + * @param limit Limits the number of rows returned by the query, formatted as LIMIT clause. + * Passing null denotes no LIMIT clause. + * @return A {@link Cursor} object, which is positioned before the first entry. Note that + * {@link Cursor}s are not synchronized, see the documentation for more details. + */ + public Cursor query(SQLiteDatabase db, String[] columns, String groupBy, + String having, String orderBy, String limit) { + assertTable(); + if (columns != null) mapColumns(columns); + Log.v(TAG, "query(columns=" + Arrays.toString(columns) + ") " + this); + return db.query(mTable, columns, getSelection(), getSelectionArgs(), groupBy, having, + orderBy, limit); + } + + /** + * Execute an {@code UPDATE} against database. + * + * @param db Database to query. + * @param values A map from column names to new column values. null is a valid value that will + * be translated to NULL + * @return The number of rows affected. + */ + public int update(SQLiteDatabase db, ContentValues values) { + assertTable(); + Log.v(TAG, "update() " + this); + return db.update(mTable, values, getSelection(), getSelectionArgs()); + } + + /** + * Execute {@code DELETE} against database. + * + * @param db Database to query. + * @return The number of rows affected. + */ + public int delete(SQLiteDatabase db) { + assertTable(); + Log.v(TAG, "delete() " + this); + return db.delete(mTable, getSelection(), getSelectionArgs()); + } +} diff --git a/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/Log.java b/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/LogNode.java b/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/LogView.java b/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/BasicSyncAdapter/src/com.example.android.common/logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/BluetoothLeGatt/AndroidManifest.xml b/samples/browseable/BluetoothLeGatt/AndroidManifest.xml new file mode 100644 index 000000000..babd6df2d --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/AndroidManifest.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.bluetoothlegatt" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk android:minSdkVersion="18" + android:targetSdkVersion="18"/> + <!-- Declare this required feature if you want to make the app available to BLE-capable + devices only. If you want to make your app available to devices that don't support BLE, + you should omit this in the manifest. Instead, determine BLE capability by using + PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) --> + <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/> + + <uses-permission android:name="android.permission.BLUETOOTH"/> + <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> + + <application android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@android:style/Theme.Holo.Light"> + <activity android:name=".DeviceScanActivity" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + <activity android:name=".DeviceControlActivity"/> + <service android:name=".BluetoothLeService" android:enabled="true"/> + </application> + +</manifest> diff --git a/samples/browseable/BluetoothLeGatt/_index.jd b/samples/browseable/BluetoothLeGatt/_index.jd new file mode 100644 index 000000000..e30bb0c7f --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/_index.jd @@ -0,0 +1,13 @@ + + + +page.tags="BluetoothLeGatt" +sample.group=Connectivity +@jd:body + +<p> + + This sample demonstrates how to use the Bluetooth LE Generic Attribute Profile (GATT) + to transmit arbitrary data between devices. + + </p> diff --git a/samples/browseable/BluetoothLeGatt/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BluetoothLeGatt/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..15367c050 --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/BluetoothLeGatt/res/drawable-hdpi/tile.9.png b/samples/browseable/BluetoothLeGatt/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/BluetoothLeGatt/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BluetoothLeGatt/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..ba810a766 --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/BluetoothLeGatt/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BluetoothLeGatt/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..14f1d7467 --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/BluetoothLeGatt/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BluetoothLeGatt/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..81ff9cc8b --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/BluetoothLeGatt/res/layout/actionbar_indeterminate_progress.xml b/samples/browseable/BluetoothLeGatt/res/layout/actionbar_indeterminate_progress.xml new file mode 100644 index 000000000..a950833f8 --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/layout/actionbar_indeterminate_progress.xml @@ -0,0 +1,23 @@ +<!-- + Copyright 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. + --> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="wrap_content" + android:layout_width="56dp" + android:minWidth="56dp"> + <ProgressBar android:layout_width="32dp" + android:layout_height="32dp" + android:layout_gravity="center"/> +</FrameLayout> diff --git a/samples/browseable/BluetoothLeGatt/res/layout/activity_main.xml b/samples/browseable/BluetoothLeGatt/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/BluetoothLeGatt/res/layout/gatt_services_characteristics.xml b/samples/browseable/BluetoothLeGatt/res/layout/gatt_services_characteristics.xml new file mode 100644 index 000000000..2f3106103 --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/layout/gatt_services_characteristics.xml @@ -0,0 +1,71 @@ +<?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:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="10dp"> + <LinearLayout android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="10dp"> + <TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/label_device_address" + android:textSize="18sp"/> + <Space android:layout_width="5dp" + android:layout_height="wrap_content"/> + <TextView android:id="@+id/device_address" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="18sp"/> + </LinearLayout> + <LinearLayout android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="10dp"> + <TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/label_state" + android:textSize="18sp"/> + <Space android:layout_width="5dp" + android:layout_height="wrap_content"/> + <TextView android:id="@+id/connection_state" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/disconnected" + android:textSize="18sp"/> + </LinearLayout> + <LinearLayout android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="10dp"> + <TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/label_data" + android:textSize="18sp"/> + <Space android:layout_width="5dp" + android:layout_height="wrap_content"/> + <TextView android:id="@+id/data_value" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/no_data" + android:textSize="18sp"/> + </LinearLayout> + <ExpandableListView android:id="@+id/gatt_services_list" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> +</LinearLayout>
\ No newline at end of file diff --git a/samples/browseable/BluetoothLeGatt/res/layout/listitem_device.xml b/samples/browseable/BluetoothLeGatt/res/layout/listitem_device.xml new file mode 100644 index 000000000..eff44fcdd --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/layout/listitem_device.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. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView android:id="@+id/device_name" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="24dp"/> + <TextView android:id="@+id/device_address" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="12dp"/> +</LinearLayout>
\ No newline at end of file diff --git a/samples/browseable/BluetoothLeGatt/res/menu/gatt_services.xml b/samples/browseable/BluetoothLeGatt/res/menu/gatt_services.xml new file mode 100644 index 000000000..464d32ff7 --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/menu/gatt_services.xml @@ -0,0 +1,29 @@ +<?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. +--> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/menu_refresh" + android:checkable="false" + android:orderInCategory="1" + android:showAsAction="ifRoom"/> + <item android:id="@+id/menu_connect" + android:title="@string/menu_connect" + android:orderInCategory="100" + android:showAsAction="ifRoom|withText"/> + <item android:id="@+id/menu_disconnect" + android:title="@string/menu_disconnect" + android:orderInCategory="101" + android:showAsAction="ifRoom|withText"/> +</menu> diff --git a/samples/browseable/BluetoothLeGatt/res/menu/main.xml b/samples/browseable/BluetoothLeGatt/res/menu/main.xml new file mode 100644 index 000000000..39dd66aa4 --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/menu/main.xml @@ -0,0 +1,29 @@ +<?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. +--> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/menu_refresh" + android:checkable="false" + android:orderInCategory="1" + android:showAsAction="ifRoom"/> + <item android:id="@+id/menu_scan" + android:title="@string/menu_scan" + android:orderInCategory="100" + android:showAsAction="ifRoom|withText"/> + <item android:id="@+id/menu_stop" + android:title="@string/menu_stop" + android:orderInCategory="101" + android:showAsAction="ifRoom|withText"/> +</menu> diff --git a/samples/browseable/BluetoothLeGatt/res/values-sw600dp/dimens.xml b/samples/browseable/BluetoothLeGatt/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BluetoothLeGatt/res/values-sw600dp/styles.xml b/samples/browseable/BluetoothLeGatt/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/BluetoothLeGatt/res/values/base-strings.xml b/samples/browseable/BluetoothLeGatt/res/values/base-strings.xml new file mode 100644 index 000000000..58b49a95f --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/values/base-strings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">BluetoothLeGatt</string> + <string name="intro_message"> + <![CDATA[ + + + This sample demonstrates how to use the Bluetooth LE Generic Attribute Profile (GATT) + to transmit arbitrary data between devices. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/BluetoothLeGatt/res/values/dimens.xml b/samples/browseable/BluetoothLeGatt/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BluetoothLeGatt/res/values/strings.xml b/samples/browseable/BluetoothLeGatt/res/values/strings.xml new file mode 100644 index 000000000..19f3dce9f --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/values/strings.xml @@ -0,0 +1,36 @@ +<?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="ble_not_supported">BLE is not supported</string> + <string name="label_data">Data:</string> + <string name="label_device_address">Device address:</string> + <string name="label_state">State:</string> + <string name="no_data">No data</string> + <string name="connected">Connected</string> + <string name="disconnected">Disconnected</string> + <string name="title_devices">BLE Device Scan</string> + <string name="error_bluetooth_not_supported">Bluetooth not supported.</string> + + <string name="unknown_device">Unknown device</string> + <string name="unknown_characteristic">Unknown characteristic</string> + <string name="unknown_service">Unknown service</string> + + <!-- Menu items --> + <string name="menu_connect">Connect</string> + <string name="menu_disconnect">Disconnect</string> + <string name="menu_scan">Scan</string> + <string name="menu_stop">Stop</string> +</resources> diff --git a/samples/browseable/BluetoothLeGatt/res/values/styles.xml b/samples/browseable/BluetoothLeGatt/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/BluetoothLeService.java b/samples/browseable/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/BluetoothLeService.java new file mode 100644 index 000000000..694faaf9d --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/BluetoothLeService.java @@ -0,0 +1,319 @@ +/* + * 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.bluetoothlegatt; + +import android.app.Service; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCallback; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; +import android.bluetooth.BluetoothGattService; +import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothProfile; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.util.Log; + +import java.util.List; +import java.util.UUID; + +/** + * Service for managing connection and data communication with a GATT server hosted on a + * given Bluetooth LE device. + */ +public class BluetoothLeService extends Service { + private final static String TAG = BluetoothLeService.class.getSimpleName(); + + private BluetoothManager mBluetoothManager; + private BluetoothAdapter mBluetoothAdapter; + private String mBluetoothDeviceAddress; + private BluetoothGatt mBluetoothGatt; + private int mConnectionState = STATE_DISCONNECTED; + + private static final int STATE_DISCONNECTED = 0; + private static final int STATE_CONNECTING = 1; + private static final int STATE_CONNECTED = 2; + + public final static String ACTION_GATT_CONNECTED = + "com.example.bluetooth.le.ACTION_GATT_CONNECTED"; + public final static String ACTION_GATT_DISCONNECTED = + "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"; + public final static String ACTION_GATT_SERVICES_DISCOVERED = + "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED"; + public final static String ACTION_DATA_AVAILABLE = + "com.example.bluetooth.le.ACTION_DATA_AVAILABLE"; + public final static String EXTRA_DATA = + "com.example.bluetooth.le.EXTRA_DATA"; + + public final static UUID UUID_HEART_RATE_MEASUREMENT = + UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT); + + // Implements callback methods for GATT events that the app cares about. For example, + // connection change and services discovered. + private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { + @Override + public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { + String intentAction; + if (newState == BluetoothProfile.STATE_CONNECTED) { + intentAction = ACTION_GATT_CONNECTED; + mConnectionState = STATE_CONNECTED; + broadcastUpdate(intentAction); + Log.i(TAG, "Connected to GATT server."); + // Attempts to discover services after successful connection. + Log.i(TAG, "Attempting to start service discovery:" + + mBluetoothGatt.discoverServices()); + + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + intentAction = ACTION_GATT_DISCONNECTED; + mConnectionState = STATE_DISCONNECTED; + Log.i(TAG, "Disconnected from GATT server."); + broadcastUpdate(intentAction); + } + } + + @Override + public void onServicesDiscovered(BluetoothGatt gatt, int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); + } else { + Log.w(TAG, "onServicesDiscovered received: " + status); + } + } + + @Override + public void onCharacteristicRead(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, + int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); + } + } + + @Override + public void onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { + broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); + } + }; + + private void broadcastUpdate(final String action) { + final Intent intent = new Intent(action); + sendBroadcast(intent); + } + + private void broadcastUpdate(final String action, + final BluetoothGattCharacteristic characteristic) { + final Intent intent = new Intent(action); + + // This is special handling for the Heart Rate Measurement profile. Data parsing is + // carried out as per profile specifications: + // http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml + if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) { + int flag = characteristic.getProperties(); + int format = -1; + if ((flag & 0x01) != 0) { + format = BluetoothGattCharacteristic.FORMAT_UINT16; + Log.d(TAG, "Heart rate format UINT16."); + } else { + format = BluetoothGattCharacteristic.FORMAT_UINT8; + Log.d(TAG, "Heart rate format UINT8."); + } + final int heartRate = characteristic.getIntValue(format, 1); + Log.d(TAG, String.format("Received heart rate: %d", heartRate)); + intent.putExtra(EXTRA_DATA, String.valueOf(heartRate)); + } else { + // For all other profiles, writes the data formatted in HEX. + final byte[] data = characteristic.getValue(); + if (data != null && data.length > 0) { + final StringBuilder stringBuilder = new StringBuilder(data.length); + for(byte byteChar : data) + stringBuilder.append(String.format("%02X ", byteChar)); + intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString()); + } + } + sendBroadcast(intent); + } + + public class LocalBinder extends Binder { + BluetoothLeService getService() { + return BluetoothLeService.this; + } + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + @Override + public boolean onUnbind(Intent intent) { + // After using a given device, you should make sure that BluetoothGatt.close() is called + // such that resources are cleaned up properly. In this particular example, close() is + // invoked when the UI is disconnected from the Service. + close(); + return super.onUnbind(intent); + } + + private final IBinder mBinder = new LocalBinder(); + + /** + * Initializes a reference to the local Bluetooth adapter. + * + * @return Return true if the initialization is successful. + */ + public boolean initialize() { + // For API level 18 and above, get a reference to BluetoothAdapter through + // BluetoothManager. + if (mBluetoothManager == null) { + mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); + if (mBluetoothManager == null) { + Log.e(TAG, "Unable to initialize BluetoothManager."); + return false; + } + } + + mBluetoothAdapter = mBluetoothManager.getAdapter(); + if (mBluetoothAdapter == null) { + Log.e(TAG, "Unable to obtain a BluetoothAdapter."); + return false; + } + + return true; + } + + /** + * Connects to the GATT server hosted on the Bluetooth LE device. + * + * @param address The device address of the destination device. + * + * @return Return true if the connection is initiated successfully. The connection result + * is reported asynchronously through the + * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} + * callback. + */ + public boolean connect(final String address) { + if (mBluetoothAdapter == null || address == null) { + Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); + return false; + } + + // Previously connected device. Try to reconnect. + if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) + && mBluetoothGatt != null) { + Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection."); + if (mBluetoothGatt.connect()) { + mConnectionState = STATE_CONNECTING; + return true; + } else { + return false; + } + } + + final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); + if (device == null) { + Log.w(TAG, "Device not found. Unable to connect."); + return false; + } + // We want to directly connect to the device, so we are setting the autoConnect + // parameter to false. + mBluetoothGatt = device.connectGatt(this, false, mGattCallback); + Log.d(TAG, "Trying to create a new connection."); + mBluetoothDeviceAddress = address; + mConnectionState = STATE_CONNECTING; + return true; + } + + /** + * Disconnects an existing connection or cancel a pending connection. The disconnection result + * is reported asynchronously through the + * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} + * callback. + */ + public void disconnect() { + if (mBluetoothAdapter == null || mBluetoothGatt == null) { + Log.w(TAG, "BluetoothAdapter not initialized"); + return; + } + mBluetoothGatt.disconnect(); + } + + /** + * After using a given BLE device, the app must call this method to ensure resources are + * released properly. + */ + public void close() { + if (mBluetoothGatt == null) { + return; + } + mBluetoothGatt.close(); + mBluetoothGatt = null; + } + + /** + * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported + * asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)} + * callback. + * + * @param characteristic The characteristic to read from. + */ + public void readCharacteristic(BluetoothGattCharacteristic characteristic) { + if (mBluetoothAdapter == null || mBluetoothGatt == null) { + Log.w(TAG, "BluetoothAdapter not initialized"); + return; + } + mBluetoothGatt.readCharacteristic(characteristic); + } + + /** + * Enables or disables notification on a give characteristic. + * + * @param characteristic Characteristic to act on. + * @param enabled If true, enable notification. False otherwise. + */ + public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, + boolean enabled) { + if (mBluetoothAdapter == null || mBluetoothGatt == null) { + Log.w(TAG, "BluetoothAdapter not initialized"); + return; + } + mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); + + // This is specific to Heart Rate Measurement. + if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) { + BluetoothGattDescriptor descriptor = characteristic.getDescriptor( + UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG)); + descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); + mBluetoothGatt.writeDescriptor(descriptor); + } + } + + /** + * Retrieves a list of supported GATT services on the connected device. This should be + * invoked only after {@code BluetoothGatt#discoverServices()} completes successfully. + * + * @return A {@code List} of supported services. + */ + public List<BluetoothGattService> getSupportedGattServices() { + if (mBluetoothGatt == null) return null; + + return mBluetoothGatt.getServices(); + } +} diff --git a/samples/browseable/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/DeviceControlActivity.java b/samples/browseable/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/DeviceControlActivity.java new file mode 100644 index 000000000..dc2f90bdf --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/DeviceControlActivity.java @@ -0,0 +1,309 @@ +/* + * 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.bluetoothlegatt; + +import android.app.Activity; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattService; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.ExpandableListView; +import android.widget.SimpleExpandableListAdapter; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * For a given BLE device, this Activity provides the user interface to connect, display data, + * and display GATT services and characteristics supported by the device. The Activity + * communicates with {@code BluetoothLeService}, which in turn interacts with the + * Bluetooth LE API. + */ +public class DeviceControlActivity extends Activity { + private final static String TAG = DeviceControlActivity.class.getSimpleName(); + + public static final String EXTRAS_DEVICE_NAME = "DEVICE_NAME"; + public static final String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS"; + + private TextView mConnectionState; + private TextView mDataField; + private String mDeviceName; + private String mDeviceAddress; + private ExpandableListView mGattServicesList; + private BluetoothLeService mBluetoothLeService; + private ArrayList<ArrayList<BluetoothGattCharacteristic>> mGattCharacteristics = + new ArrayList<ArrayList<BluetoothGattCharacteristic>>(); + private boolean mConnected = false; + private BluetoothGattCharacteristic mNotifyCharacteristic; + + private final String LIST_NAME = "NAME"; + private final String LIST_UUID = "UUID"; + + // Code to manage Service lifecycle. + private final ServiceConnection mServiceConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName componentName, IBinder service) { + mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService(); + if (!mBluetoothLeService.initialize()) { + Log.e(TAG, "Unable to initialize Bluetooth"); + finish(); + } + // Automatically connects to the device upon successful start-up initialization. + mBluetoothLeService.connect(mDeviceAddress); + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + mBluetoothLeService = null; + } + }; + + // Handles various events fired by the Service. + // ACTION_GATT_CONNECTED: connected to a GATT server. + // ACTION_GATT_DISCONNECTED: disconnected from a GATT server. + // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services. + // ACTION_DATA_AVAILABLE: received data from the device. This can be a result of read + // or notification operations. + private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) { + mConnected = true; + updateConnectionState(R.string.connected); + invalidateOptionsMenu(); + } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { + mConnected = false; + updateConnectionState(R.string.disconnected); + invalidateOptionsMenu(); + clearUI(); + } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) { + // Show all the supported services and characteristics on the user interface. + displayGattServices(mBluetoothLeService.getSupportedGattServices()); + } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) { + displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA)); + } + } + }; + + // If a given GATT characteristic is selected, check for supported features. This sample + // demonstrates 'Read' and 'Notify' features. See + // http://d.android.com/reference/android/bluetooth/BluetoothGatt.html for the complete + // list of supported characteristic features. + private final ExpandableListView.OnChildClickListener servicesListClickListner = + new ExpandableListView.OnChildClickListener() { + @Override + public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, + int childPosition, long id) { + if (mGattCharacteristics != null) { + final BluetoothGattCharacteristic characteristic = + mGattCharacteristics.get(groupPosition).get(childPosition); + final int charaProp = characteristic.getProperties(); + if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) { + // If there is an active notification on a characteristic, clear + // it first so it doesn't update the data field on the user interface. + if (mNotifyCharacteristic != null) { + mBluetoothLeService.setCharacteristicNotification( + mNotifyCharacteristic, false); + mNotifyCharacteristic = null; + } + mBluetoothLeService.readCharacteristic(characteristic); + } + if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) { + mNotifyCharacteristic = characteristic; + mBluetoothLeService.setCharacteristicNotification( + characteristic, true); + } + return true; + } + return false; + } + }; + + private void clearUI() { + mGattServicesList.setAdapter((SimpleExpandableListAdapter) null); + mDataField.setText(R.string.no_data); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.gatt_services_characteristics); + + final Intent intent = getIntent(); + mDeviceName = intent.getStringExtra(EXTRAS_DEVICE_NAME); + mDeviceAddress = intent.getStringExtra(EXTRAS_DEVICE_ADDRESS); + + // Sets up UI references. + ((TextView) findViewById(R.id.device_address)).setText(mDeviceAddress); + mGattServicesList = (ExpandableListView) findViewById(R.id.gatt_services_list); + mGattServicesList.setOnChildClickListener(servicesListClickListner); + mConnectionState = (TextView) findViewById(R.id.connection_state); + mDataField = (TextView) findViewById(R.id.data_value); + + getActionBar().setTitle(mDeviceName); + getActionBar().setDisplayHomeAsUpEnabled(true); + Intent gattServiceIntent = new Intent(this, BluetoothLeService.class); + bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE); + } + + @Override + protected void onResume() { + super.onResume(); + registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter()); + if (mBluetoothLeService != null) { + final boolean result = mBluetoothLeService.connect(mDeviceAddress); + Log.d(TAG, "Connect request result=" + result); + } + } + + @Override + protected void onPause() { + super.onPause(); + unregisterReceiver(mGattUpdateReceiver); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + unbindService(mServiceConnection); + mBluetoothLeService = null; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.gatt_services, menu); + if (mConnected) { + menu.findItem(R.id.menu_connect).setVisible(false); + menu.findItem(R.id.menu_disconnect).setVisible(true); + } else { + menu.findItem(R.id.menu_connect).setVisible(true); + menu.findItem(R.id.menu_disconnect).setVisible(false); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch(item.getItemId()) { + case R.id.menu_connect: + mBluetoothLeService.connect(mDeviceAddress); + return true; + case R.id.menu_disconnect: + mBluetoothLeService.disconnect(); + return true; + case android.R.id.home: + onBackPressed(); + return true; + } + return super.onOptionsItemSelected(item); + } + + private void updateConnectionState(final int resourceId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + mConnectionState.setText(resourceId); + } + }); + } + + private void displayData(String data) { + if (data != null) { + mDataField.setText(data); + } + } + + // Demonstrates how to iterate through the supported GATT Services/Characteristics. + // In this sample, we populate the data structure that is bound to the ExpandableListView + // on the UI. + private void displayGattServices(List<BluetoothGattService> gattServices) { + if (gattServices == null) return; + String uuid = null; + String unknownServiceString = getResources().getString(R.string.unknown_service); + String unknownCharaString = getResources().getString(R.string.unknown_characteristic); + ArrayList<HashMap<String, String>> gattServiceData = new ArrayList<HashMap<String, String>>(); + ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData + = new ArrayList<ArrayList<HashMap<String, String>>>(); + mGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>(); + + // Loops through available GATT Services. + for (BluetoothGattService gattService : gattServices) { + HashMap<String, String> currentServiceData = new HashMap<String, String>(); + uuid = gattService.getUuid().toString(); + currentServiceData.put( + LIST_NAME, SampleGattAttributes.lookup(uuid, unknownServiceString)); + currentServiceData.put(LIST_UUID, uuid); + gattServiceData.add(currentServiceData); + + ArrayList<HashMap<String, String>> gattCharacteristicGroupData = + new ArrayList<HashMap<String, String>>(); + List<BluetoothGattCharacteristic> gattCharacteristics = + gattService.getCharacteristics(); + ArrayList<BluetoothGattCharacteristic> charas = + new ArrayList<BluetoothGattCharacteristic>(); + + // Loops through available Characteristics. + for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { + charas.add(gattCharacteristic); + HashMap<String, String> currentCharaData = new HashMap<String, String>(); + uuid = gattCharacteristic.getUuid().toString(); + currentCharaData.put( + LIST_NAME, SampleGattAttributes.lookup(uuid, unknownCharaString)); + currentCharaData.put(LIST_UUID, uuid); + gattCharacteristicGroupData.add(currentCharaData); + } + mGattCharacteristics.add(charas); + gattCharacteristicData.add(gattCharacteristicGroupData); + } + + SimpleExpandableListAdapter gattServiceAdapter = new SimpleExpandableListAdapter( + this, + gattServiceData, + android.R.layout.simple_expandable_list_item_2, + new String[] {LIST_NAME, LIST_UUID}, + new int[] { android.R.id.text1, android.R.id.text2 }, + gattCharacteristicData, + android.R.layout.simple_expandable_list_item_2, + new String[] {LIST_NAME, LIST_UUID}, + new int[] { android.R.id.text1, android.R.id.text2 } + ); + mGattServicesList.setAdapter(gattServiceAdapter); + } + + private static IntentFilter makeGattUpdateIntentFilter() { + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED); + intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE); + return intentFilter; + } +} diff --git a/samples/browseable/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/DeviceScanActivity.java b/samples/browseable/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/DeviceScanActivity.java new file mode 100644 index 000000000..9b86f7ab7 --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/DeviceScanActivity.java @@ -0,0 +1,268 @@ +/* + * 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.bluetoothlegatt; + +import android.app.Activity; +import android.app.ListActivity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.os.Handler; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; + +import java.util.ArrayList; + +/** + * Activity for scanning and displaying available Bluetooth LE devices. + */ +public class DeviceScanActivity extends ListActivity { + private LeDeviceListAdapter mLeDeviceListAdapter; + private BluetoothAdapter mBluetoothAdapter; + private boolean mScanning; + private Handler mHandler; + + private static final int REQUEST_ENABLE_BT = 1; + // Stops scanning after 10 seconds. + private static final long SCAN_PERIOD = 10000; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getActionBar().setTitle(R.string.title_devices); + mHandler = new Handler(); + + // Use this check to determine whether BLE is supported on the device. Then you can + // selectively disable BLE-related features. + if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { + Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); + finish(); + } + + // Initializes a Bluetooth adapter. For API level 18 and above, get a reference to + // BluetoothAdapter through BluetoothManager. + final BluetoothManager bluetoothManager = + (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); + mBluetoothAdapter = bluetoothManager.getAdapter(); + + // Checks if Bluetooth is supported on the device. + if (mBluetoothAdapter == null) { + Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show(); + finish(); + return; + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + if (!mScanning) { + menu.findItem(R.id.menu_stop).setVisible(false); + menu.findItem(R.id.menu_scan).setVisible(true); + menu.findItem(R.id.menu_refresh).setActionView(null); + } else { + menu.findItem(R.id.menu_stop).setVisible(true); + menu.findItem(R.id.menu_scan).setVisible(false); + menu.findItem(R.id.menu_refresh).setActionView( + R.layout.actionbar_indeterminate_progress); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_scan: + mLeDeviceListAdapter.clear(); + scanLeDevice(true); + break; + case R.id.menu_stop: + scanLeDevice(false); + break; + } + return true; + } + + @Override + protected void onResume() { + super.onResume(); + + // Ensures Bluetooth is enabled on the device. If Bluetooth is not currently enabled, + // fire an intent to display a dialog asking the user to grant permission to enable it. + if (!mBluetoothAdapter.isEnabled()) { + if (!mBluetoothAdapter.isEnabled()) { + Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); + } + } + + // Initializes list view adapter. + mLeDeviceListAdapter = new LeDeviceListAdapter(); + setListAdapter(mLeDeviceListAdapter); + scanLeDevice(true); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // User chose not to enable Bluetooth. + if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) { + finish(); + return; + } + super.onActivityResult(requestCode, resultCode, data); + } + + @Override + protected void onPause() { + super.onPause(); + scanLeDevice(false); + mLeDeviceListAdapter.clear(); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position); + if (device == null) return; + final Intent intent = new Intent(this, DeviceControlActivity.class); + intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, device.getName()); + intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress()); + if (mScanning) { + mBluetoothAdapter.stopLeScan(mLeScanCallback); + mScanning = false; + } + startActivity(intent); + } + + private void scanLeDevice(final boolean enable) { + if (enable) { + // Stops scanning after a pre-defined scan period. + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + mScanning = false; + mBluetoothAdapter.stopLeScan(mLeScanCallback); + invalidateOptionsMenu(); + } + }, SCAN_PERIOD); + + mScanning = true; + mBluetoothAdapter.startLeScan(mLeScanCallback); + } else { + mScanning = false; + mBluetoothAdapter.stopLeScan(mLeScanCallback); + } + invalidateOptionsMenu(); + } + + // Adapter for holding devices found through scanning. + private class LeDeviceListAdapter extends BaseAdapter { + private ArrayList<BluetoothDevice> mLeDevices; + private LayoutInflater mInflator; + + public LeDeviceListAdapter() { + super(); + mLeDevices = new ArrayList<BluetoothDevice>(); + mInflator = DeviceScanActivity.this.getLayoutInflater(); + } + + public void addDevice(BluetoothDevice device) { + if(!mLeDevices.contains(device)) { + mLeDevices.add(device); + } + } + + public BluetoothDevice getDevice(int position) { + return mLeDevices.get(position); + } + + public void clear() { + mLeDevices.clear(); + } + + @Override + public int getCount() { + return mLeDevices.size(); + } + + @Override + public Object getItem(int i) { + return mLeDevices.get(i); + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + ViewHolder viewHolder; + // General ListView optimization code. + if (view == null) { + view = mInflator.inflate(R.layout.listitem_device, null); + viewHolder = new ViewHolder(); + viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address); + viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name); + view.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) view.getTag(); + } + + BluetoothDevice device = mLeDevices.get(i); + final String deviceName = device.getName(); + if (deviceName != null && deviceName.length() > 0) + viewHolder.deviceName.setText(deviceName); + else + viewHolder.deviceName.setText(R.string.unknown_device); + viewHolder.deviceAddress.setText(device.getAddress()); + + return view; + } + } + + // Device scan callback. + private BluetoothAdapter.LeScanCallback mLeScanCallback = + new BluetoothAdapter.LeScanCallback() { + + @Override + public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { + runOnUiThread(new Runnable() { + @Override + public void run() { + mLeDeviceListAdapter.addDevice(device); + mLeDeviceListAdapter.notifyDataSetChanged(); + } + }); + } + }; + + static class ViewHolder { + TextView deviceName; + TextView deviceAddress; + } +}
\ No newline at end of file diff --git a/samples/browseable/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/SampleGattAttributes.java b/samples/browseable/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/SampleGattAttributes.java new file mode 100644 index 000000000..e8db74cd2 --- /dev/null +++ b/samples/browseable/BluetoothLeGatt/src/com.example.android.bluetoothlegatt/SampleGattAttributes.java @@ -0,0 +1,42 @@ +/* + * 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.bluetoothlegatt; + +import java.util.HashMap; + +/** + * This class includes a small subset of standard GATT attributes for demonstration purposes. + */ +public class SampleGattAttributes { + private static HashMap<String, String> attributes = new HashMap(); + public static String HEART_RATE_MEASUREMENT = "00002a37-0000-1000-8000-00805f9b34fb"; + public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb"; + + static { + // Sample Services. + attributes.put("0000180d-0000-1000-8000-00805f9b34fb", "Heart Rate Service"); + attributes.put("0000180a-0000-1000-8000-00805f9b34fb", "Device Information Service"); + // Sample Characteristics. + attributes.put(HEART_RATE_MEASUREMENT, "Heart Rate Measurement"); + attributes.put("00002a29-0000-1000-8000-00805f9b34fb", "Manufacturer Name String"); + } + + public static String lookup(String uuid, String defaultName) { + String name = attributes.get(uuid); + return name == null ? defaultName : name; + } +} diff --git a/samples/browseable/BorderlessButtons/AndroidManifest.xml b/samples/browseable/BorderlessButtons/AndroidManifest.xml new file mode 100644 index 000000000..34b28985d --- /dev/null +++ b/samples/browseable/BorderlessButtons/AndroidManifest.xml @@ -0,0 +1,48 @@ +<!-- + 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" + package="com.example.android.borderlessbuttons" + android:versionCode="1" + android:versionName="1.0"> + + <!-- + This sample requires API 11 for use of theme attributes such as ?android:buttonBarStyle + and ?android:borderlessButtonStyle, as well as LinearLayout's android:showDividers attribute. + A similar effect can be achieved by setting a clickable view's background to + ?android:selectableItemBackground. + + This sample requires API 14 for use of theme attributes such as + ?android:listPreferredItemPaddingLeft. + --> + <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17" /> + + <application android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@style/Theme.Sample" + android:allowBackup="true"> + + <activity android:name=".MainActivity" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + </application> + +</manifest> diff --git a/samples/browseable/BorderlessButtons/_index.jd b/samples/browseable/BorderlessButtons/_index.jd new file mode 100644 index 000000000..1047950ef --- /dev/null +++ b/samples/browseable/BorderlessButtons/_index.jd @@ -0,0 +1,13 @@ + + + +page.tags="BorderlessButtons" +sample.group=UI +@jd:body + +<p> + + This sample demonstrates the use of borderless buttons, bottom button bars + (OK and Cancel) and dividers to establish visual structure. + + </p> diff --git a/samples/browseable/BorderlessButtons/res/drawable-hdpi/ic_action_delete.png b/samples/browseable/BorderlessButtons/res/drawable-hdpi/ic_action_delete.png Binary files differnew file mode 100644 index 000000000..e9ce89e04 --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/drawable-hdpi/ic_action_delete.png diff --git a/samples/browseable/BorderlessButtons/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BorderlessButtons/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..b1efaf4b2 --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/BorderlessButtons/res/drawable-hdpi/tile.9.png b/samples/browseable/BorderlessButtons/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/BorderlessButtons/res/drawable-mdpi/ic_action_delete.png b/samples/browseable/BorderlessButtons/res/drawable-mdpi/ic_action_delete.png Binary files differnew file mode 100644 index 000000000..cedb1085b --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/drawable-mdpi/ic_action_delete.png diff --git a/samples/browseable/BorderlessButtons/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BorderlessButtons/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f5f9244f2 --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/BorderlessButtons/res/drawable-xhdpi/ic_action_delete.png b/samples/browseable/BorderlessButtons/res/drawable-xhdpi/ic_action_delete.png Binary files differnew file mode 100644 index 000000000..98c73da1f --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/drawable-xhdpi/ic_action_delete.png diff --git a/samples/browseable/BorderlessButtons/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BorderlessButtons/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..5d07b3f06 --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/BorderlessButtons/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BorderlessButtons/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6ef21e1f4 --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/BorderlessButtons/res/layout/activity_main.xml b/samples/browseable/BorderlessButtons/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/BorderlessButtons/res/layout/list_item.xml b/samples/browseable/BorderlessButtons/res/layout/list_item.xml new file mode 100644 index 000000000..1966be923 --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/layout/list_item.xml @@ -0,0 +1,87 @@ +<!-- + 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. + --> + +<!-- + For list items that contain secondary actions (in this case, 'delete'), + it's important to use dividers to distinguish the primary touch target from + the secondary action. This is done using android:showDividers and its + related attributes. + + The android:dividerPadding attribute insets the divider line by the given + amount on each side (in this case, top and bottom). Divider padding helps + establish visual hierarchy when several dividers are used in a screen. In + this case, the padding helps separate this vertical divider from horizontal + list item separators in the main ListView, and establishes a stronger + relationship between the delete action and the primary target to its left. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="?android:listPreferredItemHeight" + android:divider="?android:dividerVertical" + android:dividerPadding="8dp" + android:showDividers="middle"> + + <!-- + Any view or view group can become interactive by simply setting the + android:clickable and android:focusable attributes to true. + + When doing this, make sure to provide adequate touch feedback by also + setting the view background to ?android:selectableItemBackground. When + using the Holo theme, this drawable is transparent by default, but + changes to a translucent color overlay when the view is pressed or + focused. + --> + <LinearLayout android:id="@+id/primary_target" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:orientation="vertical" + android:paddingLeft="?android:listPreferredItemPaddingLeft" + android:paddingRight="?android:listPreferredItemPaddingRight" + android:clickable="true" + android:focusable="true" + android:gravity="center_vertical" + android:background="?android:selectableItemBackground"> + + <TextView style="?android:textAppearanceListItemSmall" + android:id="@android:id/text1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/dummy_title" /> + + <TextView style="?android:textAppearanceSmall" + android:id="@android:id/text2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/dummy_subtitle" /> + + </LinearLayout> + + <!-- + When using the Holo theme, setting a Button or ImageButton to + ?android:borderlessButtonStyle removes its border and sets the + background to ?android:selectableItemBackground, as described above. + --> + <ImageButton android:id="@+id/secondary_action" + style="?android:borderlessButtonStyle" + android:layout_width="@dimen/standard_touch_target_size" + android:layout_height="match_parent" + android:src="@drawable/ic_action_delete" + android:contentDescription="@string/delete_content_description" /> + +</LinearLayout> diff --git a/samples/browseable/BorderlessButtons/res/layout/sample_main.xml b/samples/browseable/BorderlessButtons/res/layout/sample_main.xml new file mode 100755 index 000000000..eacbf4b3f --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/layout/sample_main.xml @@ -0,0 +1,87 @@ +<!-- + 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 top-level LinearLayout uses a horizontal divider to visually + distinguish the top description box, list, and bottom button bar. + + android:showDividers="middle" draws dividers between each child view and + android:divider="?android:dividerHorizontal" indicates that the standard + horizontal system divider (set in the activity's theme) should be used to + draw the divider. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:divider="?android:dividerHorizontal" + android:showDividers="middle"> + + <TextView style="@style/Widget.DescriptionBar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/intro_message" /> + + <!-- + Remember to use padding on your ListViews to adhere to the standard + metrics described in the Android Design guidelines. When doing so, + you should set the android:scrollbarStyle such that the scrollbar + doesn'isn't inset. + --> + <ListView android:id="@android:id/list" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:paddingLeft="@dimen/page_margin" + android:paddingRight="@dimen/page_margin" + android:scrollbarStyle="outsideOverlay" /> + + <!-- + When using the Holo theme (setting your activity or app theme to + Theme.Holo or one of its descendants), a LinearLayout with the + ?android:buttonBarStyle will draw dividers (with padding) between + buttons. + --> + <LinearLayout style="?android:buttonBarStyle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <!-- + Make sure to apply the ?android:buttonBarStyle to each button + in the button bar. + + In the Holo theme, this style is very similar to + ?android:borderlessButtonStyle, but designed for use specifically + in horizontal button bars. + --> + <Button style="?android:buttonBarButtonStyle" + android:id="@+id/cancel_button" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="@string/cancel" /> + + <Button style="?android:buttonBarButtonStyle" + android:id="@+id/ok_button" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="@string/ok" /> + + </LinearLayout> + +</LinearLayout> diff --git a/samples/browseable/BorderlessButtons/res/menu/main.xml b/samples/browseable/BorderlessButtons/res/menu/main.xml new file mode 100644 index 000000000..54f0d3f3d --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/menu/main.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. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:id="@+id/docs_link" + android:title="@string/docs_link_title" + android:showAsAction="never" /> +</menu> diff --git a/samples/browseable/BorderlessButtons/res/values-sw600dp/dimens.xml b/samples/browseable/BorderlessButtons/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BorderlessButtons/res/values-sw600dp/styles.xml b/samples/browseable/BorderlessButtons/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/BorderlessButtons/res/values/base-strings.xml b/samples/browseable/BorderlessButtons/res/values/base-strings.xml new file mode 100644 index 000000000..efbd14d01 --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/values/base-strings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">BorderlessButtons</string> + <string name="intro_message"> + <![CDATA[ + + + This sample demonstrates the use of borderless buttons, bottom button bars + (OK and Cancel) and dividers to establish visual structure. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/BorderlessButtons/res/values/dimens.xml b/samples/browseable/BorderlessButtons/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/BorderlessButtons/res/values/strings.xml b/samples/browseable/BorderlessButtons/res/values/strings.xml new file mode 100755 index 000000000..fb141d214 --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/values/strings.xml @@ -0,0 +1,30 @@ +<!-- + 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="cancel">Cancel</string> + <string name="ok">OK</string> + + <string name="dummy_title">Dummy title</string> + <string name="dummy_subtitle">Dummy subtitle</string> + + <string name="delete_content_description">Delete</string> + + <string name="touched_primary_message">Touched primary list item target.</string> + <string name="touched_secondary_message">Touched secondary list item target (delete).</string> + + <string name="docs_link_title">Design docs: borderless buttons</string> +</resources> diff --git a/samples/browseable/BorderlessButtons/res/values/styles.xml b/samples/browseable/BorderlessButtons/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/BorderlessButtons/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/BorderlessButtons/src/com.example.android.borderlessbuttons/MainActivity.java b/samples/browseable/BorderlessButtons/src/com.example.android.borderlessbuttons/MainActivity.java new file mode 100755 index 000000000..04105f409 --- /dev/null +++ b/samples/browseable/BorderlessButtons/src/com.example.android.borderlessbuttons/MainActivity.java @@ -0,0 +1,130 @@ +/* + * 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.borderlessbuttons; + +import android.app.ListActivity; +import android.content.ActivityNotFoundException; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.Toast; + +/** + * This activity demonstrates the <b>borderless button</b> styling from the Holo visual language. + * The most interesting bits in this sample are in the layout files (res/layout/). + * <p> + * See <a href="http://developer.android.com/design/building-blocks/buttons.html#borderless"> + * borderless buttons</a> at the Android Design guide for a discussion of this visual style. + */ +public class MainActivity extends ListActivity { + private static final Uri DOCS_URI = Uri.parse( + "http://developer.android.com/design/building-blocks/buttons.html#borderless"); + + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.sample_main); + + setListAdapter(mListAdapter); + + findViewById(R.id.cancel_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + finish(); + } + }); + + findViewById(R.id.ok_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + finish(); + } + }); + } + + private BaseAdapter mListAdapter = new BaseAdapter() { + @Override + public int getCount() { + return 10; + } + + @Override + public Object getItem(int position) { + return null; + } + + @Override + public long getItemId(int position) { + return position + 1; + } + + @Override + public View getView(int position, View convertView, ViewGroup container) { + if (convertView == null) { + convertView = getLayoutInflater().inflate(R.layout.list_item, container, false); + } + + // Because the list item contains multiple touch targets, you should not override + // onListItemClick. Instead, set a click listener for each target individually. + + convertView.findViewById(R.id.primary_target).setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View view) { + Toast.makeText(MainActivity.this, + R.string.touched_primary_message, + Toast.LENGTH_SHORT).show(); + } + }); + + convertView.findViewById(R.id.secondary_action).setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View view) { + Toast.makeText(MainActivity.this, + R.string.touched_secondary_message, + Toast.LENGTH_SHORT).show(); + } + }); + return convertView; + } + }; + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.docs_link: + try { + startActivity(new Intent(Intent.ACTION_VIEW, DOCS_URI)); + } catch (ActivityNotFoundException ignored) { + } + return true; + } + return super.onOptionsItemSelected(item); + } +} diff --git a/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/Log.java b/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/LogFragment.java b/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/LogNode.java b/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/LogView.java b/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/LogWrapper.java b/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/MessageOnlyLogFilter.java b/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/BorderlessButtons/src/com.example.android.common.logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/CustomChoiceList/AndroidManifest.xml b/samples/browseable/CustomChoiceList/AndroidManifest.xml new file mode 100644 index 000000000..8c194f536 --- /dev/null +++ b/samples/browseable/CustomChoiceList/AndroidManifest.xml @@ -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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.customchoicelist" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="17" /> + + <application android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@style/Theme.Sample" + android:allowBackup="true"> + + <activity android:name=".MainActivity" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + </application> + +</manifest> diff --git a/samples/browseable/CustomChoiceList/_index.jd b/samples/browseable/CustomChoiceList/_index.jd new file mode 100644 index 000000000..aee858e25 --- /dev/null +++ b/samples/browseable/CustomChoiceList/_index.jd @@ -0,0 +1,13 @@ + + + +page.tags="CustomChoiceList" +sample.group=UI +@jd:body + +<p> + + This sample demonstrates how to create custom checkable layouts, for use with ListView\'s choiceMode + attribute. + + </p> diff --git a/samples/browseable/CustomChoiceList/res/color/hideable_text_color.xml b/samples/browseable/CustomChoiceList/res/color/hideable_text_color.xml new file mode 100644 index 000000000..c48bb3830 --- /dev/null +++ b/samples/browseable/CustomChoiceList/res/color/hideable_text_color.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. + --> + +<!-- + This color state list changes from gray to blue depending on its state (checked or not checked). +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_checked="false" android:color="#6000" /> + <item android:color="#09c" /> +</selector> diff --git a/samples/browseable/CustomChoiceList/res/drawable-hdpi/ic_launcher.png b/samples/browseable/CustomChoiceList/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..230b1c398 --- /dev/null +++ b/samples/browseable/CustomChoiceList/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/CustomChoiceList/res/drawable-hdpi/tile.9.png b/samples/browseable/CustomChoiceList/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/CustomChoiceList/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/CustomChoiceList/res/drawable-mdpi/ic_launcher.png b/samples/browseable/CustomChoiceList/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..dc56d2664 --- /dev/null +++ b/samples/browseable/CustomChoiceList/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/CustomChoiceList/res/drawable-xhdpi/ic_hideable_item_checked.png b/samples/browseable/CustomChoiceList/res/drawable-xhdpi/ic_hideable_item_checked.png Binary files differnew file mode 100644 index 000000000..9346d8a54 --- /dev/null +++ b/samples/browseable/CustomChoiceList/res/drawable-xhdpi/ic_hideable_item_checked.png diff --git a/samples/browseable/CustomChoiceList/res/drawable-xhdpi/ic_hideable_item_unchecked.png b/samples/browseable/CustomChoiceList/res/drawable-xhdpi/ic_hideable_item_unchecked.png Binary files differnew file mode 100644 index 000000000..d45b37481 --- /dev/null +++ b/samples/browseable/CustomChoiceList/res/drawable-xhdpi/ic_hideable_item_unchecked.png diff --git a/samples/browseable/CustomChoiceList/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/CustomChoiceList/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..a71e6ca43 --- /dev/null +++ b/samples/browseable/CustomChoiceList/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/CustomChoiceList/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/CustomChoiceList/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..051f1e32a --- /dev/null +++ b/samples/browseable/CustomChoiceList/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/CustomChoiceList/res/drawable/ic_hideable_item.xml b/samples/browseable/CustomChoiceList/res/drawable/ic_hideable_item.xml new file mode 100644 index 000000000..bc5ea543c --- /dev/null +++ b/samples/browseable/CustomChoiceList/res/drawable/ic_hideable_item.xml @@ -0,0 +1,24 @@ +<!-- + 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. + --> + +<!-- + This state list drawable changes from an outline of an eye (ic_hideable_item_unchecked) to a + blue eye with iris (ic_hideable_item_checked) depending on its state (checked or not checked). +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_checked="false" android:drawable="@drawable/ic_hideable_item_unchecked" /> + <item android:drawable="@drawable/ic_hideable_item_checked" /> +</selector> diff --git a/samples/browseable/CustomChoiceList/res/layout/activity_main.xml b/samples/browseable/CustomChoiceList/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/CustomChoiceList/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/CustomChoiceList/res/layout/list_item.xml b/samples/browseable/CustomChoiceList/res/layout/list_item.xml new file mode 100644 index 000000000..09a17edfe --- /dev/null +++ b/samples/browseable/CustomChoiceList/res/layout/list_item.xml @@ -0,0 +1,62 @@ +<!-- + 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 ListView from sample_main.xml has a choiceMode set, meaning that when a user + selects a list item, the ListView will set the state for that item's root view + (this CheckableLinearLayout) to "checked". Note that this requires that the root view + implements the Checkable interface. Once the root view is checked, any children that + have the duplicateParentState attribute set will inherit this "checked" state. +--> +<com.example.android.customchoicelist.CheckableLinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingLeft="8dp" + android:paddingRight="8dp" + android:minHeight="?android:listPreferredItemHeight" + android:gravity="center_vertical"> + + <!-- + The duplicateParentState attribute on this TextView, along with the color state list + used in the textColor attribute causes its text color to change when its parent + is checked or unchecked. + --> + <TextView android:id="@android:id/text1" + android:duplicateParentState="true" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:textAppearance="?android:textAppearanceMedium" + android:textColor="@color/hideable_text_color" /> + + <!-- + The duplicateParentState attribute on this ImageView, along with the state list + drawable in the src attribute causes its image to change when its parent + is checked or unchecked. + + To use the standard radio or checkmark image, set the src to + ?android:listChoiceIndicatorMultiple or ?android:listChoiceIndicatorSingle. These + are system theme attributes that reference a state list drawable. + --> + <ImageView android:src="@drawable/ic_hideable_item" + android:duplicateParentState="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="16dp" /> + +</com.example.android.customchoicelist.CheckableLinearLayout> diff --git a/samples/browseable/CustomChoiceList/res/layout/sample_main.xml b/samples/browseable/CustomChoiceList/res/layout/sample_main.xml new file mode 100755 index 000000000..17a69e411 --- /dev/null +++ b/samples/browseable/CustomChoiceList/res/layout/sample_main.xml @@ -0,0 +1,54 @@ +<!-- + 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:showDividers="middle" + android:divider="?android:dividerHorizontal"> + + <TextView style="@style/Widget.DescriptionBar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/intro_message" /> + + <!-- + When a ListView has a choiceMode set, it will allow users to "choose" + one or more items. The framework provides default list item layouts + that show standard radio buttons or check boxes next to a + single line of text: + + android.R.layout.simple_list_item_single_choice and + android.R.layout.simple_list_item_multiple_choice. + + In some cases, you may want to customize this layout. When doing so, + the root view must implement the Checkable interface. + + Lastly, remember to use padding on your ListViews to adhere to the standard + metrics described in the Android Design guidelines. When doing so, + you should set the android:scrollbarStyle such that the scrollbar + doesn'isn't inset. + --> + <ListView android:id="@android:id/list" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:paddingLeft="@dimen/page_margin" + android:paddingRight="@dimen/page_margin" + android:scrollbarStyle="outsideOverlay" + android:choiceMode="multipleChoice" /> +</LinearLayout> diff --git a/samples/browseable/CustomChoiceList/res/values-sw600dp/dimens.xml b/samples/browseable/CustomChoiceList/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/CustomChoiceList/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/CustomChoiceList/res/values-sw600dp/styles.xml b/samples/browseable/CustomChoiceList/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/CustomChoiceList/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/CustomChoiceList/res/values/base-strings.xml b/samples/browseable/CustomChoiceList/res/values/base-strings.xml new file mode 100644 index 000000000..e2890c46a --- /dev/null +++ b/samples/browseable/CustomChoiceList/res/values/base-strings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">CustomChoiceList</string> + <string name="intro_message"> + <![CDATA[ + + + This sample demonstrates how to create custom checkable layouts, for use with ListView\'s choiceMode + attribute. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/CustomChoiceList/res/values/dimens.xml b/samples/browseable/CustomChoiceList/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/CustomChoiceList/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/CustomChoiceList/res/values/styles.xml b/samples/browseable/CustomChoiceList/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/CustomChoiceList/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/Log.java b/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/LogFragment.java b/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/LogNode.java b/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/LogView.java b/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/LogWrapper.java b/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/MessageOnlyLogFilter.java b/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/CustomChoiceList/src/com.example.android.common.logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/CustomChoiceList/src/com.example.android.customchoicelist/CheckableLinearLayout.java b/samples/browseable/CustomChoiceList/src/com.example.android.customchoicelist/CheckableLinearLayout.java new file mode 100644 index 000000000..a30eb2a46 --- /dev/null +++ b/samples/browseable/CustomChoiceList/src/com.example.android.customchoicelist/CheckableLinearLayout.java @@ -0,0 +1,66 @@ +/* + * 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.customchoicelist; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.widget.Checkable; +import android.widget.LinearLayout; + +/** + * This is a simple wrapper for {@link android.widget.LinearLayout} that implements the {@link android.widget.Checkable} + * interface by keeping an internal 'checked' state flag. + * <p> + * This can be used as the root view for a custom list item layout for + * {@link android.widget.AbsListView} elements with a + * {@link android.widget.AbsListView#setChoiceMode(int) choiceMode} set. + */ +public class CheckableLinearLayout extends LinearLayout implements Checkable { + private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked}; + + private boolean mChecked = false; + + public CheckableLinearLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public boolean isChecked() { + return mChecked; + } + + public void setChecked(boolean b) { + if (b != mChecked) { + mChecked = b; + refreshDrawableState(); + } + } + + public void toggle() { + setChecked(!mChecked); + } + + @Override + public int[] onCreateDrawableState(int extraSpace) { + final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); + if (isChecked()) { + mergeDrawableStates(drawableState, CHECKED_STATE_SET); + } + return drawableState; + } +} diff --git a/samples/browseable/CustomChoiceList/src/com.example.android.customchoicelist/Cheeses.java b/samples/browseable/CustomChoiceList/src/com.example.android.customchoicelist/Cheeses.java new file mode 100644 index 000000000..871ae2929 --- /dev/null +++ b/samples/browseable/CustomChoiceList/src/com.example.android.customchoicelist/Cheeses.java @@ -0,0 +1,155 @@ +/* + * 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.customchoicelist; + +/** + * Dummy data. + */ +public class Cheeses { + public static final String[] CHEESES = { + "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi", + "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", + "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese", + "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell", + "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc", + "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss", + "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon", + "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase", + "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese", + "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy", + "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille", + "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore", + "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)", + "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves", + "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur", + "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon", + "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin", + "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)", + "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine", + "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza", + "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)", + "Buxton Blue", "Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta", + "Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie", + "Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat", + "Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano", + "Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain", + "Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou", + "Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar", + "Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno", + "Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack", + "Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper", + "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)", + "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese", + "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza", + "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley", + "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino", + "Cypress Grove Chevre", "Danablu (Danish Blue)", "Danbo", "Danish Fontina", + "Daralagjazsky", "Dauphin", "Delice des Fiouves", "Denhany Dorset Drum", "Derby", + "Dessertnyj Belyj", "Devon Blue", "Devon Garland", "Dolcelatte", "Doolin", + "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester", "Double Worcester", + "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra", "Dunlop", "Dunsyre Blue", + "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz", + "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne", "Esbareich", + "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo", "Exmoor Blue", + "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle", + "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis", "Flor de Guia", + "Flower Marie", "Folded", "Folded cheese with mint", "Fondant de Brebis", + "Fontainebleau", "Fontal", "Fontina Val d'Aosta", "Formaggio di capra", "Fougerus", + "Four Herb Gouda", "Fourme d' Ambert", "Fourme de Haute Loire", "Fourme de Montbrison", + "Fresh Jack", "Fresh Mozzarella", "Fresh Ricotta", "Fresh Truffles", "Fribourgeois", + "Friesekaas", "Friesian", "Friesla", "Frinault", "Fromage a Raclette", "Fromage Corse", + "Fromage de Montagne de Savoie", "Fromage Frais", "Fruit Cream Cheese", + "Frying Cheese", "Fynbo", "Gabriel", "Galette du Paludier", "Galette Lyonnaise", + "Galloway Goat's Milk Gems", "Gammelost", "Gaperon a l'Ail", "Garrotxa", "Gastanberra", + "Geitost", "Gippsland Blue", "Gjetost", "Gloucester", "Golden Cross", "Gorgonzola", + "Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost", + "Grafton Village Cheddar", "Grana", "Grana Padano", "Grand Vatel", + "Grataron d' Areches", "Gratte-Paille", "Graviera", "Greuilh", "Greve", + "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi", + "Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti", + "Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve", + "Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster", + "Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg", + "Jermi Tortes", "Jibneh Arabieh", "Jindi Brie", "Jubilee Blue", "Juustoleipa", + "Kadchgall", "Kaseri", "Kashta", "Kefalotyri", "Kenafa", "Kernhem", "Kervella Affine", + "Kikorangi", "King Island Cape Wickham Brie", "King River Gold", "Klosterkaese", + "Knockalara", "Kugelkase", "L'Aveyronnais", "L'Ecir de l'Aubrac", "La Taupiniere", + "La Vache Qui Rit", "Laguiole", "Lairobell", "Lajta", "Lanark Blue", "Lancashire", + "Langres", "Lappi", "Laruns", "Lavistown", "Le Brin", "Le Fium Orbo", "Le Lacandou", + "Le Roule", "Leafield", "Lebbene", "Leerdammer", "Leicester", "Leyden", "Limburger", + "Lincolnshire Poacher", "Lingot Saint Bousquet d'Orb", "Liptauer", "Little Rydings", + "Livarot", "Llanboidy", "Llanglofan Farmhouse", "Loch Arthur Farmhouse", + "Loddiswell Avondale", "Longhorn", "Lou Palou", "Lou Pevre", "Lyonnais", "Maasdam", + "Macconais", "Mahoe Aged Gouda", "Mahon", "Malvern", "Mamirolle", "Manchego", + "Manouri", "Manur", "Marble Cheddar", "Marbled Cheeses", "Maredsous", "Margotin", + "Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)", + "Mascarpone Torta", "Matocq", "Maytag Blue", "Meira", "Menallack Farmhouse", + "Menonita", "Meredith Blue", "Mesost", "Metton (Cancoillotte)", "Meyer Vintage Gouda", + "Mihalic Peynir", "Milleens", "Mimolette", "Mine-Gabhar", "Mini Baby Bells", "Mixte", + "Molbo", "Monastery Cheeses", "Mondseer", "Mont D'or Lyonnais", "Montasio", + "Monterey Jack", "Monterey Jack Dry", "Morbier", "Morbier Cru de Montagne", + "Mothais a la Feuille", "Mozzarella", "Mozzarella (Australian)", + "Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster", + "Murol", "Mycella", "Myzithra", "Naboulsi", "Nantais", "Neufchatel", + "Neufchatel (Australian)", "Niolo", "Nokkelost", "Northumberland", "Oaxaca", + "Olde York", "Olivet au Foin", "Olivet Bleu", "Olivet Cendre", + "Orkney Extra Mature Cheddar", "Orla", "Oschtjepka", "Ossau Fermier", "Ossau-Iraty", + "Oszczypek", "Oxford Blue", "P'tit Berrichon", "Palet de Babligny", "Paneer", "Panela", + "Pannerone", "Pant ys Gawn", "Parmesan (Parmigiano)", "Parmigiano Reggiano", + "Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage", + "Patefine Fort", "Pave d'Affinois", "Pave d'Auge", "Pave de Chirac", "Pave du Berry", + "Pecorino", "Pecorino in Walnut Leaves", "Pecorino Romano", "Peekskill Pyramid", + "Pelardon des Cevennes", "Pelardon des Corbieres", "Penamellera", "Penbryn", + "Pencarreg", "Perail de Brebis", "Petit Morin", "Petit Pardou", "Petit-Suisse", + "Picodon de Chevre", "Picos de Europa", "Piora", "Pithtviers au Foin", + "Plateau de Herve", "Plymouth Cheese", "Podhalanski", "Poivre d'Ane", "Polkolbin", + "Pont l'Eveque", "Port Nicholson", "Port-Salut", "Postel", "Pouligny-Saint-Pierre", + "Pourly", "Prastost", "Pressato", "Prince-Jean", "Processed Cheddar", "Provolone", + "Provolone (Australian)", "Pyengana Cheddar", "Pyramide", "Quark", + "Quark (Australian)", "Quartirolo Lombardo", "Quatre-Vents", "Quercy Petit", + "Queso Blanco", "Queso Blanco con Frutas --Pina y Mango", "Queso de Murcia", + "Queso del Montsec", "Queso del Tietar", "Queso Fresco", "Queso Fresco (Adobera)", + "Queso Iberico", "Queso Jalapeno", "Queso Majorero", "Queso Media Luna", + "Queso Para Frier", "Queso Quesadilla", "Rabacal", "Raclette", "Ragusano", "Raschera", + "Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou", + "Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder", + "Rigotte", "Rocamadour", "Rollot", "Romano", "Romans Part Dieu", "Roncal", "Roquefort", + "Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr", + "Saanenkaese", "Saga", "Sage Derby", "Sainte Maure", "Saint-Marcellin", + "Saint-Nectaire", "Saint-Paulin", "Salers", "Samso", "San Simon", "Sancerre", + "Sap Sago", "Sardo", "Sardo Egyptian", "Sbrinz", "Scamorza", "Schabzieger", "Schloss", + "Selles sur Cher", "Selva", "Serat", "Seriously Strong Cheddar", "Serra da Estrela", + "Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda", + "Somerset Brie", "Sonoma Jack", "Sottocenare al Tartufo", "Soumaintrain", + "Sourire Lozerien", "Spenwood", "Sraffordshire Organic", "St. Agur Blue Cheese", + "Stilton", "Stinking Bishop", "String", "Sussex Slipcote", "Sveciaost", "Swaledale", + "Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie", + "Tasmania Highland Chevre Log", "Taupiniere", "Teifi", "Telemea", "Testouri", + "Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar", + "Tilsit", "Timboon Brie", "Toma", "Tomme Brulee", "Tomme d'Abondance", + "Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes", + "Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet", + "Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe", + "Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa", + "Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois", + "Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue", + "Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington", + "Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou", + "Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue", + "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano" + }; +} diff --git a/samples/browseable/CustomChoiceList/src/com.example.android.customchoicelist/MainActivity.java b/samples/browseable/CustomChoiceList/src/com.example.android.customchoicelist/MainActivity.java new file mode 100755 index 000000000..e4e89f234 --- /dev/null +++ b/samples/browseable/CustomChoiceList/src/com.example.android.customchoicelist/MainActivity.java @@ -0,0 +1,68 @@ +/* + * 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.customchoicelist; + +import android.app.ListActivity; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +/** + * This sample demonstrates how to create custom single- or multi-choice + * {@link android.widget.ListView} UIs. The most interesting bits are in + * the <code>res/layout/</code> directory of this sample. + */ +public class MainActivity extends ListActivity { + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.sample_main); + setListAdapter(new MyAdapter()); + } + + /** + * A simple array adapter that creates a list of cheeses. + */ + private class MyAdapter extends BaseAdapter { + @Override + public int getCount() { + return Cheeses.CHEESES.length; + } + + @Override + public String getItem(int position) { + return Cheeses.CHEESES[position]; + } + + @Override + public long getItemId(int position) { + return Cheeses.CHEESES[position].hashCode(); + } + + @Override + public View getView(int position, View convertView, ViewGroup container) { + if (convertView == null) { + convertView = getLayoutInflater().inflate(R.layout.list_item, container, false); + } + + ((TextView) convertView.findViewById(android.R.id.text1)) + .setText(getItem(position)); + return convertView; + } + } +} diff --git a/samples/browseable/CustomNotifications/AndroidManifest.xml b/samples/browseable/CustomNotifications/AndroidManifest.xml new file mode 100644 index 000000000..b20a41196 --- /dev/null +++ b/samples/browseable/CustomNotifications/AndroidManifest.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.customnotifications" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk + android:minSdkVersion="4" + android:targetSdkVersion="18" /> + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + <activity + android:name="com.example.android.customnotifications.MainActivity" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/samples/browseable/CustomNotifications/_index.jd b/samples/browseable/CustomNotifications/_index.jd new file mode 100644 index 000000000..2da471808 --- /dev/null +++ b/samples/browseable/CustomNotifications/_index.jd @@ -0,0 +1,12 @@ + + + +page.tags="CustomNotifications" +sample.group=UI +@jd:body + +<p> + + This sample demonstrates notifications with custom content views. + + </p> diff --git a/samples/browseable/CustomNotifications/res/drawable-hdpi-v11/ic_stat_custom.png b/samples/browseable/CustomNotifications/res/drawable-hdpi-v11/ic_stat_custom.png Binary files differnew file mode 100644 index 000000000..db182ce7f --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-hdpi-v11/ic_stat_custom.png diff --git a/samples/browseable/CustomNotifications/res/drawable-hdpi-v9/ic_stat_custom.png b/samples/browseable/CustomNotifications/res/drawable-hdpi-v9/ic_stat_custom.png Binary files differnew file mode 100644 index 000000000..c35f570c8 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-hdpi-v9/ic_stat_custom.png diff --git a/samples/browseable/CustomNotifications/res/drawable-hdpi/ic_launcher.png b/samples/browseable/CustomNotifications/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..155ac982a --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/CustomNotifications/res/drawable-hdpi/ic_stat_custom.png b/samples/browseable/CustomNotifications/res/drawable-hdpi/ic_stat_custom.png Binary files differnew file mode 100644 index 000000000..b0434fd02 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-hdpi/ic_stat_custom.png diff --git a/samples/browseable/CustomNotifications/res/drawable-hdpi/tile.9.png b/samples/browseable/CustomNotifications/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/CustomNotifications/res/drawable-ldpi-v11/ic_stat_custom.png b/samples/browseable/CustomNotifications/res/drawable-ldpi-v11/ic_stat_custom.png Binary files differnew file mode 100644 index 000000000..50e1b3205 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-ldpi-v11/ic_stat_custom.png diff --git a/samples/browseable/CustomNotifications/res/drawable-ldpi-v9/ic_stat_custom.png b/samples/browseable/CustomNotifications/res/drawable-ldpi-v9/ic_stat_custom.png Binary files differnew file mode 100644 index 000000000..d4de32ab7 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-ldpi-v9/ic_stat_custom.png diff --git a/samples/browseable/CustomNotifications/res/drawable-mdpi-v11/ic_stat_custom.png b/samples/browseable/CustomNotifications/res/drawable-mdpi-v11/ic_stat_custom.png Binary files differnew file mode 100644 index 000000000..a46f47fb8 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-mdpi-v11/ic_stat_custom.png diff --git a/samples/browseable/CustomNotifications/res/drawable-mdpi-v9/ic_stat_custom.png b/samples/browseable/CustomNotifications/res/drawable-mdpi-v9/ic_stat_custom.png Binary files differnew file mode 100644 index 000000000..2896b6333 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-mdpi-v9/ic_stat_custom.png diff --git a/samples/browseable/CustomNotifications/res/drawable-mdpi/ic_launcher.png b/samples/browseable/CustomNotifications/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6e38dc62d --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/CustomNotifications/res/drawable-mdpi/ic_stat_custom.png b/samples/browseable/CustomNotifications/res/drawable-mdpi/ic_stat_custom.png Binary files differnew file mode 100644 index 000000000..304f7e9db --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-mdpi/ic_stat_custom.png diff --git a/samples/browseable/CustomNotifications/res/drawable-xhdpi-v11/ic_stat_custom.png b/samples/browseable/CustomNotifications/res/drawable-xhdpi-v11/ic_stat_custom.png Binary files differnew file mode 100644 index 000000000..52f7df8af --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-xhdpi-v11/ic_stat_custom.png diff --git a/samples/browseable/CustomNotifications/res/drawable-xhdpi-v9/ic_stat_custom.png b/samples/browseable/CustomNotifications/res/drawable-xhdpi-v9/ic_stat_custom.png Binary files differnew file mode 100644 index 000000000..5d2617eb0 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-xhdpi-v9/ic_stat_custom.png diff --git a/samples/browseable/CustomNotifications/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/CustomNotifications/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..638fc6765 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/CustomNotifications/res/drawable-xhdpi/ic_stat_custom.png b/samples/browseable/CustomNotifications/res/drawable-xhdpi/ic_stat_custom.png Binary files differnew file mode 100644 index 000000000..79c30a8bc --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-xhdpi/ic_stat_custom.png diff --git a/samples/browseable/CustomNotifications/res/drawable-xhdpi/robot.png b/samples/browseable/CustomNotifications/res/drawable-xhdpi/robot.png Binary files differnew file mode 100644 index 000000000..e21ee7571 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-xhdpi/robot.png diff --git a/samples/browseable/CustomNotifications/res/drawable-xhdpi/robot_expanded.png b/samples/browseable/CustomNotifications/res/drawable-xhdpi/robot_expanded.png Binary files differnew file mode 100644 index 000000000..f9469d45c --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-xhdpi/robot_expanded.png diff --git a/samples/browseable/CustomNotifications/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/CustomNotifications/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..9a9a60c65 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/CustomNotifications/res/layout/activity_main.xml b/samples/browseable/CustomNotifications/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/CustomNotifications/res/layout/notification.xml b/samples/browseable/CustomNotifications/res/layout/notification.xml new file mode 100644 index 000000000..9d977d326 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/layout/notification.xml @@ -0,0 +1,41 @@ +<?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. +--> + +<!-- Layout for the collapsed notification. --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:gravity="center_horizontal"> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="@style/NotificationContent" + android:id="@+id/textView" + android:gravity="center" /> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/imageView" + android:src="@drawable/robot" + android:contentDescription="@string/collapsed_image" /> + + +</LinearLayout> diff --git a/samples/browseable/CustomNotifications/res/layout/notification_expanded.xml b/samples/browseable/CustomNotifications/res/layout/notification_expanded.xml new file mode 100644 index 000000000..9d5a78445 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/layout/notification_expanded.xml @@ -0,0 +1,55 @@ +<?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. +--> + +<!-- Layout for the expanded notification. --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:gravity="right|top"> + + + <RelativeLayout + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/NotificationContent" + android:text="@string/expanded" + android:layout_gravity="center_vertical" + android:layout_alignParentTop="false" + android:layout_alignParentLeft="true" + android:layout_toLeftOf="@+id/imageView" + android:gravity="center" + android:layout_centerVertical="true" /> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/robot_expanded" + android:layout_gravity="right|top" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:id="@+id/imageView" + android:contentDescription="@string/expanded_image" /> + </RelativeLayout> + + +</LinearLayout> diff --git a/samples/browseable/CustomNotifications/res/layout/sample_main.xml b/samples/browseable/CustomNotifications/res/layout/sample_main.xml new file mode 100644 index 000000000..d67477dcd --- /dev/null +++ b/samples/browseable/CustomNotifications/res/layout/sample_main.xml @@ -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. + --> + +<!-- Layout for MainActivity. + Includes an introductory text and a button to show the notification. --> + +<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:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + android:orientation="vertical" + tools:context=".MainActivity" + android:gravity="center_horizontal"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/intro_text" /> + + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/show_notification" + android:id="@+id/button" + android:onClick="showNotificationClicked" /> + +</LinearLayout> diff --git a/samples/browseable/CustomNotifications/res/values-sw600dp/dimens.xml b/samples/browseable/CustomNotifications/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/CustomNotifications/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/CustomNotifications/res/values-sw600dp/styles.xml b/samples/browseable/CustomNotifications/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/CustomNotifications/res/values-sw720dp-land/dimens.xml b/samples/browseable/CustomNotifications/res/values-sw720dp-land/dimens.xml new file mode 100644 index 000000000..901314ae6 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/values-sw720dp-land/dimens.xml @@ -0,0 +1,20 @@ +<!-- + * 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> + <!-- Customize dimensions originally defined in res/values/dimens.xml (such as + screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here. --> + <dimen name="activity_horizontal_margin">128dp</dimen> +</resources> diff --git a/samples/browseable/CustomNotifications/res/values-v9/styles.xml b/samples/browseable/CustomNotifications/res/values-v9/styles.xml new file mode 100644 index 000000000..85010a5f7 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/values-v9/styles.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <style name="NotificationContent" parent="@android:style/TextAppearance.StatusBar.EventContent"> + </style> + +</resources>
\ No newline at end of file diff --git a/samples/browseable/CustomNotifications/res/values/base-strings.xml b/samples/browseable/CustomNotifications/res/values/base-strings.xml new file mode 100644 index 000000000..7ddbc2dc2 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/values/base-strings.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">CustomNotifications</string> + <string name="intro_message"> + <![CDATA[ + + + This sample demonstrates notifications with custom content views. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/CustomNotifications/res/values/dimens.xml b/samples/browseable/CustomNotifications/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/CustomNotifications/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/CustomNotifications/res/values/strings.xml b/samples/browseable/CustomNotifications/res/values/strings.xml new file mode 100644 index 000000000..a76644138 --- /dev/null +++ b/samples/browseable/CustomNotifications/res/values/strings.xml @@ -0,0 +1,41 @@ +<?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="expanded">I\'m the expanded notification.\nCollapse me!</string> + <string name="collapsed">I\'m the collapsed notification.\nCreated at: %s</string> + <string name="show_notification">Show Notification</string> + <string name="custom_notification">I\'m a custom notification.</string> + + <string name="intro_text">This sample demonstrates how a notification is created using the + <b>NotificationCompatBuilder</b> + with a custom content view. The layout of the notification is defined as a + layout resource and inflated as a <b>RemoteViews</b> object. + \n\nOn API level 16 and above, a different layout is inflated and set as + the <i>big content view</i>, which is used when the notification is expanded. + \n\n<b>Use the button below to create the notification. + \n\nIf your device is running Jelly Bean or above, try expanding or collapsing + the notification to see the different layouts.</b> + </string> + <string name="collapsed_image">A single Android robot waving. Symbolises a collapsed + notification. + </string> + <string name="expanded_image">Two Androids on top of each other. Symbolises an expanded + notification. + </string> + +</resources> diff --git a/samples/browseable/CustomNotifications/res/values/styles.xml b/samples/browseable/CustomNotifications/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/CustomNotifications/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/CustomNotifications/src/com.example.android.customnotifications/MainActivity.java b/samples/browseable/CustomNotifications/src/com.example.android.customnotifications/MainActivity.java new file mode 100644 index 000000000..57a4315d7 --- /dev/null +++ b/samples/browseable/CustomNotifications/src/com.example.android.customnotifications/MainActivity.java @@ -0,0 +1,125 @@ +/* + * 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.customnotifications; + +import android.app.Activity; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.support.v4.app.NotificationCompat; +import android.view.View; +import android.widget.RemoteViews; + +import java.text.DateFormat; +import java.util.Date; + +public class MainActivity extends Activity { + /** + * This sample demonstrates notifications with custom content views. + * + * <p>On API level 16 and above a big content view is also defined that is used for the + * 'expanded' notification. The notification is created by the NotificationCompat.Builder. + * The expanded content view is set directly on the {@link android.app.Notification} once it has been build. + * (See {@link android.app.Notification#bigContentView}.) </p> + * + * <p>The content views are inflated as {@link android.widget.RemoteViews} directly from their XML layout + * definitions using {@link android.widget.RemoteViews#RemoteViews(String, int)}.</p> + */ + private void createNotification() { + // BEGIN_INCLUDE(notificationCompat) + NotificationCompat.Builder builder = new NotificationCompat.Builder(this); + // END_INCLUDE(notificationCompat) + + // BEGIN_INCLUDE(intent) + //Create Intent to launch this Activity again if the notification is clicked. + Intent i = new Intent(this, MainActivity.class); + i.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + PendingIntent intent = PendingIntent.getActivity(this, 0, i, + PendingIntent.FLAG_UPDATE_CURRENT); + builder.setContentIntent(intent); + // END_INCLUDE(intent) + + // BEGIN_INCLUDE(ticker) + // Sets the ticker text + builder.setTicker(getResources().getString(R.string.custom_notification)); + + // Sets the small icon for the ticker + builder.setSmallIcon(R.drawable.ic_stat_custom); + // END_INCLUDE(ticker) + + // BEGIN_INCLUDE(buildNotification) + // Cancel the notification when clicked + builder.setAutoCancel(true); + + // Build the notification + Notification notification = builder.build(); + // END_INCLUDE(buildNotification) + + // BEGIN_INCLUDE(customLayout) + // Inflate the notification layout as RemoteViews + RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.notification); + + // Set text on a TextView in the RemoteViews programmatically. + final String time = DateFormat.getTimeInstance().format(new Date()).toString(); + final String text = getResources().getString(R.string.collapsed, time); + contentView.setTextViewText(R.id.textView, text); + + /* Workaround: Need to set the content view here directly on the notification. + * NotificationCompatBuilder contains a bug that prevents this from working on platform + * versions HoneyComb. + * See https://code.google.com/p/android/issues/detail?id=30495 + */ + notification.contentView = contentView; + + // Add a big content view to the notification if supported. + // Support for expanded notifications was added in API level 16. + // (The normal contentView is shown when the notification is collapsed, when expanded the + // big content view set here is displayed.) + if (Build.VERSION.SDK_INT >= 16) { + // Inflate and set the layout for the expanded notification view + RemoteViews expandedView = + new RemoteViews(getPackageName(), R.layout.notification_expanded); + notification.bigContentView = expandedView; + } + // END_INCLUDE(customLayout) + + // START_INCLUDE(notify) + // Use the NotificationManager to show the notification + NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + nm.notify(0, notification); + // END_INCLUDE(notify) + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.sample_main); + } + + /** + * Create and show a notification with a custom layout. + * This callback is defined through the 'onClick' attribute of the + * 'Show Notification' button in the XML layout. + * + * @param v + */ + public void showNotificationClicked(View v) { + createNotification(); + } +} diff --git a/samples/browseable/DoneBar/AndroidManifest.xml b/samples/browseable/DoneBar/AndroidManifest.xml new file mode 100644 index 000000000..473111435 --- /dev/null +++ b/samples/browseable/DoneBar/AndroidManifest.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.donebar" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17" /> + + <application android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@style/Theme.Sample" + android:allowBackup="true"> + + <activity android:name=".MainActivity" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <activity android:name=".DoneBarActivity" + android:parentActivityName=".MainActivity" /> + + <activity android:name=".DoneButtonActivity" + android:parentActivityName=".MainActivity" /> + + </application> + +</manifest> diff --git a/samples/browseable/DoneBar/_index.jd b/samples/browseable/DoneBar/_index.jd new file mode 100644 index 000000000..287a04a48 --- /dev/null +++ b/samples/browseable/DoneBar/_index.jd @@ -0,0 +1,18 @@ + + + +page.tags="DoneBar" +sample.group=UI +@jd:body + +<p> + + This sample demonstrates two alternative presentations of the + action bar that are well-suited for simple data entry scenarios. + + In this presentation, a done bar replaces the action + bar entirely, providing two direct actions to persist or dismiss changes. This is + suitable for cases where no additional view details or actions are needed in the + action bar. + + </p> diff --git a/samples/browseable/DoneBar/res/drawable-hdpi/ic_action_cancel.png b/samples/browseable/DoneBar/res/drawable-hdpi/ic_action_cancel.png Binary files differnew file mode 100644 index 000000000..cde36e1fa --- /dev/null +++ b/samples/browseable/DoneBar/res/drawable-hdpi/ic_action_cancel.png diff --git a/samples/browseable/DoneBar/res/drawable-hdpi/ic_action_done.png b/samples/browseable/DoneBar/res/drawable-hdpi/ic_action_done.png Binary files differnew file mode 100644 index 000000000..58bf97217 --- /dev/null +++ b/samples/browseable/DoneBar/res/drawable-hdpi/ic_action_done.png diff --git a/samples/browseable/DoneBar/res/drawable-hdpi/ic_launcher.png b/samples/browseable/DoneBar/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..b1efaf4b2 --- /dev/null +++ b/samples/browseable/DoneBar/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/DoneBar/res/drawable-hdpi/tile.9.png b/samples/browseable/DoneBar/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/DoneBar/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/DoneBar/res/drawable-mdpi/ic_action_cancel.png b/samples/browseable/DoneBar/res/drawable-mdpi/ic_action_cancel.png Binary files differnew file mode 100644 index 000000000..9f4c3d6a2 --- /dev/null +++ b/samples/browseable/DoneBar/res/drawable-mdpi/ic_action_cancel.png diff --git a/samples/browseable/DoneBar/res/drawable-mdpi/ic_action_done.png b/samples/browseable/DoneBar/res/drawable-mdpi/ic_action_done.png Binary files differnew file mode 100644 index 000000000..cf5fab3ad --- /dev/null +++ b/samples/browseable/DoneBar/res/drawable-mdpi/ic_action_done.png diff --git a/samples/browseable/DoneBar/res/drawable-mdpi/ic_launcher.png b/samples/browseable/DoneBar/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f5f9244f2 --- /dev/null +++ b/samples/browseable/DoneBar/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/DoneBar/res/drawable-xhdpi/ic_action_cancel.png b/samples/browseable/DoneBar/res/drawable-xhdpi/ic_action_cancel.png Binary files differnew file mode 100644 index 000000000..ca7d159fd --- /dev/null +++ b/samples/browseable/DoneBar/res/drawable-xhdpi/ic_action_cancel.png diff --git a/samples/browseable/DoneBar/res/drawable-xhdpi/ic_action_done.png b/samples/browseable/DoneBar/res/drawable-xhdpi/ic_action_done.png Binary files differnew file mode 100644 index 000000000..b8915716e --- /dev/null +++ b/samples/browseable/DoneBar/res/drawable-xhdpi/ic_action_done.png diff --git a/samples/browseable/DoneBar/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/DoneBar/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..5d07b3f06 --- /dev/null +++ b/samples/browseable/DoneBar/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/DoneBar/res/drawable-xhdpi/sample_dashboard_item_background.9.png b/samples/browseable/DoneBar/res/drawable-xhdpi/sample_dashboard_item_background.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/DoneBar/res/drawable-xhdpi/sample_dashboard_item_background.9.png diff --git a/samples/browseable/DoneBar/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/DoneBar/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6ef21e1f4 --- /dev/null +++ b/samples/browseable/DoneBar/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/DoneBar/res/layout/actionbar_custom_view_done.xml b/samples/browseable/DoneBar/res/layout/actionbar_custom_view_done.xml new file mode 100644 index 000000000..44c1d61af --- /dev/null +++ b/samples/browseable/DoneBar/res/layout/actionbar_custom_view_done.xml @@ -0,0 +1,26 @@ +<!-- + 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal" + android:divider="?android:attr/dividerVertical" + android:showDividers="end" + android:dividerPadding="12dp"> + + <include layout="@layout/include_done_button" /> +</LinearLayout> diff --git a/samples/browseable/DoneBar/res/layout/actionbar_custom_view_done_cancel.xml b/samples/browseable/DoneBar/res/layout/actionbar_custom_view_done_cancel.xml new file mode 100644 index 000000000..5cecdf045 --- /dev/null +++ b/samples/browseable/DoneBar/res/layout/actionbar_custom_view_done_cancel.xml @@ -0,0 +1,27 @@ +<!-- + 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal" + android:divider="?android:attr/dividerVertical" + android:showDividers="middle" + android:dividerPadding="12dp"> + + <include layout="@layout/include_cancel_button" /> + <include layout="@layout/include_done_button" /> +</LinearLayout> diff --git a/samples/browseable/DoneBar/res/layout/activity_done_bar.xml b/samples/browseable/DoneBar/res/layout/activity_done_bar.xml new file mode 100755 index 000000000..0d0272dfe --- /dev/null +++ b/samples/browseable/DoneBar/res/layout/activity_done_bar.xml @@ -0,0 +1,32 @@ +<!-- + 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. + --> + +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout style="@style/Widget.SampleContentContainer" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/donebaractivity_description" /> + + </LinearLayout> +</ScrollView> diff --git a/samples/browseable/DoneBar/res/layout/activity_done_button.xml b/samples/browseable/DoneBar/res/layout/activity_done_button.xml new file mode 100755 index 000000000..7fc2274cc --- /dev/null +++ b/samples/browseable/DoneBar/res/layout/activity_done_button.xml @@ -0,0 +1,32 @@ +<!-- + 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. + --> + +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout style="@style/Widget.SampleContentContainer" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/donebuttonactivity_description" /> + + </LinearLayout> +</ScrollView> diff --git a/samples/browseable/DoneBar/res/layout/activity_main.xml b/samples/browseable/DoneBar/res/layout/activity_main.xml new file mode 100755 index 000000000..88cdb8056 --- /dev/null +++ b/samples/browseable/DoneBar/res/layout/activity_main.xml @@ -0,0 +1,41 @@ +<!-- + 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + + <GridView android:id="@android:id/list" + style="@style/Widget.SampleDashboard.Grid" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:paddingLeft="@dimen/horizontal_page_margin" + android:paddingRight="@dimen/horizontal_page_margin" + android:paddingBottom="@dimen/vertical_page_margin" + android:scrollbarStyle="outsideOverlay" /> + +</LinearLayout> diff --git a/samples/browseable/DoneBar/res/layout/activity_sample_dashboard.xml b/samples/browseable/DoneBar/res/layout/activity_sample_dashboard.xml new file mode 100755 index 000000000..88cdb8056 --- /dev/null +++ b/samples/browseable/DoneBar/res/layout/activity_sample_dashboard.xml @@ -0,0 +1,41 @@ +<!-- + 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + + <GridView android:id="@android:id/list" + style="@style/Widget.SampleDashboard.Grid" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:paddingLeft="@dimen/horizontal_page_margin" + android:paddingRight="@dimen/horizontal_page_margin" + android:paddingBottom="@dimen/vertical_page_margin" + android:scrollbarStyle="outsideOverlay" /> + +</LinearLayout> diff --git a/samples/browseable/DoneBar/res/layout/include_cancel_button.xml b/samples/browseable/DoneBar/res/layout/include_cancel_button.xml new file mode 100644 index 000000000..bffffa6f1 --- /dev/null +++ b/samples/browseable/DoneBar/res/layout/include_cancel_button.xml @@ -0,0 +1,33 @@ +<!-- + 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" + style="?android:actionButtonStyle" + android:id="@+id/actionbar_cancel" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1"> + + <TextView style="?android:actionBarTabTextStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:paddingRight="20dp" + android:drawableLeft="@drawable/ic_action_cancel" + android:drawablePadding="8dp" + android:gravity="center_vertical" + android:text="@string/cancel" /> +</FrameLayout> diff --git a/samples/browseable/DoneBar/res/layout/include_done_button.xml b/samples/browseable/DoneBar/res/layout/include_done_button.xml new file mode 100644 index 000000000..92077337a --- /dev/null +++ b/samples/browseable/DoneBar/res/layout/include_done_button.xml @@ -0,0 +1,33 @@ +<!-- + 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" + style="?android:actionButtonStyle" + android:id="@+id/actionbar_done" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1"> + + <TextView style="?android:actionBarTabTextStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:paddingRight="20dp" + android:drawableLeft="@drawable/ic_action_done" + android:drawablePadding="8dp" + android:gravity="center_vertical" + android:text="@string/done" /> +</FrameLayout> diff --git a/samples/browseable/DoneBar/res/layout/sample_dashboard_item.xml b/samples/browseable/DoneBar/res/layout/sample_dashboard_item.xml new file mode 100644 index 000000000..38987ee20 --- /dev/null +++ b/samples/browseable/DoneBar/res/layout/sample_dashboard_item.xml @@ -0,0 +1,32 @@ +<!-- + 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/Widget.SampleDashboard.Item" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <TextView android:id="@android:id/text1" + style="@style/Widget.SampleDashboard.Item.Title" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + <TextView android:id="@android:id/text2" + style="@style/Widget.SampleDashboard.Item.Description" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> +</LinearLayout> diff --git a/samples/browseable/DoneBar/res/menu/cancel.xml b/samples/browseable/DoneBar/res/menu/cancel.xml new file mode 100644 index 000000000..18e3eeda5 --- /dev/null +++ b/samples/browseable/DoneBar/res/menu/cancel.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. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/cancel" + android:title="@string/cancel" + android:icon="@drawable/ic_action_cancel" + android:showAsAction="never" /> +</menu> diff --git a/samples/browseable/DoneBar/res/values-sw600dp/dimens.xml b/samples/browseable/DoneBar/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/DoneBar/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/DoneBar/res/values-sw600dp/styles.xml b/samples/browseable/DoneBar/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/DoneBar/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/DoneBar/res/values/activitycards-strings.xml b/samples/browseable/DoneBar/res/values/activitycards-strings.xml new file mode 100644 index 000000000..5c2557c97 --- /dev/null +++ b/samples/browseable/DoneBar/res/values/activitycards-strings.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="donebaractivity_title">Done Bar</string> + <string name="donebaractivity_description">In this presentation, a done bar replaces the action bar entirely, providing + two direct actions to persist or dismiss changes. This is suitable for cases where no + additional view details or actions are needed in the action bar.</string> + <string name="donebuttonactivity_title">Done Button</string> + <string name="donebuttonactivity_description">In this presentation, a done button replaces the action bar\'s "Up" affordance + and app icon, while the cancel action is made available in the action overflow. This is + well-suited to scenarios where additional view details or + actions may be needed in the action bar.</string> +</resources> diff --git a/samples/browseable/DoneBar/res/values/base-strings.xml b/samples/browseable/DoneBar/res/values/base-strings.xml new file mode 100644 index 000000000..22a4fae9f --- /dev/null +++ b/samples/browseable/DoneBar/res/values/base-strings.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">DoneBar</string> + <string name="intro_message"> + <![CDATA[ + + + This sample demonstrates two alternative presentations of the + action bar that are well-suited for simple data entry scenarios. + + In this presentation, a done bar replaces the action + bar entirely, providing two direct actions to persist or dismiss changes. This is + suitable for cases where no additional view details or actions are needed in the + action bar. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/DoneBar/res/values/dimens.xml b/samples/browseable/DoneBar/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/DoneBar/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/DoneBar/res/values/strings.xml b/samples/browseable/DoneBar/res/values/strings.xml new file mode 100755 index 000000000..df66a95e1 --- /dev/null +++ b/samples/browseable/DoneBar/res/values/strings.xml @@ -0,0 +1,21 @@ +<!-- + 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> + <!-- Done bar strings --> + <string name="done">Done</string> + <string name="cancel">Cancel</string> +</resources> diff --git a/samples/browseable/DoneBar/res/values/styles.xml b/samples/browseable/DoneBar/res/values/styles.xml new file mode 100644 index 000000000..cafe531a4 --- /dev/null +++ b/samples/browseable/DoneBar/res/values/styles.xml @@ -0,0 +1,71 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleContentContainer"> + <item name="android:paddingTop">@dimen/vertical_page_margin</item> + <item name="android:paddingBottom">@dimen/vertical_page_margin</item> + <item name="android:paddingLeft">@dimen/horizontal_page_margin</item> + <item name="android:paddingRight">@dimen/horizontal_page_margin</item> + </style> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleDashboard.Grid" parent="Widget"> + <item name="android:stretchMode">columnWidth</item> + <item name="android:columnWidth">200dp</item> + <item name="android:numColumns">auto_fit</item> + <item name="android:drawSelectorOnTop">true</item> + <item name="android:horizontalSpacing">@dimen/margin_medium</item> + <item name="android:verticalSpacing">@dimen/margin_medium</item> + </style> + + <style name="Widget.SampleDashboard.Item" parent="Widget"> + <item name="android:background">@drawable/sample_dashboard_item_background</item> + <item name="android:paddingTop">@dimen/margin_small</item> + <item name="android:paddingLeft">@dimen/margin_medium</item> + <item name="android:paddingRight">@dimen/margin_medium</item> + <item name="android:paddingBottom">@dimen/margin_medium</item> + </style> + + <style name="Widget.SampleDashboard.Item.Title" parent="Widget"> + <item name="android:layout_marginBottom">@dimen/margin_tiny</item> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:textColor">#09c</item> + <item name="android:textStyle">bold</item> + <item name="android:textSize">24sp</item> + </style> + + <style name="Widget.SampleDashboard.Item.Description" parent="Widget"> + <item name="android:textAppearance">?android:textAppearanceSmall</item> + <item name="android:fontFamily">sans-serif-light</item> + </style> +</resources> diff --git a/samples/browseable/DoneBar/src/com.example.android.donebar/DoneBarActivity.java b/samples/browseable/DoneBar/src/com.example.android.donebar/DoneBarActivity.java new file mode 100755 index 000000000..f2a2b002c --- /dev/null +++ b/samples/browseable/DoneBar/src/com.example.android.donebar/DoneBarActivity.java @@ -0,0 +1,71 @@ +/* + * 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.donebar; + +import android.app.ActionBar; +import android.app.Activity; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +/** + * A sample activity demonstrating the "done bar" alternative action bar presentation. For a more + * detailed description see {@link R.string.done_bar_description}. + */ +public class DoneBarActivity extends Activity { + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // BEGIN_INCLUDE (inflate_set_custom_view) + // Inflate a "Done/Cancel" custom action bar view. + final LayoutInflater inflater = (LayoutInflater) getActionBar().getThemedContext() + .getSystemService(LAYOUT_INFLATER_SERVICE); + final View customActionBarView = inflater.inflate( + R.layout.actionbar_custom_view_done_cancel, null); + customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + // "Done" + finish(); + } + }); + customActionBarView.findViewById(R.id.actionbar_cancel).setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + // "Cancel" + finish(); + } + }); + + // Show the custom action bar view and hide the normal Home icon and title. + final ActionBar actionBar = getActionBar(); + actionBar.setDisplayOptions( + ActionBar.DISPLAY_SHOW_CUSTOM, + ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME + | ActionBar.DISPLAY_SHOW_TITLE); + actionBar.setCustomView(customActionBarView, + new ActionBar.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + // END_INCLUDE (inflate_set_custom_view) + + setContentView(R.layout.activity_done_bar); + } +} diff --git a/samples/browseable/DoneBar/src/com.example.android.donebar/DoneButtonActivity.java b/samples/browseable/DoneBar/src/com.example.android.donebar/DoneButtonActivity.java new file mode 100755 index 000000000..3b1e37d23 --- /dev/null +++ b/samples/browseable/DoneBar/src/com.example.android.donebar/DoneButtonActivity.java @@ -0,0 +1,82 @@ +/* + * 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.donebar; + +import android.app.ActionBar; +import android.app.Activity; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; + +/** + * A sample activity demonstrating the "done button" alternative action bar presentation. For a more + * detailed description see {@link R.string.done_button_description}. + */ +public class DoneButtonActivity extends Activity { + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // BEGIN_INCLUDE (inflate_set_custom_view) + // Inflate a "Done" custom action bar view to serve as the "Up" affordance. + final LayoutInflater inflater = (LayoutInflater) getActionBar().getThemedContext() + .getSystemService(LAYOUT_INFLATER_SERVICE); + final View customActionBarView = inflater.inflate( + R.layout.actionbar_custom_view_done, null); + customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + // "Done" + finish(); + } + }); + + // Show the custom action bar view and hide the normal Home icon and title. + final ActionBar actionBar = getActionBar(); + actionBar.setDisplayOptions( + ActionBar.DISPLAY_SHOW_CUSTOM, + ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME + | ActionBar.DISPLAY_SHOW_TITLE); + actionBar.setCustomView(customActionBarView); + // END_INCLUDE (inflate_set_custom_view) + + setContentView(R.layout.activity_done_button); + } + + // BEGIN_INCLUDE (handle_cancel) + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.cancel, menu); + return true; + } + + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.cancel: + // "Cancel" + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + // END_INCLUDE (handle_cancel) +} diff --git a/samples/browseable/DoneBar/src/com.example.android.donebar/MainActivity.java b/samples/browseable/DoneBar/src/com.example.android.donebar/MainActivity.java new file mode 100644 index 000000000..8b1e8a469 --- /dev/null +++ b/samples/browseable/DoneBar/src/com.example.android.donebar/MainActivity.java @@ -0,0 +1,112 @@ +/* +* 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.donebar; + +import android.app.Activity; +import android.content.Intent; +import android.content.res.Resources; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.GridView; +import android.widget.TextView; + +/** + * A simple launcher activity offering access to the individual samples in this project. + */ +public class MainActivity extends Activity implements AdapterView.OnItemClickListener { + private Sample[] mSamples; + private GridView mGridView; + + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + // Prepare list of samples in this dashboard. + mSamples = new Sample[]{ + new Sample(R.string.donebaractivity_title, R.string.donebaractivity_description, + DoneBarActivity.class), + new Sample(R.string.donebuttonactivity_title, R.string.donebuttonactivity_description, + DoneButtonActivity.class), + }; + + // Prepare the GridView + mGridView = (GridView) findViewById(android.R.id.list); + mGridView.setAdapter(new SampleAdapter()); + mGridView.setOnItemClickListener(this); + } + + @Override + public void onItemClick(AdapterView<?> container, View view, int position, long id) { + startActivity(mSamples[position].intent); + } + + private class SampleAdapter extends BaseAdapter { + @Override + public int getCount() { + return mSamples.length; + } + + @Override + public Object getItem(int position) { + return mSamples[position]; + } + + @Override + public long getItemId(int position) { + return mSamples[position].hashCode(); + } + + @Override + public View getView(int position, View convertView, ViewGroup container) { + if (convertView == null) { + convertView = getLayoutInflater().inflate(R.layout.sample_dashboard_item, + container, false); + } + + ((TextView) convertView.findViewById(android.R.id.text1)).setText( + mSamples[position].titleResId); + ((TextView) convertView.findViewById(android.R.id.text2)).setText( + mSamples[position].descriptionResId); + return convertView; + } + } + + private class Sample { + int titleResId; + int descriptionResId; + Intent intent; + + private Sample(int titleResId, int descriptionResId, Intent intent) { + this.intent = intent; + this.titleResId = titleResId; + this.descriptionResId = descriptionResId; + } + + private Sample(int titleResId, int descriptionResId, + Class<? extends Activity> activityClass) { + this(titleResId, descriptionResId, + new Intent(MainActivity.this, activityClass)); + } + } +} diff --git a/samples/browseable/HorizontalPaging/AndroidManifest.xml b/samples/browseable/HorizontalPaging/AndroidManifest.xml new file mode 100644 index 000000000..33b9e15b9 --- /dev/null +++ b/samples/browseable/HorizontalPaging/AndroidManifest.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.horizontalpaging" + android:versionCode="1" + android:versionName="1.0" > + + <!-- While ViewPager will work on API 4 or above, tabs require an ActionBar. ActionBar is only + available in API 11 or above. --> + <uses-sdk + android:minSdkVersion="11" + android:targetSdkVersion="16" /> + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + <!-- This is a standard Activity invocation for MainActivity. --> + <activity + android:name="com.example.android.horizontalpaging.MainActivity" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/samples/browseable/HorizontalPaging/_index.jd b/samples/browseable/HorizontalPaging/_index.jd new file mode 100644 index 000000000..d636d106a --- /dev/null +++ b/samples/browseable/HorizontalPaging/_index.jd @@ -0,0 +1,13 @@ + + + +page.tags="HorizontalPaging" +sample.group=UI +@jd:body + +<p> + + This sample demonstrates how to implement horizontal paging between fragments in + applications that use ActionBar, using a ViewPager widget. + + </p> diff --git a/samples/browseable/HorizontalPaging/res/drawable-hdpi/ic_launcher.png b/samples/browseable/HorizontalPaging/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..96a442e5b --- /dev/null +++ b/samples/browseable/HorizontalPaging/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/HorizontalPaging/res/drawable-hdpi/tile.9.png b/samples/browseable/HorizontalPaging/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/HorizontalPaging/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/HorizontalPaging/res/drawable-mdpi/ic_launcher.png b/samples/browseable/HorizontalPaging/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..359047dfa --- /dev/null +++ b/samples/browseable/HorizontalPaging/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/HorizontalPaging/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/HorizontalPaging/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..71c6d760f --- /dev/null +++ b/samples/browseable/HorizontalPaging/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/HorizontalPaging/res/layout/activity_main.xml b/samples/browseable/HorizontalPaging/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/HorizontalPaging/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/HorizontalPaging/res/layout/fragment_main_dummy.xml b/samples/browseable/HorizontalPaging/res/layout/fragment_main_dummy.xml new file mode 100644 index 000000000..6197494d6 --- /dev/null +++ b/samples/browseable/HorizontalPaging/res/layout/fragment_main_dummy.xml @@ -0,0 +1,16 @@ +<RelativeLayout 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:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + tools:context=".MainActivity$DummySectionFragment"> + + <TextView + android:id="@+id/section_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + +</RelativeLayout> diff --git a/samples/browseable/HorizontalPaging/res/layout/sample_main.xml b/samples/browseable/HorizontalPaging/res/layout/sample_main.xml new file mode 100644 index 000000000..237372c5f --- /dev/null +++ b/samples/browseable/HorizontalPaging/res/layout/sample_main.xml @@ -0,0 +1,8 @@ +<!-- BEGIN_INCLUDE (view_pager_layout) --> +<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/pager" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".MainActivity" /> +<!-- END_INCLUDE (view_pager_layout) --> diff --git a/samples/browseable/HorizontalPaging/res/values-sw600dp/dimens.xml b/samples/browseable/HorizontalPaging/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/HorizontalPaging/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/HorizontalPaging/res/values-sw600dp/styles.xml b/samples/browseable/HorizontalPaging/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/HorizontalPaging/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/HorizontalPaging/res/values/base-strings.xml b/samples/browseable/HorizontalPaging/res/values/base-strings.xml new file mode 100644 index 000000000..70484bcb4 --- /dev/null +++ b/samples/browseable/HorizontalPaging/res/values/base-strings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">HorizontalPaging</string> + <string name="intro_message"> + <![CDATA[ + + + This sample demonstrates how to implement horizontal paging between fragments in + applications that use ActionBar, using a ViewPager widget. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/HorizontalPaging/res/values/dimens.xml b/samples/browseable/HorizontalPaging/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/HorizontalPaging/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/HorizontalPaging/res/values/strings.xml b/samples/browseable/HorizontalPaging/res/values/strings.xml new file mode 100644 index 000000000..f9b4212bf --- /dev/null +++ b/samples/browseable/HorizontalPaging/res/values/strings.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="action_settings">Settings</string> + <string name="title_section1">Section 1</string> + <string name="title_section2">Section 2</string> + <string name="title_section3">Section 3</string> + +</resources> diff --git a/samples/browseable/HorizontalPaging/res/values/styles.xml b/samples/browseable/HorizontalPaging/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/HorizontalPaging/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/HorizontalPaging/res/values/values-sw720dp-land/dimens.xml b/samples/browseable/HorizontalPaging/res/values/values-sw720dp-land/dimens.xml new file mode 100644 index 000000000..00059fc5c --- /dev/null +++ b/samples/browseable/HorizontalPaging/res/values/values-sw720dp-land/dimens.xml @@ -0,0 +1,5 @@ +<resources> + <!-- Customize dimensions originally defined in res/values/dimens.xml (such as + screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here. --> + <dimen name="activity_horizontal_margin">128dp</dimen> +</resources> diff --git a/samples/browseable/HorizontalPaging/src/com.example.android.horizontalpaging/MainActivity.java b/samples/browseable/HorizontalPaging/src/com.example.android.horizontalpaging/MainActivity.java new file mode 100644 index 000000000..fdd4495f3 --- /dev/null +++ b/samples/browseable/HorizontalPaging/src/com.example.android.horizontalpaging/MainActivity.java @@ -0,0 +1,219 @@ +package com.example.android.horizontalpaging; + +import android.app.ActionBar; +import android.app.FragmentTransaction; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.ViewPager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import java.util.Locale; + +public class MainActivity extends FragmentActivity implements ActionBar.TabListener { + + /** + * The {@link android.support.v4.view.PagerAdapter} that will provide + * fragments for each of the sections. We use a + * {@link android.support.v4.app.FragmentPagerAdapter} derivative, which + * will keep every loaded fragment in memory. If this becomes too memory + * intensive, it may be best to switch to a + * {@link android.support.v4.app.FragmentStatePagerAdapter}. + */ + SectionsPagerAdapter mSectionsPagerAdapter; + + /** + * The {@link ViewPager} that will host the section contents. + */ + ViewPager mViewPager; + + /** + * Create the activity. Sets up an {@link android.app.ActionBar} with tabs, and then configures the + * {@link ViewPager} contained inside R.layout.activity_main. + * + * <p>A {@link SectionsPagerAdapter} will be instantiated to hold the different pages of + * fragments that are to be displayed. A + * {@link android.support.v4.view.ViewPager.SimpleOnPageChangeListener} will also be configured + * to receive callbacks when the user swipes between pages in the ViewPager. + * + * @param savedInstanceState + */ + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // Load the UI from res/layout/activity_main.xml + setContentView(R.layout.sample_main); + + // Set up the action bar. The navigation mode is set to NAVIGATION_MODE_TABS, which will + // cause the ActionBar to render a set of tabs. Note that these tabs are *not* rendered + // by the ViewPager; additional logic is lower in this file to synchronize the ViewPager + // state with the tab state. (See mViewPager.setOnPageChangeListener() and onTabSelected().) + // BEGIN_INCLUDE (set_navigation_mode) + final ActionBar actionBar = getActionBar(); + actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + // END_INCLUDE (set_navigation_mode) + + // BEGIN_INCLUDE (setup_view_pager) + // Create the adapter that will return a fragment for each of the three primary sections + // of the app. + mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); + + // Set up the ViewPager with the sections adapter. + mViewPager = (ViewPager) findViewById(R.id.pager); + mViewPager.setAdapter(mSectionsPagerAdapter); + // END_INCLUDE (setup_view_pager) + + // When swiping between different sections, select the corresponding tab. We can also use + // ActionBar.Tab#select() to do this if we have a reference to the Tab. + // BEGIN_INCLUDE (page_change_listener) + mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { + @Override + public void onPageSelected(int position) { + actionBar.setSelectedNavigationItem(position); + } + }); + // END_INCLUDE (page_change_listener) + + // BEGIN_INCLUDE (add_tabs) + // For each of the sections in the app, add a tab to the action bar. + for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) { + // Create a tab with text corresponding to the page title defined by the adapter. Also + // specify this Activity object, which implements the TabListener interface, as the + // callback (listener) for when this tab is selected. + actionBar.addTab( + actionBar.newTab() + .setText(mSectionsPagerAdapter.getPageTitle(i)) + .setTabListener(this)); + } + // END_INCLUDE (add_tabs) + } + + /** + * Update {@link ViewPager} after a tab has been selected in the ActionBar. + * + * @param tab Tab that was selected. + * @param fragmentTransaction A {@link android.app.FragmentTransaction} for queuing fragment operations to + * execute once this method returns. This FragmentTransaction does + * not support being added to the back stack. + */ + // BEGIN_INCLUDE (on_tab_selected) + @Override + public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { + // When the given tab is selected, tell the ViewPager to switch to the corresponding page. + mViewPager.setCurrentItem(tab.getPosition()); + } + // END_INCLUDE (on_tab_selected) + + /** + * Unused. Required for {@link android.app.ActionBar.TabListener}. + */ + @Override + public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { + } + + /** + * Unused. Required for {@link android.app.ActionBar.TabListener}. + */ + @Override + public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { + } + + // BEGIN_INCLUDE (fragment_pager_adapter) + /** + * A {@link FragmentPagerAdapter} that returns a fragment corresponding to + * one of the sections/tabs/pages. This provides the data for the {@link ViewPager}. + */ + public class SectionsPagerAdapter extends FragmentPagerAdapter { + // END_INCLUDE (fragment_pager_adapter) + + public SectionsPagerAdapter(FragmentManager fm) { + super(fm); + } + + // BEGIN_INCLUDE (fragment_pager_adapter_getitem) + /** + * Get fragment corresponding to a specific position. This will be used to populate the + * contents of the {@link ViewPager}. + * + * @param position Position to fetch fragment for. + * @return Fragment for specified position. + */ + @Override + public Fragment getItem(int position) { + // getItem is called to instantiate the fragment for the given page. + // Return a DummySectionFragment (defined as a static inner class + // below) with the page number as its lone argument. + Fragment fragment = new DummySectionFragment(); + Bundle args = new Bundle(); + args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, position + 1); + fragment.setArguments(args); + return fragment; + } + // END_INCLUDE (fragment_pager_adapter_getitem) + + // BEGIN_INCLUDE (fragment_pager_adapter_getcount) + /** + * Get number of pages the {@link ViewPager} should render. + * + * @return Number of fragments to be rendered as pages. + */ + @Override + public int getCount() { + // Show 3 total pages. + return 3; + } + // END_INCLUDE (fragment_pager_adapter_getcount) + + // BEGIN_INCLUDE (fragment_pager_adapter_getpagetitle) + /** + * Get title for each of the pages. This will be displayed on each of the tabs. + * + * @param position Page to fetch title for. + * @return Title for specified page. + */ + @Override + public CharSequence getPageTitle(int position) { + Locale l = Locale.getDefault(); + switch (position) { + case 0: + return getString(R.string.title_section1).toUpperCase(l); + case 1: + return getString(R.string.title_section2).toUpperCase(l); + case 2: + return getString(R.string.title_section3).toUpperCase(l); + } + return null; + } + // END_INCLUDE (fragment_pager_adapter_getpagetitle) + } + + /** + * A dummy fragment representing a section of the app, but that simply displays dummy text. + * This would be replaced with your application's content. + */ + public static class DummySectionFragment extends Fragment { + /** + * The fragment argument representing the section number for this + * fragment. + */ + public static final String ARG_SECTION_NUMBER = "section_number"; + + public DummySectionFragment() { + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_main_dummy, container, false); + TextView dummyTextView = (TextView) rootView.findViewById(R.id.section_label); + dummyTextView.setText(Integer.toString(getArguments().getInt(ARG_SECTION_NUMBER))); + return rootView; + } + } + +} diff --git a/samples/browseable/ImmersiveMode/AndroidManifest.xml b/samples/browseable/ImmersiveMode/AndroidManifest.xml new file mode 100644 index 000000000..72a60ce15 --- /dev/null +++ b/samples/browseable/ImmersiveMode/AndroidManifest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.immersivemode" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="19" /> + + <application android:allowBackup="true" + android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@style/AppTheme"> + + <activity android:name=".MainActivity" + android:label="@string/app_name" + android:uiOptions="splitActionBarWhenNarrow"> + <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/browseable/ImmersiveMode/_index.jd b/samples/browseable/ImmersiveMode/_index.jd new file mode 100644 index 000000000..7c9a2728f --- /dev/null +++ b/samples/browseable/ImmersiveMode/_index.jd @@ -0,0 +1,14 @@ + + + +page.tags="ImmersiveMode" +sample.group=UI +@jd:body + +<p> + + One of the features introduced in KitKat is "immersive mode". Immersive mode gives the + user the ability to show/hide the status bar and navigation bar with a swipe. To try, + click the "Toggle immersive mode" button, then try swiping the bar in and out! + + </p> diff --git a/samples/browseable/ImmersiveMode/res/drawable-hdpi/ic_launcher.png b/samples/browseable/ImmersiveMode/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..b1efaf4b2 --- /dev/null +++ b/samples/browseable/ImmersiveMode/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/ImmersiveMode/res/drawable-hdpi/tile.9.png b/samples/browseable/ImmersiveMode/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/ImmersiveMode/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/ImmersiveMode/res/drawable-mdpi/ic_launcher.png b/samples/browseable/ImmersiveMode/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f5f9244f2 --- /dev/null +++ b/samples/browseable/ImmersiveMode/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/ImmersiveMode/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/ImmersiveMode/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..5d07b3f06 --- /dev/null +++ b/samples/browseable/ImmersiveMode/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/ImmersiveMode/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/ImmersiveMode/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6ef21e1f4 --- /dev/null +++ b/samples/browseable/ImmersiveMode/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/ImmersiveMode/res/layout/activity_main.xml b/samples/browseable/ImmersiveMode/res/layout/activity_main.xml new file mode 100755 index 000000000..bc5a57591 --- /dev/null +++ b/samples/browseable/ImmersiveMode/res/layout/activity_main.xml @@ -0,0 +1,38 @@ +<!-- + 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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:id="@+id/sample_main_layout"> + <TextView android:id="@+id/sample_output" + style="@style/Widget.SampleMessage" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:text="@string/intro_message" /> + <View + android:layout_width="fill_parent" + android:layout_height="1dp" + android:background="@android:color/darker_gray"/> + <fragment + android:name="com.example.android.common.logger.LogFragment" + android:id="@+id/log_fragment" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" /> +</LinearLayout> diff --git a/samples/browseable/ImmersiveMode/res/menu/main.xml b/samples/browseable/ImmersiveMode/res/menu/main.xml new file mode 100644 index 000000000..2c3515dd4 --- /dev/null +++ b/samples/browseable/ImmersiveMode/res/menu/main.xml @@ -0,0 +1,21 @@ +<!-- + 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. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/sample_action" + android:showAsAction="ifRoom|withText" + android:title="@string/sample_action" /> +</menu> diff --git a/samples/browseable/ImmersiveMode/res/values-sw600dp/dimens.xml b/samples/browseable/ImmersiveMode/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/ImmersiveMode/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/ImmersiveMode/res/values-sw600dp/styles.xml b/samples/browseable/ImmersiveMode/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/ImmersiveMode/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/ImmersiveMode/res/values/base-strings.xml b/samples/browseable/ImmersiveMode/res/values/base-strings.xml new file mode 100644 index 000000000..2092f63a4 --- /dev/null +++ b/samples/browseable/ImmersiveMode/res/values/base-strings.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">ImmersiveMode</string> + <string name="intro_message"> + <![CDATA[ + + + One of the features introduced in KitKat is "immersive mode". Immersive mode gives the + user the ability to show/hide the status bar and navigation bar with a swipe. To try, + click the "Toggle immersive mode" button, then try swiping the bar in and out! + + + ]]> + </string> +</resources> diff --git a/samples/browseable/ImmersiveMode/res/values/dimens.xml b/samples/browseable/ImmersiveMode/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/ImmersiveMode/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/ImmersiveMode/res/values/strings.xml b/samples/browseable/ImmersiveMode/res/values/strings.xml new file mode 100644 index 000000000..8a1e9e16f --- /dev/null +++ b/samples/browseable/ImmersiveMode/res/values/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="sample_action">Toggle immersive mode!</string> +</resources> diff --git a/samples/browseable/ImmersiveMode/res/values/styles.xml b/samples/browseable/ImmersiveMode/res/values/styles.xml new file mode 100644 index 000000000..d3f82ff64 --- /dev/null +++ b/samples/browseable/ImmersiveMode/res/values/styles.xml @@ -0,0 +1,51 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="AppTheme" parent="Theme.Base" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + + + <style name="Widget.SampleOutput"> + <item name="android:padding">@dimen/margin_medium</item> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Log" parent="Widget.SampleOutput"> + <item name="android:typeface">monospace</item> + </style> + +</resources> diff --git a/samples/browseable/ImmersiveMode/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/ImmersiveMode/src/com.example.android.common/activities/SampleActivityBase.java new file mode 100644 index 000000000..3228927b7 --- /dev/null +++ b/samples/browseable/ImmersiveMode/src/com.example.android.common/activities/SampleActivityBase.java @@ -0,0 +1,52 @@ +/* +* 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.common.activities; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogWrapper; + +/** + * Base launcher activity, to handle most of the common plumbing for samples. + */ +public class SampleActivityBase extends FragmentActivity { + + public static final String TAG = "SampleActivityBase"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + initializeLogging(); + } + + /** Set up targets to receive log data */ + public void initializeLogging() { + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/Log.java b/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/LogNode.java b/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/LogView.java b/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/ImmersiveMode/src/com.example.android.common/logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/ImmersiveMode/src/com.example.android.immersivemode/ImmersiveModeFragment.java b/samples/browseable/ImmersiveMode/src/com.example.android.immersivemode/ImmersiveModeFragment.java new file mode 100644 index 000000000..8a7255cff --- /dev/null +++ b/samples/browseable/ImmersiveMode/src/com.example.android.immersivemode/ImmersiveModeFragment.java @@ -0,0 +1,103 @@ +/* +* Copyright (C) 2012 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.immersivemode; + +import android.os.Build; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.MenuItem; +import android.view.View; + +import com.example.android.common.logger.Log; + +public class ImmersiveModeFragment extends Fragment { + + public static final String TAG = "ImmersiveModeFragment"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + final View decorView = getActivity().getWindow().getDecorView(); + decorView.setOnSystemUiVisibilityChangeListener( + new View.OnSystemUiVisibilityChangeListener() { + @Override + public void onSystemUiVisibilityChange(int i) { + int height = decorView.getHeight(); + Log.i(TAG, "Current height: " + height); + } + }); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.sample_action) { + toggleHideyBar(); + } + return true; + } + + /** + * Detects and toggles immersive mode (also known as "hidey bar" mode). + */ + public void toggleHideyBar() { + + // BEGIN_INCLUDE (get_current_ui_flags) + // The UI options currently enabled are represented by a bitfield. + // getSystemUiVisibility() gives us that bitfield. + int uiOptions = getActivity().getWindow().getDecorView().getSystemUiVisibility(); + int newUiOptions = uiOptions; + // END_INCLUDE (get_current_ui_flags) + // BEGIN_INCLUDE (toggle_ui_flags) + boolean isImmersiveModeEnabled = + ((uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) == uiOptions); + if (isImmersiveModeEnabled) { + Log.i(TAG, "Turning immersive mode mode off. "); + } else { + Log.i(TAG, "Turning immersive mode mode on."); + } + + // Navigation bar hiding: Backwards compatible to ICS. + if (Build.VERSION.SDK_INT >= 14) { + newUiOptions ^= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + } + + // Status bar hiding: Backwards compatible to Jellybean + if (Build.VERSION.SDK_INT >= 16) { + newUiOptions ^= View.SYSTEM_UI_FLAG_FULLSCREEN; + } + + // Immersive mode: Backward compatible to KitKat. + // Note that this flag doesn't do anything by itself, it only augments the behavior + // of HIDE_NAVIGATION and FLAG_FULLSCREEN. For the purposes of this sample + // all three flags are being toggled together. + // Note that there are two immersive mode UI flags, one of which is referred to as "sticky". + // Sticky immersive mode differs in that it makes the navigation and status bars + // semi-transparent, and the UI flag does not get cleared when the user interacts with + // the screen. + if (Build.VERSION.SDK_INT >= 18) { + newUiOptions ^= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + } + + getActivity().getWindow().getDecorView().setSystemUiVisibility(newUiOptions); + //END_INCLUDE (set_ui_flags) + } +} diff --git a/samples/browseable/ImmersiveMode/src/com.example.android.immersivemode/MainActivity.java b/samples/browseable/ImmersiveMode/src/com.example.android.immersivemode/MainActivity.java new file mode 100644 index 000000000..fd0211275 --- /dev/null +++ b/samples/browseable/ImmersiveMode/src/com.example.android.immersivemode/MainActivity.java @@ -0,0 +1,80 @@ +/* +* 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.immersivemode; + +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.view.Menu; + +import com.example.android.common.activities.SampleActivityBase; +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogFragment; +import com.example.android.common.logger.LogWrapper; +import com.example.android.common.logger.MessageOnlyLogFilter; + +/** + * A simple launcher activity containing a summary sample description + * and a few action bar buttons. + */ +public class MainActivity extends SampleActivityBase { + + public static final String TAG = "MainActivity"; + + public static final String FRAGTAG = "ImmersiveModeFragment"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + if (getSupportFragmentManager().findFragmentByTag(FRAGTAG) == null ) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + ImmersiveModeFragment fragment = new ImmersiveModeFragment(); + transaction.add(fragment, FRAGTAG); + transaction.commit(); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + /** Create a chain of targets that will receive log data */ + @Override + public void initializeLogging() { + // Wraps Android's native log framework. + LogWrapper logWrapper = new LogWrapper(); + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + Log.setLogNode(logWrapper); + + // Filter strips out everything except the message text. + MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); + logWrapper.setNext(msgFilter); + + // On screen logging via a fragment with a TextView. + LogFragment logFragment = (LogFragment) getSupportFragmentManager() + .findFragmentById(R.id.log_fragment); + msgFilter.setNext(logFragment.getLogView()); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/ListPopupMenu/AndroidManifest.xml b/samples/browseable/ListPopupMenu/AndroidManifest.xml new file mode 100644 index 000000000..114053ed6 --- /dev/null +++ b/samples/browseable/ListPopupMenu/AndroidManifest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.actionbarcompat.listpopupmenu" + android:versionCode="1" + android:versionName="1.0"> + + <!-- ActionBarCompat provides an implementation of Popup Menu from API v7 onwards --> + <uses-sdk + android:minSdkVersion="7" + android:targetSdkVersion="17" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/Theme.AppCompat" + android:allowBackup="true"> + + <activity android:name=".MainActivity"> + <!-- Launcher Intent filter --> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest>
\ No newline at end of file diff --git a/samples/browseable/ListPopupMenu/_index.jd b/samples/browseable/ListPopupMenu/_index.jd new file mode 100644 index 000000000..97d95877c --- /dev/null +++ b/samples/browseable/ListPopupMenu/_index.jd @@ -0,0 +1,13 @@ + + + +page.tags="ListPopupMenu" +sample.group=UI +@jd:body + +<p> + + This sample shows you how to use {@link android.support.v7.widget.PopupMenu PopupMenu} + from ActionBarCompat to create a list, with each item having a dropdown menu. + + </p> diff --git a/samples/browseable/ListPopupMenu/res/drawable-hdpi/ic_launcher.png b/samples/browseable/ListPopupMenu/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..a7365b9eb --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/ListPopupMenu/res/drawable-hdpi/ic_overflow.png b/samples/browseable/ListPopupMenu/res/drawable-hdpi/ic_overflow.png Binary files differnew file mode 100644 index 000000000..2abc45809 --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/drawable-hdpi/ic_overflow.png diff --git a/samples/browseable/ListPopupMenu/res/drawable-hdpi/tile.9.png b/samples/browseable/ListPopupMenu/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/ListPopupMenu/res/drawable-mdpi/ic_launcher.png b/samples/browseable/ListPopupMenu/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..3fd55935e --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/ListPopupMenu/res/drawable-mdpi/ic_overflow.png b/samples/browseable/ListPopupMenu/res/drawable-mdpi/ic_overflow.png Binary files differnew file mode 100644 index 000000000..ba704b67e --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/drawable-mdpi/ic_overflow.png diff --git a/samples/browseable/ListPopupMenu/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/ListPopupMenu/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..204f861ba --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/ListPopupMenu/res/drawable-xhdpi/ic_overflow.png b/samples/browseable/ListPopupMenu/res/drawable-xhdpi/ic_overflow.png Binary files differnew file mode 100644 index 000000000..a92fb1d4a --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/drawable-xhdpi/ic_overflow.png diff --git a/samples/browseable/ListPopupMenu/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/ListPopupMenu/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..ada8266c6 --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/ListPopupMenu/res/layout/activity_main.xml b/samples/browseable/ListPopupMenu/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/ListPopupMenu/res/layout/list_item.xml b/samples/browseable/ListPopupMenu/res/layout/list_item.xml new file mode 100644 index 000000000..3eabda0e0 --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/layout/list_item.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="?attr/listPreferredItemHeight"> + + <TextView + android:id="@android:id/text1" + android:layout_height="match_parent" + android:layout_width="0dp" + android:layout_weight="1" + android:gravity="center_vertical" + android:paddingLeft="8dp" + android:paddingRight="8dp" + android:maxLines="1" + android:ellipsize="end" + android:textAppearance="?android:attr/textAppearanceMedium" /> + + <ImageView + android:id="@+id/button_popup" + android:layout_height="match_parent" + android:layout_width="56dip" + android:background="?attr/selectableItemBackground" + android:src="@drawable/ic_overflow" + android:contentDescription="@string/content_open_popup"/> + +</LinearLayout>
\ No newline at end of file diff --git a/samples/browseable/ListPopupMenu/res/layout/sample_main.xml b/samples/browseable/ListPopupMenu/res/layout/sample_main.xml new file mode 100644 index 000000000..bfc7ad0c3 --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/layout/sample_main.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> +<fragment xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:name="com.example.android.actionbarcompat.listpopupmenu.PopupListFragment" />
\ No newline at end of file diff --git a/samples/browseable/ListPopupMenu/res/menu/popup.xml b/samples/browseable/ListPopupMenu/res/menu/popup.xml new file mode 100644 index 000000000..0329e9e4d --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/menu/popup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + + <item + android:id="@+id/menu_remove" + android:title="@string/menu_remove" /> + +</menu>
\ No newline at end of file diff --git a/samples/browseable/ListPopupMenu/res/values-sw600dp/dimens.xml b/samples/browseable/ListPopupMenu/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/ListPopupMenu/res/values-sw600dp/styles.xml b/samples/browseable/ListPopupMenu/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/ListPopupMenu/res/values/base-strings.xml b/samples/browseable/ListPopupMenu/res/values/base-strings.xml new file mode 100644 index 000000000..a23ed75cb --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/values/base-strings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">ListPopupMenu</string> + <string name="intro_message"> + <![CDATA[ + + + This sample shows you how to use {@link android.support.v7.widget.PopupMenu PopupMenu} + from ActionBarCompat to create a list, with each item having a dropdown menu. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/ListPopupMenu/res/values/dimens.xml b/samples/browseable/ListPopupMenu/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/ListPopupMenu/res/values/strings.xml b/samples/browseable/ListPopupMenu/res/values/strings.xml new file mode 100644 index 000000000..e5784a9c9 --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/values/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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="menu_remove">Remove</string> + <string name="content_open_popup">Open Popup Menu</string> + +</resources>
\ No newline at end of file diff --git a/samples/browseable/ListPopupMenu/res/values/styles.xml b/samples/browseable/ListPopupMenu/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/ListPopupMenu/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/Cheeses.java b/samples/browseable/ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/Cheeses.java new file mode 100644 index 000000000..5ef11616b --- /dev/null +++ b/samples/browseable/ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/Cheeses.java @@ -0,0 +1,155 @@ +/* + * 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.actionbarcompat.listpopupmenu; + +/** + * Dummy data. + */ +public class Cheeses { + public static final String[] CHEESES = { + "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi", + "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", + "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese", + "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell", + "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc", + "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss", + "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon", + "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase", + "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese", + "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy", + "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille", + "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore", + "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)", + "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves", + "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur", + "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon", + "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin", + "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)", + "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine", + "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza", + "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)", + "Buxton Blue", "Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta", + "Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie", + "Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat", + "Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano", + "Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain", + "Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou", + "Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar", + "Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno", + "Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack", + "Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper", + "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)", + "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese", + "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza", + "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley", + "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino", + "Cypress Grove Chevre", "Danablu (Danish Blue)", "Danbo", "Danish Fontina", + "Daralagjazsky", "Dauphin", "Delice des Fiouves", "Denhany Dorset Drum", "Derby", + "Dessertnyj Belyj", "Devon Blue", "Devon Garland", "Dolcelatte", "Doolin", + "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester", "Double Worcester", + "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra", "Dunlop", "Dunsyre Blue", + "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz", + "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne", "Esbareich", + "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo", "Exmoor Blue", + "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle", + "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis", "Flor de Guia", + "Flower Marie", "Folded", "Folded cheese with mint", "Fondant de Brebis", + "Fontainebleau", "Fontal", "Fontina Val d'Aosta", "Formaggio di capra", "Fougerus", + "Four Herb Gouda", "Fourme d' Ambert", "Fourme de Haute Loire", "Fourme de Montbrison", + "Fresh Jack", "Fresh Mozzarella", "Fresh Ricotta", "Fresh Truffles", "Fribourgeois", + "Friesekaas", "Friesian", "Friesla", "Frinault", "Fromage a Raclette", "Fromage Corse", + "Fromage de Montagne de Savoie", "Fromage Frais", "Fruit Cream Cheese", + "Frying Cheese", "Fynbo", "Gabriel", "Galette du Paludier", "Galette Lyonnaise", + "Galloway Goat's Milk Gems", "Gammelost", "Gaperon a l'Ail", "Garrotxa", "Gastanberra", + "Geitost", "Gippsland Blue", "Gjetost", "Gloucester", "Golden Cross", "Gorgonzola", + "Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost", + "Grafton Village Cheddar", "Grana", "Grana Padano", "Grand Vatel", + "Grataron d' Areches", "Gratte-Paille", "Graviera", "Greuilh", "Greve", + "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi", + "Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti", + "Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve", + "Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster", + "Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg", + "Jermi Tortes", "Jibneh Arabieh", "Jindi Brie", "Jubilee Blue", "Juustoleipa", + "Kadchgall", "Kaseri", "Kashta", "Kefalotyri", "Kenafa", "Kernhem", "Kervella Affine", + "Kikorangi", "King Island Cape Wickham Brie", "King River Gold", "Klosterkaese", + "Knockalara", "Kugelkase", "L'Aveyronnais", "L'Ecir de l'Aubrac", "La Taupiniere", + "La Vache Qui Rit", "Laguiole", "Lairobell", "Lajta", "Lanark Blue", "Lancashire", + "Langres", "Lappi", "Laruns", "Lavistown", "Le Brin", "Le Fium Orbo", "Le Lacandou", + "Le Roule", "Leafield", "Lebbene", "Leerdammer", "Leicester", "Leyden", "Limburger", + "Lincolnshire Poacher", "Lingot Saint Bousquet d'Orb", "Liptauer", "Little Rydings", + "Livarot", "Llanboidy", "Llanglofan Farmhouse", "Loch Arthur Farmhouse", + "Loddiswell Avondale", "Longhorn", "Lou Palou", "Lou Pevre", "Lyonnais", "Maasdam", + "Macconais", "Mahoe Aged Gouda", "Mahon", "Malvern", "Mamirolle", "Manchego", + "Manouri", "Manur", "Marble Cheddar", "Marbled Cheeses", "Maredsous", "Margotin", + "Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)", + "Mascarpone Torta", "Matocq", "Maytag Blue", "Meira", "Menallack Farmhouse", + "Menonita", "Meredith Blue", "Mesost", "Metton (Cancoillotte)", "Meyer Vintage Gouda", + "Mihalic Peynir", "Milleens", "Mimolette", "Mine-Gabhar", "Mini Baby Bells", "Mixte", + "Molbo", "Monastery Cheeses", "Mondseer", "Mont D'or Lyonnais", "Montasio", + "Monterey Jack", "Monterey Jack Dry", "Morbier", "Morbier Cru de Montagne", + "Mothais a la Feuille", "Mozzarella", "Mozzarella (Australian)", + "Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster", + "Murol", "Mycella", "Myzithra", "Naboulsi", "Nantais", "Neufchatel", + "Neufchatel (Australian)", "Niolo", "Nokkelost", "Northumberland", "Oaxaca", + "Olde York", "Olivet au Foin", "Olivet Bleu", "Olivet Cendre", + "Orkney Extra Mature Cheddar", "Orla", "Oschtjepka", "Ossau Fermier", "Ossau-Iraty", + "Oszczypek", "Oxford Blue", "P'tit Berrichon", "Palet de Babligny", "Paneer", "Panela", + "Pannerone", "Pant ys Gawn", "Parmesan (Parmigiano)", "Parmigiano Reggiano", + "Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage", + "Patefine Fort", "Pave d'Affinois", "Pave d'Auge", "Pave de Chirac", "Pave du Berry", + "Pecorino", "Pecorino in Walnut Leaves", "Pecorino Romano", "Peekskill Pyramid", + "Pelardon des Cevennes", "Pelardon des Corbieres", "Penamellera", "Penbryn", + "Pencarreg", "Perail de Brebis", "Petit Morin", "Petit Pardou", "Petit-Suisse", + "Picodon de Chevre", "Picos de Europa", "Piora", "Pithtviers au Foin", + "Plateau de Herve", "Plymouth Cheese", "Podhalanski", "Poivre d'Ane", "Polkolbin", + "Pont l'Eveque", "Port Nicholson", "Port-Salut", "Postel", "Pouligny-Saint-Pierre", + "Pourly", "Prastost", "Pressato", "Prince-Jean", "Processed Cheddar", "Provolone", + "Provolone (Australian)", "Pyengana Cheddar", "Pyramide", "Quark", + "Quark (Australian)", "Quartirolo Lombardo", "Quatre-Vents", "Quercy Petit", + "Queso Blanco", "Queso Blanco con Frutas --Pina y Mango", "Queso de Murcia", + "Queso del Montsec", "Queso del Tietar", "Queso Fresco", "Queso Fresco (Adobera)", + "Queso Iberico", "Queso Jalapeno", "Queso Majorero", "Queso Media Luna", + "Queso Para Frier", "Queso Quesadilla", "Rabacal", "Raclette", "Ragusano", "Raschera", + "Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou", + "Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder", + "Rigotte", "Rocamadour", "Rollot", "Romano", "Romans Part Dieu", "Roncal", "Roquefort", + "Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr", + "Saanenkaese", "Saga", "Sage Derby", "Sainte Maure", "Saint-Marcellin", + "Saint-Nectaire", "Saint-Paulin", "Salers", "Samso", "San Simon", "Sancerre", + "Sap Sago", "Sardo", "Sardo Egyptian", "Sbrinz", "Scamorza", "Schabzieger", "Schloss", + "Selles sur Cher", "Selva", "Serat", "Seriously Strong Cheddar", "Serra da Estrela", + "Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda", + "Somerset Brie", "Sonoma Jack", "Sottocenare al Tartufo", "Soumaintrain", + "Sourire Lozerien", "Spenwood", "Sraffordshire Organic", "St. Agur Blue Cheese", + "Stilton", "Stinking Bishop", "String", "Sussex Slipcote", "Sveciaost", "Swaledale", + "Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie", + "Tasmania Highland Chevre Log", "Taupiniere", "Teifi", "Telemea", "Testouri", + "Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar", + "Tilsit", "Timboon Brie", "Toma", "Tomme Brulee", "Tomme d'Abondance", + "Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes", + "Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet", + "Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe", + "Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa", + "Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois", + "Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue", + "Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington", + "Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou", + "Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue", + "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano" + }; +}
\ No newline at end of file diff --git a/samples/browseable/ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/MainActivity.java b/samples/browseable/ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/MainActivity.java new file mode 100644 index 000000000..13a77f3ec --- /dev/null +++ b/samples/browseable/ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/MainActivity.java @@ -0,0 +1,41 @@ +/* + * 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.actionbarcompat.listpopupmenu; + +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; + +/** + * This sample shows you how to use {@link android.support.v7.widget.PopupMenu PopupMenu} from + * ActionBarCompat to create a list, with each item having a dropdown menu. + * <p> + * The interesting part of this sample is in {@link PopupListFragment}. + * + * This Activity extends from {@link ActionBarActivity}, which provides all of the function + * necessary to display a compatible Action Bar on devices running Android v2.1+. + */ +public class MainActivity extends ActionBarActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Set content view (which contains a PopupListFragment) + setContentView(R.layout.sample_main); + } + +} diff --git a/samples/browseable/ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/PopupListFragment.java b/samples/browseable/ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/PopupListFragment.java new file mode 100644 index 000000000..754bf224e --- /dev/null +++ b/samples/browseable/ListPopupMenu/src/com.example.android.actionbarcompat.listpopupmenu/PopupListFragment.java @@ -0,0 +1,134 @@ +/* + * 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.actionbarcompat.listpopupmenu; + +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.support.v7.widget.PopupMenu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.Toast; + +import java.util.ArrayList; + +/** + * This ListFragment displays a list of cheeses, with a clickable view on each item whichs displays + * a {@link android.support.v7.widget.PopupMenu PopupMenu} when clicked, allowing the user to + * remove the item from the list. + */ +public class PopupListFragment extends ListFragment implements View.OnClickListener { + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + // We want to allow modifications to the list so copy the dummy data array into an ArrayList + ArrayList<String> items = new ArrayList<String>(); + for (int i = 0, z = Cheeses.CHEESES.length ; i < z ; i++) { + items.add(Cheeses.CHEESES[i]); + } + + // Set the ListAdapter + setListAdapter(new PopupAdapter(items)); + } + + @Override + public void onListItemClick(ListView listView, View v, int position, long id) { + String item = (String) listView.getItemAtPosition(position); + + // Show a toast if the user clicks on an item + Toast.makeText(getActivity(), "Item Clicked: " + item, Toast.LENGTH_SHORT).show(); + } + + @Override + public void onClick(final View view) { + // We need to post a Runnable to show the popup to make sure that the PopupMenu is + // correctly positioned. The reason being that the view may change position before the + // PopupMenu is shown. + view.post(new Runnable() { + @Override + public void run() { + showPopupMenu(view); + } + }); + } + + // BEGIN_INCLUDE(show_popup) + private void showPopupMenu(View view) { + final PopupAdapter adapter = (PopupAdapter) getListAdapter(); + + // Retrieve the clicked item from view's tag + final String item = (String) view.getTag(); + + // Create a PopupMenu, giving it the clicked view for an anchor + PopupMenu popup = new PopupMenu(getActivity(), view); + + // Inflate our menu resource into the PopupMenu's Menu + popup.getMenuInflater().inflate(R.menu.popup, popup.getMenu()); + + // Set a listener so we are notified if a menu item is clicked + popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem menuItem) { + switch (menuItem.getItemId()) { + case R.id.menu_remove: + // Remove the item from the adapter + adapter.remove(item); + return true; + } + return false; + } + }); + + // Finally show the PopupMenu + popup.show(); + } + // END_INCLUDE(show_popup) + + /** + * A simple array adapter that creates a list of cheeses. + */ + class PopupAdapter extends ArrayAdapter<String> { + + PopupAdapter(ArrayList<String> items) { + super(getActivity(), R.layout.list_item, android.R.id.text1, items); + } + + @Override + public View getView(int position, View convertView, ViewGroup container) { + // Let ArrayAdapter inflate the layout and set the text + View view = super.getView(position, convertView, container); + + // BEGIN_INCLUDE(button_popup) + // Retrieve the popup button from the inflated view + View popupButton = view.findViewById(R.id.button_popup); + + // Set the item as the button's tag so it can be retrieved later + popupButton.setTag(getItem(position)); + + // Set the fragment instance as the OnClickListener + popupButton.setOnClickListener(PopupListFragment.this); + // END_INCLUDE(button_popup) + + // Finally return the view to be displayed + return view; + } + } + +} diff --git a/samples/browseable/MediaRecorder/AndroidManifest.xml b/samples/browseable/MediaRecorder/AndroidManifest.xml new file mode 100644 index 000000000..32f88f64f --- /dev/null +++ b/samples/browseable/MediaRecorder/AndroidManifest.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.mediarecorder" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk + android:minSdkVersion="14" + android:targetSdkVersion="17" /> + + <!-- This app records A/V content from camera and stores it to disk --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.RECORD_VIDEO" /> + <uses-permission android:name="android.permission.RECORD_AUDIO"/> + <uses-permission android:name="android.permission.CAMERA" /> + <uses-feature android:name="android.hardware.camera" /> + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme"> + <!-- Since this sample records video from camera preview, locking the orientation to + landscape. Landscape mode offers us more preview space with standard video aspect + ratios (width > height) --> + <activity + android:name=".MainActivity" + android:label="@string/app_name" + android:screenOrientation="landscape"> + <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/browseable/MediaRecorder/res/drawable-hdpi/ic_launcher.png b/samples/browseable/MediaRecorder/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..13cd1e8ec --- /dev/null +++ b/samples/browseable/MediaRecorder/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/MediaRecorder/res/drawable-mdpi/ic_launcher.png b/samples/browseable/MediaRecorder/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..00b2bd9a4 --- /dev/null +++ b/samples/browseable/MediaRecorder/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/MediaRecorder/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/MediaRecorder/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..953f1cc53 --- /dev/null +++ b/samples/browseable/MediaRecorder/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/MediaRecorder/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/MediaRecorder/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f2ccb102a --- /dev/null +++ b/samples/browseable/MediaRecorder/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/MediaRecorder/res/layout/sample_main.xml b/samples/browseable/MediaRecorder/res/layout/sample_main.xml new file mode 100644 index 000000000..d53b37646 --- /dev/null +++ b/samples/browseable/MediaRecorder/res/layout/sample_main.xml @@ -0,0 +1,24 @@ +<FrameLayout 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:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:paddingBottom="@dimen/activity_vertical_margin" + android:orientation="vertical" + tools:context=".MainActivity"> + + <TextureView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:id="@+id/surface_view" /> + + <Button + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:id="@+id/button_capture" + android:layout_gravity="bottom" + android:onClick="onCaptureClick" + android:text="@string/btnCapture"/> +</FrameLayout> diff --git a/samples/browseable/MediaRecorder/res/values-sw720dp-land/dimens.xml b/samples/browseable/MediaRecorder/res/values-sw720dp-land/dimens.xml new file mode 100644 index 000000000..c4aad9b04 --- /dev/null +++ b/samples/browseable/MediaRecorder/res/values-sw720dp-land/dimens.xml @@ -0,0 +1,21 @@ +<!-- + 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> + <!-- Customize dimensions originally defined in res/values/dimens.xml (such as + screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here. --> + <dimen name="activity_horizontal_margin">128dp</dimen> +</resources> diff --git a/samples/browseable/MediaRecorder/res/values/dimens.xml b/samples/browseable/MediaRecorder/res/values/dimens.xml new file mode 100644 index 000000000..0353ed695 --- /dev/null +++ b/samples/browseable/MediaRecorder/res/values/dimens.xml @@ -0,0 +1,21 @@ +<!-- + 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="activity_horizontal_margin">16dp</dimen> + <dimen name="activity_vertical_margin">16dp</dimen> +</resources> diff --git a/samples/browseable/MediaRecorder/res/values/strings.xml b/samples/browseable/MediaRecorder/res/values/strings.xml new file mode 100644 index 000000000..b6a2cd2a9 --- /dev/null +++ b/samples/browseable/MediaRecorder/res/values/strings.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. +--> + +<resources> + + <string name="action_settings">Settings</string> + <string name="hello_world">Hello world!</string> + <string name="btnCapture">capture</string> + +</resources> diff --git a/samples/browseable/MediaRecorder/src/com.example.android.mediarecorder/MainActivity.java b/samples/browseable/MediaRecorder/src/com.example.android.mediarecorder/MainActivity.java new file mode 100644 index 000000000..85876363b --- /dev/null +++ b/samples/browseable/MediaRecorder/src/com.example.android.mediarecorder/MainActivity.java @@ -0,0 +1,230 @@ +/* + * 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.mediarecorder; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.hardware.Camera; +import android.media.CamcorderProfile; +import android.media.MediaRecorder; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.TextureView; +import android.view.View; +import android.widget.Button; + +import com.example.android.common.media.CameraHelper; + +import java.io.IOException; +import java.util.List; + +/** + * This activity uses the camera/camcorder as the A/V source for the {@link android.media.MediaRecorder} API. + * A {@link android.view.TextureView} is used as the camera preview which limits the code to API 14+. This + * can be easily replaced with a {@link android.view.SurfaceView} to run on older devices. + */ +public class MainActivity extends Activity { + + private Camera mCamera; + private TextureView mPreview; + private MediaRecorder mMediaRecorder; + + private boolean isRecording = false; + private static final String TAG = "Recorder"; + private Button captureButton; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.sample_main); + + mPreview = (TextureView) findViewById(R.id.surface_view); + captureButton = (Button) findViewById(R.id.button_capture); + } + + /** + * The capture button controls all user interaction. When recording, the button click + * stops recording, releases {@link android.media.MediaRecorder} and {@link android.hardware.Camera}. When not recording, + * it prepares the {@link android.media.MediaRecorder} and starts recording. + * + * @param view the view generating the event. + */ + public void onCaptureClick(View view) { + if (isRecording) { + // BEGIN_INCLUDE(stop_release_media_recorder) + + // stop recording and release camera + mMediaRecorder.stop(); // stop the recording + releaseMediaRecorder(); // release the MediaRecorder object + mCamera.lock(); // take camera access back from MediaRecorder + + // inform the user that recording has stopped + setCaptureButtonText("Capture"); + isRecording = false; + releaseCamera(); + // END_INCLUDE(stop_release_media_recorder) + + } else { + + // BEGIN_INCLUDE(prepare_start_media_recorder) + + new MediaPrepareTask().execute(null, null, null); + + // END_INCLUDE(prepare_start_media_recorder) + + } + } + + private void setCaptureButtonText(String title) { + captureButton.setText(title); + } + + @Override + protected void onPause() { + super.onPause(); + // if we are using MediaRecorder, release it first + releaseMediaRecorder(); + // release the camera immediately on pause event + releaseCamera(); + } + + private void releaseMediaRecorder(){ + if (mMediaRecorder != null) { + // clear recorder configuration + mMediaRecorder.reset(); + // release the recorder object + mMediaRecorder.release(); + mMediaRecorder = null; + // Lock camera for later use i.e taking it back from MediaRecorder. + // MediaRecorder doesn't need it anymore and we will release it if the activity pauses. + mCamera.lock(); + } + } + + private void releaseCamera(){ + if (mCamera != null){ + // release the camera for other applications + mCamera.release(); + mCamera = null; + } + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + private boolean prepareVideoRecorder(){ + + // BEGIN_INCLUDE (configure_preview) + mCamera = CameraHelper.getDefaultCameraInstance(); + + // We need to make sure that our preview and recording video size are supported by the + // camera. Query camera to find all the sizes and choose the optimal size given the + // dimensions of our preview surface. + Camera.Parameters parameters = mCamera.getParameters(); + List<Camera.Size> mSupportedPreviewSizes = parameters.getSupportedPreviewSizes(); + Camera.Size optimalSize = CameraHelper.getOptimalPreviewSize(mSupportedPreviewSizes, + mPreview.getWidth(), mPreview.getHeight()); + + // Use the same size for recording profile. + CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH); + profile.videoFrameWidth = optimalSize.width; + profile.videoFrameHeight = optimalSize.height; + + // likewise for the camera object itself. + parameters.setPreviewSize(profile.videoFrameWidth, profile.videoFrameHeight); + mCamera.setParameters(parameters); + try { + // Requires API level 11+, For backward compatibility use {@link setPreviewDisplay} + // with {@link SurfaceView} + mCamera.setPreviewTexture(mPreview.getSurfaceTexture()); + } catch (IOException e) { + Log.e(TAG, "Surface texture is unavailable or unsuitable" + e.getMessage()); + return false; + } + // END_INCLUDE (configure_preview) + + + // BEGIN_INCLUDE (configure_media_recorder) + mMediaRecorder = new MediaRecorder(); + + // Step 1: Unlock and set camera to MediaRecorder + mCamera.unlock(); + mMediaRecorder.setCamera(mCamera); + + // Step 2: Set sources + mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT ); + mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); + + // Step 3: Set a CamcorderProfile (requires API Level 8 or higher) + mMediaRecorder.setProfile(profile); + + // Step 4: Set output file + mMediaRecorder.setOutputFile(CameraHelper.getOutputMediaFile( + CameraHelper.MEDIA_TYPE_VIDEO).toString()); + // END_INCLUDE (configure_media_recorder) + + // Step 5: Prepare configured MediaRecorder + try { + mMediaRecorder.prepare(); + } catch (IllegalStateException e) { + Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage()); + releaseMediaRecorder(); + return false; + } catch (IOException e) { + Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage()); + releaseMediaRecorder(); + return false; + } + return true; + } + + /** + * Asynchronous task for preparing the {@link android.media.MediaRecorder} since it's a long blocking + * operation. + */ + class MediaPrepareTask extends AsyncTask<Void, Void, Boolean> { + + @Override + protected Boolean doInBackground(Void... voids) { + // initialize video camera + if (prepareVideoRecorder()) { + // Camera is available and unlocked, MediaRecorder is prepared, + // now you can start recording + mMediaRecorder.start(); + + isRecording = true; + } else { + // prepare didn't work, release the camera + releaseMediaRecorder(); + return false; + } + return true; + } + + @Override + protected void onPostExecute(Boolean result) { + if (!result) { + MainActivity.this.finish(); + } + // inform the user that recording has started + setCaptureButtonText("Stop"); + + } + } + +}
\ No newline at end of file diff --git a/samples/browseable/NetworkConnect/AndroidManifest.xml b/samples/browseable/NetworkConnect/AndroidManifest.xml new file mode 100644 index 000000000..1ae29df9a --- /dev/null +++ b/samples/browseable/NetworkConnect/AndroidManifest.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.networkconnect" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" /> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + + <application + android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@style/Theme.Sample" + android:allowBackup="true"> + + <activity + android:name="com.example.android.networkconnect.MainActivity" + android:label="@string/app_name" + android:uiOptions="splitActionBarWhenNarrow"> + + <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/browseable/NetworkConnect/_index.jd b/samples/browseable/NetworkConnect/_index.jd new file mode 100644 index 000000000..911e8bf17 --- /dev/null +++ b/samples/browseable/NetworkConnect/_index.jd @@ -0,0 +1,13 @@ + + + +page.tags="NetworkConnect" +sample.group=Connectivity +@jd:body + +<p> + + This sample demonstrates how to connect to the network and fetch raw HTML using + HttpURLConnection. AsyncTask is used to perform the fetch on a background thread. + + </p> diff --git a/samples/browseable/NetworkConnect/res/drawable-hdpi/ic_launcher.png b/samples/browseable/NetworkConnect/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..22ce60611 --- /dev/null +++ b/samples/browseable/NetworkConnect/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/NetworkConnect/res/drawable-hdpi/tile.9.png b/samples/browseable/NetworkConnect/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/NetworkConnect/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/NetworkConnect/res/drawable-mdpi/ic_launcher.png b/samples/browseable/NetworkConnect/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..f21e17b65 --- /dev/null +++ b/samples/browseable/NetworkConnect/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/NetworkConnect/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/NetworkConnect/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..64b805902 --- /dev/null +++ b/samples/browseable/NetworkConnect/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/NetworkConnect/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/NetworkConnect/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..6b4434a87 --- /dev/null +++ b/samples/browseable/NetworkConnect/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/NetworkConnect/res/layout/activity_main.xml b/samples/browseable/NetworkConnect/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/NetworkConnect/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/NetworkConnect/res/layout/sample_main.xml b/samples/browseable/NetworkConnect/res/layout/sample_main.xml new file mode 100755 index 000000000..76fa7d783 --- /dev/null +++ b/samples/browseable/NetworkConnect/res/layout/sample_main.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + 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. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + <fragment + android:name="com.example.android.networkconnect.SimpleTextFragment" + android:id="@+id/intro_fragment" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + <View + android:layout_width="fill_parent" + android:layout_height="1dp" + android:background="@android:color/darker_gray"/> + <fragment + android:name="com.example.android.common.logger.LogFragment" + android:id="@+id/log_fragment" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" /> +</LinearLayout> diff --git a/samples/browseable/NetworkConnect/res/menu/main.xml b/samples/browseable/NetworkConnect/res/menu/main.xml new file mode 100644 index 000000000..ef1568fc1 --- /dev/null +++ b/samples/browseable/NetworkConnect/res/menu/main.xml @@ -0,0 +1,24 @@ +<!-- + 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. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/fetch_action" + android:showAsAction="ifRoom|withText" + android:title="@string/fetch_text" /> + <item android:id="@+id/clear_action" + android:showAsAction="ifRoom|withText" + android:title="@string/clear_text" /> +</menu> diff --git a/samples/browseable/NetworkConnect/res/values-sw600dp/dimens.xml b/samples/browseable/NetworkConnect/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/NetworkConnect/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/NetworkConnect/res/values-sw600dp/styles.xml b/samples/browseable/NetworkConnect/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/NetworkConnect/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/NetworkConnect/res/values/base-strings.xml b/samples/browseable/NetworkConnect/res/values/base-strings.xml new file mode 100644 index 000000000..0248814b8 --- /dev/null +++ b/samples/browseable/NetworkConnect/res/values/base-strings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">NetworkConnect</string> + <string name="intro_message"> + <![CDATA[ + + + This sample demonstrates how to connect to the network and fetch raw HTML using + HttpURLConnection. AsyncTask is used to perform the fetch on a background thread. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/NetworkConnect/res/values/dimens.xml b/samples/browseable/NetworkConnect/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/NetworkConnect/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/NetworkConnect/res/values/strings.xml b/samples/browseable/NetworkConnect/res/values/strings.xml new file mode 100755 index 000000000..1e7915afe --- /dev/null +++ b/samples/browseable/NetworkConnect/res/values/strings.xml @@ -0,0 +1,25 @@ +<!-- + 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="welcome_message">Welcome to Network Connect! + Click FETCH to fetch the first 500 characters of raw HTML from www.google.com. + </string> + + <string name="fetch_text">Fetch</string> + <string name="clear_text">Clear</string> + <string name="connection_error">Connection error.</string> +</resources> diff --git a/samples/browseable/NetworkConnect/res/values/styles.xml b/samples/browseable/NetworkConnect/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/NetworkConnect/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/Log.java b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogFragment.java b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogNode.java b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogView.java b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogWrapper.java b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/MessageOnlyLogFilter.java b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/MainActivity.java b/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/MainActivity.java new file mode 100755 index 000000000..3ad46463c --- /dev/null +++ b/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/MainActivity.java @@ -0,0 +1,189 @@ +/* + * 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.networkconnect; + +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.util.TypedValue; +import android.view.Menu; +import android.view.MenuItem; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogFragment; +import com.example.android.common.logger.LogWrapper; +import com.example.android.common.logger.MessageOnlyLogFilter; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; + +/** + * Sample application demonstrating how to connect to the network and fetch raw + * HTML. It uses AsyncTask to do the fetch on a background thread. To establish + * the network connection, it uses HttpURLConnection. + * + * This sample uses the logging framework to display log output in the log + * fragment (LogFragment). + */ +public class MainActivity extends FragmentActivity { + + public static final String TAG = "Network Connect"; + + // Reference to the fragment showing events, so we can clear it with a button + // as necessary. + private LogFragment mLogFragment; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.sample_main); + + // Initialize text fragment that displays intro text. + SimpleTextFragment introFragment = (SimpleTextFragment) + getSupportFragmentManager().findFragmentById(R.id.intro_fragment); + introFragment.setText(R.string.welcome_message); + introFragment.getTextView().setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16.0f); + + // Initialize the logging framework. + initializeLogging(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + // When the user clicks FETCH, fetch the first 500 characters of + // raw HTML from www.google.com. + case R.id.fetch_action: + new DownloadTask().execute("http://www.google.com"); + return true; + // Clear the log view fragment. + case R.id.clear_action: + mLogFragment.getLogView().setText(""); + return true; + } + return false; + } + + /** + * Implementation of AsyncTask, to fetch the data in the background away from + * the UI thread. + */ + private class DownloadTask extends AsyncTask<String, Void, String> { + + @Override + protected String doInBackground(String... urls) { + try { + return loadFromNetwork(urls[0]); + } catch (IOException e) { + return getString(R.string.connection_error); + } + } + + /** + * Uses the logging framework to display the output of the fetch + * operation in the log fragment. + */ + @Override + protected void onPostExecute(String result) { + Log.i(TAG, result); + } + } + + /** Initiates the fetch operation. */ + private String loadFromNetwork(String urlString) throws IOException { + InputStream stream = null; + String str =""; + + try { + stream = downloadUrl(urlString); + str = readIt(stream, 500); + } finally { + if (stream != null) { + stream.close(); + } + } + return str; + } + + /** + * Given a string representation of a URL, sets up a connection and gets + * an input stream. + * @param urlString A string representation of a URL. + * @return An InputStream retrieved from a successful HttpURLConnection. + * @throws java.io.IOException + */ + private InputStream downloadUrl(String urlString) throws IOException { + // BEGIN_INCLUDE(get_inputstream) + URL url = new URL(urlString); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setReadTimeout(10000 /* milliseconds */); + conn.setConnectTimeout(15000 /* milliseconds */); + conn.setRequestMethod("GET"); + conn.setDoInput(true); + // Start the query + conn.connect(); + InputStream stream = conn.getInputStream(); + return stream; + // END_INCLUDE(get_inputstream) + } + + /** Reads an InputStream and converts it to a String. + * @param stream InputStream containing HTML from targeted site. + * @param len Length of string that this method returns. + * @return String concatenated according to len parameter. + * @throws java.io.IOException + * @throws java.io.UnsupportedEncodingException + */ + private String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException { + Reader reader = null; + reader = new InputStreamReader(stream, "UTF-8"); + char[] buffer = new char[len]; + reader.read(buffer); + return new String(buffer); + } + + /** Create a chain of targets that will receive log data */ + public void initializeLogging() { + + // Using Log, front-end to the logging chain, emulates + // android.util.log method signatures. + + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + // A filter that strips out everything except the message text. + MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); + logWrapper.setNext(msgFilter); + + // On screen logging via a fragment with a TextView. + mLogFragment = + (LogFragment) getSupportFragmentManager().findFragmentById(R.id.log_fragment); + msgFilter.setNext(mLogFragment.getLogView()); + } +} diff --git a/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/SimpleTextFragment.java b/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/SimpleTextFragment.java new file mode 100644 index 000000000..320293760 --- /dev/null +++ b/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/SimpleTextFragment.java @@ -0,0 +1,98 @@ +/* + * 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.networkconnect; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +/** + * Simple fragment containing only a TextView. Used by TextPagerAdapter to create + * tutorial-style pages for apps. + */ +public class SimpleTextFragment extends Fragment { + + // Contains the text that will be displayed by this Fragment + String mText; + + // Contains a resource ID for the text that will be displayed by this fragment. + int mTextId = -1; + + // Keys which will be used to store/retrieve text passed in via setArguments. + public static final String TEXT_KEY = "text"; + public static final String TEXT_ID_KEY = "text_id"; + + // For situations where the app wants to modify text at Runtime, exposing the TextView. + private TextView mTextView; + + public SimpleTextFragment() { + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Before initializing the textView, check if any arguments were provided via setArguments. + processArguments(); + + // Create a new TextView and set its text to whatever was provided. + mTextView = new TextView(getActivity()); + mTextView.setGravity(Gravity.CENTER); + + if (mText != null) { + mTextView.setText(mText); + Log.i("SimpleTextFragment", mText); + } + return mTextView; + } + + public TextView getTextView() { + return mTextView; + } + + /** + * Changes the text for this TextView, according to the resource ID provided. + * @param stringId A resource ID representing the text content for this Fragment's TextView. + */ + public void setText(int stringId) { + getTextView().setText(getActivity().getString(stringId)); + } + + /** + * Processes the arguments passed into this Fragment via setArguments method. + * Currently the method only looks for text or a textID, nothing else. + */ + public void processArguments() { + // For most objects we'd handle the multiple possibilities for initialization variables + // as multiple constructors. For Fragments, however, it's customary to use + // setArguments / getArguments. + if (getArguments() != null) { + Bundle args = getArguments(); + if (args.containsKey(TEXT_KEY)) { + mText = args.getString(TEXT_KEY); + Log.d("Constructor", "Added Text."); + } else if (args.containsKey(TEXT_ID_KEY)) { + mTextId = args.getInt(TEXT_ID_KEY); + mText = getString(mTextId); + } + } + } +}
\ No newline at end of file diff --git a/samples/browseable/ShareActionProvider/AndroidManifest.xml b/samples/browseable/ShareActionProvider/AndroidManifest.xml new file mode 100644 index 000000000..be1ed4961 --- /dev/null +++ b/samples/browseable/ShareActionProvider/AndroidManifest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.actionbarcompat.shareactionprovider" + android:versionCode="1" + android:versionName="1.0"> + + <!-- + ActionBarCompat provides an Action Bar from API v7 onwards + --> + <uses-sdk + android:minSdkVersion="7" + android:targetSdkVersion="17" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/Theme.AppCompat" + android:allowBackup="true"> + + <activity + android:name=".MainActivity"> + <!-- Launcher Intent filter --> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <!-- ContentProvider which serves files from this application's asset folder --> + <provider + android:name=".content.AssetProvider" + android:authorities="com.example.android.actionbarcompat.shareactionprovider" + android:grantUriPermissions="true" + android:exported="true" /> + + </application> + +</manifest>
\ No newline at end of file diff --git a/samples/browseable/ShareActionProvider/_index.jd b/samples/browseable/ShareActionProvider/_index.jd new file mode 100644 index 000000000..a503585a0 --- /dev/null +++ b/samples/browseable/ShareActionProvider/_index.jd @@ -0,0 +1,13 @@ + + + +page.tags="ShareActionProvider" +sample.group=UI +@jd:body + +<p> + + This sample shows you how a provide a context-sensitive ShareActionProvider with + ActionBarCompat, backwards compatible to API v7. + + </p> diff --git a/samples/browseable/ShareActionProvider/res/drawable-hdpi/ic_launcher.png b/samples/browseable/ShareActionProvider/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..48db73f18 --- /dev/null +++ b/samples/browseable/ShareActionProvider/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/ShareActionProvider/res/drawable-hdpi/tile.9.png b/samples/browseable/ShareActionProvider/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/ShareActionProvider/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/ShareActionProvider/res/drawable-mdpi/ic_launcher.png b/samples/browseable/ShareActionProvider/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..674b1eee3 --- /dev/null +++ b/samples/browseable/ShareActionProvider/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/ShareActionProvider/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/ShareActionProvider/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..e76105d9f --- /dev/null +++ b/samples/browseable/ShareActionProvider/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/ShareActionProvider/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/ShareActionProvider/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..67605d802 --- /dev/null +++ b/samples/browseable/ShareActionProvider/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/ShareActionProvider/res/layout/activity_main.xml b/samples/browseable/ShareActionProvider/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/ShareActionProvider/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/ShareActionProvider/res/layout/item_image.xml b/samples/browseable/ShareActionProvider/res/layout/item_image.xml new file mode 100644 index 000000000..f7940e75e --- /dev/null +++ b/samples/browseable/ShareActionProvider/res/layout/item_image.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> +<ImageView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:scaleType="fitCenter" /> diff --git a/samples/browseable/ShareActionProvider/res/layout/item_text.xml b/samples/browseable/ShareActionProvider/res/layout/item_text.xml new file mode 100644 index 000000000..00c6a387d --- /dev/null +++ b/samples/browseable/ShareActionProvider/res/layout/item_text.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="16dp" + android:textAppearance="?android:textAppearanceLarge" + android:lineSpacingMultiplier="1.1" + android:gravity="center"/> diff --git a/samples/browseable/ShareActionProvider/res/layout/sample_main.xml b/samples/browseable/ShareActionProvider/res/layout/sample_main.xml new file mode 100644 index 000000000..902e8ab27 --- /dev/null +++ b/samples/browseable/ShareActionProvider/res/layout/sample_main.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <android.support.v4.view.ViewPager + android:id="@+id/viewpager" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/intro_message" + android:padding="16dp" + android:textAppearance="?android:textAppearanceMedium" + android:lineSpacingMultiplier="1.1" + android:background="#fb3"/> + +</LinearLayout>
\ No newline at end of file diff --git a/samples/browseable/ShareActionProvider/res/menu/main_menu.xml b/samples/browseable/ShareActionProvider/res/menu/main_menu.xml new file mode 100644 index 000000000..acd213417 --- /dev/null +++ b/samples/browseable/ShareActionProvider/res/menu/main_menu.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> + +<!-- + As we're using ActionBarCompat, any action item attributes come from ActionBarCompat's XML + namespace instead of the android namespace. Here we've added a new support namespace added to + the menu element allowing us to use the 'showAsAction' attribute in a backwards compatible way. + Any other action item attributes used should be referenced from this namespace too + (actionProviderClass, actionViewClass, actionLayout). +--> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:support="http://schemas.android.com/apk/res-auto"> + + <!-- + To use ShareActionProvider provided by ActionBarCompat, we reference the class by set the + support:actionProviderClass attribute with the full class name of ShareActionProvider. + --> + <item + android:id="@+id/menu_share" + android:title="@string/menu_share" + support:actionProviderClass="android.support.v7.widget.ShareActionProvider" + support:showAsAction="always" /> + +</menu>
\ No newline at end of file diff --git a/samples/browseable/ShareActionProvider/res/values-sw600dp/dimens.xml b/samples/browseable/ShareActionProvider/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/ShareActionProvider/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/ShareActionProvider/res/values-sw600dp/styles.xml b/samples/browseable/ShareActionProvider/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/ShareActionProvider/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/ShareActionProvider/res/values/base-strings.xml b/samples/browseable/ShareActionProvider/res/values/base-strings.xml new file mode 100644 index 000000000..4ca955889 --- /dev/null +++ b/samples/browseable/ShareActionProvider/res/values/base-strings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">ShareActionProvider</string> + <string name="intro_message"> + <![CDATA[ + + + This sample shows you how a provide a context-sensitive ShareActionProvider with + ActionBarCompat, backwards compatible to API v7. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/ShareActionProvider/res/values/dimens.xml b/samples/browseable/ShareActionProvider/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/ShareActionProvider/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/ShareActionProvider/res/values/strings.xml b/samples/browseable/ShareActionProvider/res/values/strings.xml new file mode 100644 index 000000000..298596f04 --- /dev/null +++ b/samples/browseable/ShareActionProvider/res/values/strings.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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="menu_share">Share</string> + <string name="quote_1">Expectation is the root of all heartache - William Shakespeare</string> + <string name="quote_2">The true sign of intelligence is not knowledge but imagination - Albert + Einstein</string> + <string name="quote_3">As for me, all I know is that I know nothing - Socrates</string> +</resources>
\ No newline at end of file diff --git a/samples/browseable/ShareActionProvider/res/values/styles.xml b/samples/browseable/ShareActionProvider/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/ShareActionProvider/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/MainActivity.java b/samples/browseable/ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/MainActivity.java new file mode 100644 index 000000000..b8cc900b5 --- /dev/null +++ b/samples/browseable/ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/MainActivity.java @@ -0,0 +1,202 @@ +/* + * 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.actionbarcompat.shareactionprovider; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.view.MenuItemCompat; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.ShareActionProvider; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.example.android.actionbarcompat.shareactionprovider.content.ContentItem; + +import java.util.ArrayList; + +/** + * This sample shows you how a provide a {@link ShareActionProvider} with ActionBarCompat, + * backwards compatible to API v7. + * <p> + * The sample contains a {@link ViewPager} which displays content of differing types: image and + * text. When a new item is selected in the ViewPager, the ShareActionProvider is updated with + * a share intent specific to that content. + * <p> + * This Activity extends from {@link ActionBarActivity}, which provides all of the function + * necessary to display a compatible Action Bar on devices running Android v2.1+. + */ +public class MainActivity extends ActionBarActivity { + + // The items to be displayed in the ViewPager + private final ArrayList<ContentItem> mItems = getSampleContent(); + + // Keep reference to the ShareActionProvider from the menu + private ShareActionProvider mShareActionProvider; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Set content view (which contains a CheeseListFragment) + setContentView(R.layout.sample_main); + + // Retrieve the ViewPager from the content view + ViewPager vp = (ViewPager) findViewById(R.id.viewpager); + + // Set an OnPageChangeListener so we are notified when a new item is selected + vp.setOnPageChangeListener(mOnPageChangeListener); + + // Finally set the adapter so the ViewPager can display items + vp.setAdapter(mPagerAdapter); + } + + // BEGIN_INCLUDE(get_sap) + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu resource + getMenuInflater().inflate(R.menu.main_menu, menu); + + // Retrieve the share menu item + MenuItem shareItem = menu.findItem(R.id.menu_share); + + // Now get the ShareActionProvider from the item + mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(shareItem); + + return super.onCreateOptionsMenu(menu); + } + // END_INCLUDE(get_sap) + + /** + * A PagerAdapter which instantiates views based on the ContentItem's content type. + */ + private final PagerAdapter mPagerAdapter = new PagerAdapter() { + LayoutInflater mInflater; + + @Override + public int getCount() { + return mItems.size(); + } + + @Override + public boolean isViewFromObject(View view, Object o) { + return view == o; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + // Just remove the view from the ViewPager + container.removeView((View) object); + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + // Ensure that the LayoutInflater is instantiated + if (mInflater == null) { + mInflater = LayoutInflater.from(MainActivity.this); + } + + // Get the item for the requested position + final ContentItem item = mItems.get(position); + + // The view we need to inflate changes based on the type of content + switch (item.contentType) { + case ContentItem.CONTENT_TYPE_TEXT: { + // Inflate item layout for text + TextView tv = (TextView) mInflater + .inflate(R.layout.item_text, container, false); + + // Set text content using it's resource id + tv.setText(item.contentResourceId); + + // Add the view to the ViewPager + container.addView(tv); + return tv; + } + case ContentItem.CONTENT_TYPE_IMAGE: { + // Inflate item layout for images + ImageView iv = (ImageView) mInflater + .inflate(R.layout.item_image, container, false); + + // Load the image from it's content URI + iv.setImageURI(item.getContentUri()); + + // Add the view to the ViewPager + container.addView(iv); + return iv; + } + } + + return null; + } + }; + + /** + * A OnPageChangeListener used to update the ShareActionProvider's share intent when a new item + * is selected in the ViewPager. + */ + private final ViewPager.OnPageChangeListener mOnPageChangeListener + = new ViewPager.OnPageChangeListener() { + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + // NO-OP + } + + @Override + public void onPageSelected(int position) { + // BEGIN_INCLUDE(update_sap) + if (mShareActionProvider != null) { + // Get the currently selected item, and retrieve it's share intent + ContentItem item = mItems.get(position); + Intent shareIntent = item.getShareIntent(MainActivity.this); + + // Now update the ShareActionProvider with the new share intent + mShareActionProvider.setShareIntent(shareIntent); + } + // END_INCLUDE(update_sap) + } + + @Override + public void onPageScrollStateChanged(int state) { + // NO-OP + } + }; + + /** + * @return An ArrayList of ContentItem's to be displayed in this sample + */ + static ArrayList<ContentItem> getSampleContent() { + ArrayList<ContentItem> items = new ArrayList<ContentItem>(); + + items.add(new ContentItem(ContentItem.CONTENT_TYPE_IMAGE, "photo_1.jpg")); + items.add(new ContentItem(ContentItem.CONTENT_TYPE_TEXT, R.string.quote_1)); + items.add(new ContentItem(ContentItem.CONTENT_TYPE_TEXT, R.string.quote_2)); + items.add(new ContentItem(ContentItem.CONTENT_TYPE_IMAGE, "photo_2.jpg")); + items.add(new ContentItem(ContentItem.CONTENT_TYPE_TEXT, R.string.quote_3)); + items.add(new ContentItem(ContentItem.CONTENT_TYPE_IMAGE, "photo_3.jpg")); + + return items; + } + +}
\ No newline at end of file diff --git a/samples/browseable/ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/AssetProvider.java b/samples/browseable/ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/AssetProvider.java new file mode 100644 index 000000000..b60f7d78b --- /dev/null +++ b/samples/browseable/ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/AssetProvider.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.actionbarcompat.shareactionprovider.content; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.database.Cursor; +import android.net.Uri; +import android.text.TextUtils; + +import java.io.FileNotFoundException; +import java.io.IOException; + +/** + * A simple ContentProvider which can serve files from this application's assets. The majority of + * functionality is in {@link #openAssetFile(android.net.Uri, String)}. + */ +public class AssetProvider extends ContentProvider { + + public static String CONTENT_URI = "com.example.android.actionbarcompat.shareactionprovider"; + + @Override + public boolean onCreate() { + return true; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + // Do not support delete requests. + return 0; + } + + @Override + public String getType(Uri uri) { + // Do not support returning the data type + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + // Do not support insert requests. + return null; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + // Do not support query requests. + return null; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + // Do not support update requests. + return 0; + } + + @Override + public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException { + // The asset file name should be the last path segment + final String assetName = uri.getLastPathSegment(); + + // If the given asset name is empty, throw an exception + if (TextUtils.isEmpty(assetName)) { + throw new FileNotFoundException(); + } + + try { + // Try and return a file descriptor for the given asset name + AssetManager am = getContext().getAssets(); + return am.openFd(assetName); + } catch (IOException e) { + e.printStackTrace(); + return super.openAssetFile(uri, mode); + } + } +} diff --git a/samples/browseable/ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/ContentItem.java b/samples/browseable/ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/ContentItem.java new file mode 100644 index 000000000..756a9e66d --- /dev/null +++ b/samples/browseable/ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/ContentItem.java @@ -0,0 +1,101 @@ +/* + * 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.actionbarcompat.shareactionprovider.content; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.text.TextUtils; + +/** + * This class encapsulates a content item. Referencing the content's type, and the differing way + * to reference the content (asset URI or resource id). + */ +public class ContentItem { + // Used to signify an image content type + public static final int CONTENT_TYPE_IMAGE = 0; + // Used to signify a text/string content type + public static final int CONTENT_TYPE_TEXT = 1; + + public final int contentType; + public final int contentResourceId; + public final String contentAssetFilePath; + + /** + * Creates a ContentItem with the specified type, referencing a resource id. + * + * @param type - One of {@link #CONTENT_TYPE_IMAGE} or {@link #CONTENT_TYPE_TEXT} + * @param resourceId - Resource ID to use for this item's content + */ + public ContentItem(int type, int resourceId) { + contentType = type; + contentResourceId = resourceId; + contentAssetFilePath = null; + } + + /** + * Creates a ContentItem with the specified type, referencing an asset file path. + * + * @param type - One of {@link #CONTENT_TYPE_IMAGE} or {@link #CONTENT_TYPE_TEXT} + * @param assetFilePath - File path from the application's asset for this item's content + */ + public ContentItem(int type, String assetFilePath) { + contentType = type; + contentAssetFilePath = assetFilePath; + contentResourceId = 0; + } + + /** + * @return Uri to the content + */ + public Uri getContentUri() { + if (!TextUtils.isEmpty(contentAssetFilePath)) { + // If this content has an asset, then return a AssetProvider Uri + return Uri.parse("content://" + AssetProvider.CONTENT_URI + "/" + contentAssetFilePath); + } else { + return null; + } + } + + /** + * Returns an {@link android.content.Intent} which can be used to share this item's content with other + * applications. + * + * @param context - Context to be used for fetching resources if needed + * @return Intent to be given to a ShareActionProvider. + */ + public Intent getShareIntent(Context context) { + Intent intent = new Intent(Intent.ACTION_SEND); + + switch (contentType) { + case CONTENT_TYPE_IMAGE: + intent.setType("image/jpg"); + // Bundle the asset content uri as the EXTRA_STREAM uri + intent.putExtra(Intent.EXTRA_STREAM, getContentUri()); + break; + + case CONTENT_TYPE_TEXT: + intent.setType("text/plain"); + // Get the string resource and bundle it as an intent extra + intent.putExtra(Intent.EXTRA_TEXT, context.getString(contentResourceId)); + break; + } + + return intent; + } + +} diff --git a/samples/browseable/StorageClient/AndroidManifest.xml b/samples/browseable/StorageClient/AndroidManifest.xml new file mode 100644 index 000000000..d35a4ec22 --- /dev/null +++ b/samples/browseable/StorageClient/AndroidManifest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.storageclient" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" /> + + <application android:allowBackup="true" + android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@style/AppTheme"> + + <activity android:name=".MainActivity" + android:label="@string/app_name" + android:uiOptions="splitActionBarWhenNarrow"> + <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/browseable/StorageClient/_index.jd b/samples/browseable/StorageClient/_index.jd new file mode 100644 index 000000000..2a7aaa086 --- /dev/null +++ b/samples/browseable/StorageClient/_index.jd @@ -0,0 +1,15 @@ + + + +page.tags="StorageClient" +sample.group=Content +@jd:body + +<p> + + Using the OPEN_DOCUMENT intent, a client app can access a list of Document Providers + on the device, and choose a file from any of them. + \n\nTo demonstrate this, click the button below to open up the Storage Access Framework + interface, and choose an image on your device. It will be displayed in this app. + + </p> diff --git a/samples/browseable/StorageClient/res/drawable-hdpi/ic_launcher.png b/samples/browseable/StorageClient/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..b1efaf4b2 --- /dev/null +++ b/samples/browseable/StorageClient/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/StorageClient/res/drawable-hdpi/tile.9.png b/samples/browseable/StorageClient/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/StorageClient/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/StorageClient/res/drawable-mdpi/ic_launcher.png b/samples/browseable/StorageClient/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f5f9244f2 --- /dev/null +++ b/samples/browseable/StorageClient/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/StorageClient/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/StorageClient/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..5d07b3f06 --- /dev/null +++ b/samples/browseable/StorageClient/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/StorageClient/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/StorageClient/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6ef21e1f4 --- /dev/null +++ b/samples/browseable/StorageClient/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/StorageClient/res/layout/activity_main.xml b/samples/browseable/StorageClient/res/layout/activity_main.xml new file mode 100755 index 000000000..bc5a57591 --- /dev/null +++ b/samples/browseable/StorageClient/res/layout/activity_main.xml @@ -0,0 +1,38 @@ +<!-- + 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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:id="@+id/sample_main_layout"> + <TextView android:id="@+id/sample_output" + style="@style/Widget.SampleMessage" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:text="@string/intro_message" /> + <View + android:layout_width="fill_parent" + android:layout_height="1dp" + android:background="@android:color/darker_gray"/> + <fragment + android:name="com.example.android.common.logger.LogFragment" + android:id="@+id/log_fragment" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" /> +</LinearLayout> diff --git a/samples/browseable/StorageClient/res/menu/main.xml b/samples/browseable/StorageClient/res/menu/main.xml new file mode 100644 index 000000000..2c3515dd4 --- /dev/null +++ b/samples/browseable/StorageClient/res/menu/main.xml @@ -0,0 +1,21 @@ +<!-- + 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. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/sample_action" + android:showAsAction="ifRoom|withText" + android:title="@string/sample_action" /> +</menu> diff --git a/samples/browseable/StorageClient/res/values-sw600dp/dimens.xml b/samples/browseable/StorageClient/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/StorageClient/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/StorageClient/res/values-sw600dp/styles.xml b/samples/browseable/StorageClient/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/StorageClient/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/StorageClient/res/values/base-strings.xml b/samples/browseable/StorageClient/res/values/base-strings.xml new file mode 100644 index 000000000..9498a2dab --- /dev/null +++ b/samples/browseable/StorageClient/res/values/base-strings.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">StorageClient</string> + <string name="intro_message"> + <![CDATA[ + + + Using the OPEN_DOCUMENT intent, a client app can access a list of Document Providers + on the device, and choose a file from any of them. + \n\nTo demonstrate this, click the button below to open up the Storage Access Framework + interface, and choose an image on your device. It will be displayed in this app. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/StorageClient/res/values/dimens.xml b/samples/browseable/StorageClient/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/StorageClient/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/StorageClient/res/values/strings.xml b/samples/browseable/StorageClient/res/values/strings.xml new file mode 100644 index 000000000..303776fbf --- /dev/null +++ b/samples/browseable/StorageClient/res/values/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="sample_action">Show Me The Image</string> +</resources> diff --git a/samples/browseable/StorageClient/res/values/styles.xml b/samples/browseable/StorageClient/res/values/styles.xml new file mode 100644 index 000000000..d3f82ff64 --- /dev/null +++ b/samples/browseable/StorageClient/res/values/styles.xml @@ -0,0 +1,51 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="AppTheme" parent="Theme.Base" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + + + <style name="Widget.SampleOutput"> + <item name="android:padding">@dimen/margin_medium</item> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Log" parent="Widget.SampleOutput"> + <item name="android:typeface">monospace</item> + </style> + +</resources> diff --git a/samples/browseable/StorageClient/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/StorageClient/src/com.example.android.common/activities/SampleActivityBase.java new file mode 100644 index 000000000..3228927b7 --- /dev/null +++ b/samples/browseable/StorageClient/src/com.example.android.common/activities/SampleActivityBase.java @@ -0,0 +1,52 @@ +/* +* 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.common.activities; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogWrapper; + +/** + * Base launcher activity, to handle most of the common plumbing for samples. + */ +public class SampleActivityBase extends FragmentActivity { + + public static final String TAG = "SampleActivityBase"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + initializeLogging(); + } + + /** Set up targets to receive log data */ + public void initializeLogging() { + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/StorageClient/src/com.example.android.common/logger/Log.java b/samples/browseable/StorageClient/src/com.example.android.common/logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/StorageClient/src/com.example.android.common/logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/StorageClient/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/StorageClient/src/com.example.android.common/logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/StorageClient/src/com.example.android.common/logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/StorageClient/src/com.example.android.common/logger/LogNode.java b/samples/browseable/StorageClient/src/com.example.android.common/logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/StorageClient/src/com.example.android.common/logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/StorageClient/src/com.example.android.common/logger/LogView.java b/samples/browseable/StorageClient/src/com.example.android.common/logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/StorageClient/src/com.example.android.common/logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/StorageClient/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/StorageClient/src/com.example.android.common/logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/StorageClient/src/com.example.android.common/logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/StorageClient/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/StorageClient/src/com.example.android.common/logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/StorageClient/src/com.example.android.common/logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/StorageClient/src/com.example.android.storageclient/MainActivity.java b/samples/browseable/StorageClient/src/com.example.android.storageclient/MainActivity.java new file mode 100644 index 000000000..69c75eb9c --- /dev/null +++ b/samples/browseable/StorageClient/src/com.example.android.storageclient/MainActivity.java @@ -0,0 +1,80 @@ +/* +* 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.storageclient; + +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.view.Menu; + +import com.example.android.common.activities.SampleActivityBase; +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogFragment; +import com.example.android.common.logger.LogWrapper; +import com.example.android.common.logger.MessageOnlyLogFilter; + +/** + * A simple launcher activity containing a summary sample description + * and a few action bar buttons. + */ +public class MainActivity extends SampleActivityBase { + + public static final String TAG = "MainActivity"; + + public static final String FRAGTAG = "StorageClientFragment"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + if (getSupportFragmentManager().findFragmentByTag(FRAGTAG) == null ) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + StorageClientFragment fragment = new StorageClientFragment(); + transaction.add(fragment, FRAGTAG); + transaction.commit(); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + /** Create a chain of targets that will receive log data */ + @Override + public void initializeLogging() { + // Wraps Android's native log framework. + LogWrapper logWrapper = new LogWrapper(); + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + Log.setLogNode(logWrapper); + + // Filter strips out everything except the message text. + MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); + logWrapper.setNext(msgFilter); + + // On screen logging via a fragment with a TextView. + LogFragment logFragment = (LogFragment) getSupportFragmentManager() + .findFragmentById(R.id.log_fragment); + msgFilter.setNext(logFragment.getLogView()); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/StorageClient/src/com.example.android.storageclient/StorageClientFragment.java b/samples/browseable/StorageClient/src/com.example.android.storageclient/StorageClientFragment.java new file mode 100644 index 000000000..7f9f73eeb --- /dev/null +++ b/samples/browseable/StorageClient/src/com.example.android.storageclient/StorageClientFragment.java @@ -0,0 +1,255 @@ +/* +* Copyright (C) 2012 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.storageclient; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.provider.OpenableColumns; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.view.MenuItem; +import android.view.Window; +import android.widget.ImageView; + +import com.example.android.common.logger.Log; + +import java.io.FileDescriptor; +import java.io.IOException; + +public class StorageClientFragment extends Fragment { + + // A request code's purpose is to match the result of a "startActivityForResult" with + // the type of the original request. Choose any value. + private static final int READ_REQUEST_CODE = 1337; + + public static final String TAG = "StorageClientFragment"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.sample_action) { + performFileSearch(); + } + return true; + } + + /** + * Fires an intent to spin up the "file chooser" UI and select an image. + */ + public void performFileSearch() { + + // BEGIN_INCLUDE (use_open_document_intent) + // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file browser. + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + + // Filter to only show results that can be "opened", such as a file (as opposed to a list + // of contacts or timezones) + intent.addCategory(Intent.CATEGORY_OPENABLE); + + // Filter to show only images, using the image MIME data type. + // If one wanted to search for ogg vorbis files, the type would be "audio/ogg". + // To search for all documents available via installed storage providers, it would be + // "*/*". + intent.setType("image/*"); + + startActivityForResult(intent, READ_REQUEST_CODE); + // END_INCLUDE (use_open_document_intent) + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent resultData) { + Log.i(TAG, "Received an \"Activity Result\""); + // BEGIN_INCLUDE (parse_open_document_response) + // The ACTION_OPEN_DOCUMENT intent was sent with the request code READ_REQUEST_CODE. + // If the request code seen here doesn't match, it's the response to some other intent, + // and the below code shouldn't run at all. + + if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) { + // The document selected by the user won't be returned in the intent. + // Instead, a URI to that document will be contained in the return intent + // provided to this method as a parameter. Pull that uri using "resultData.getData()" + Uri uri = null; + if (resultData != null) { + uri = resultData.getData(); + Log.i(TAG, "Uri: " + uri.toString()); + showImage(uri); + } + // END_INCLUDE (parse_open_document_response) + } + } + + /** + * Given the URI of an image, shows it on the screen using a DialogFragment. + * + * @param uri the Uri of the image to display. + */ + public void showImage(Uri uri) { + // BEGIN_INCLUDE (create_show_image_dialog) + if (uri != null) { + // Since the URI is to an image, create and show a DialogFragment to display the + // image to the user. + FragmentManager fm = getActivity().getSupportFragmentManager(); + ImageDialogFragment imageDialog = new ImageDialogFragment(uri); + imageDialog.show(fm, "image_dialog"); + } + // END_INCLUDE (create_show_image_dialog) + } + + /** + * Grabs metadata for a document specified by URI, logs it to the screen. + * + * @param uri The uri for the document whose metadata should be printed. + */ + public void dumpImageMetaData(Uri uri) { + // BEGIN_INCLUDE (dump_metadata) + + // The query, since it only applies to a single document, will only return one row. + // no need to filter, sort, or select fields, since we want all fields for one + // document. + Cursor cursor = getActivity().getContentResolver() + .query(uri, null, null, null, null, null); + + try { + // moveToFirst() returns false if the cursor has 0 rows. Very handy for + // "if there's anything to look at, look at it" conditionals. + if (cursor != null && cursor.moveToFirst()) { + + // Note it's called "Display Name". This is provider-specific, and + // might not necessarily be the file name. + String displayName = cursor.getString( + cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); + Log.i(TAG, "Display Name: " + displayName); + + int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE); + // If the size is unknown, the value stored is null. But since an int can't be + // null in java, the behavior is implementation-specific, which is just a fancy + // term for "unpredictable". So as a rule, check if it's null before assigning + // to an int. This will happen often: The storage API allows for remote + // files, whose size might not be locally known. + String size = null; + if (!cursor.isNull(sizeIndex)) { + // Technically the column stores an int, but cursor.getString will do the + // conversion automatically. + size = cursor.getString(sizeIndex); + } else { + size = "Unknown"; + } + Log.i(TAG, "Size: " + size); + } + } finally { + cursor.close(); + } + // END_INCLUDE (dump_metadata) + } + + /** + * DialogFragment which displays an image, given a URI. + */ + private class ImageDialogFragment extends DialogFragment { + private Dialog mDialog; + private Uri mUri; + + public ImageDialogFragment(Uri uri) { + super(); + mUri = uri; + } + + /** Create a Bitmap from the URI for that image and return it. + * + * @param uri the Uri for the image to return. + */ + private Bitmap getBitmapFromUri(Uri uri) { + ParcelFileDescriptor parcelFileDescriptor = null; + try { + parcelFileDescriptor = + getActivity().getContentResolver().openFileDescriptor(uri, "r"); + FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); + Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor); + parcelFileDescriptor.close(); + return image; + } catch (Exception e) { + Log.e(TAG, "Failed to load image.", e); + return null; + } finally { + try { + if (parcelFileDescriptor != null) { + parcelFileDescriptor.close(); + } + } catch (IOException e) { + e.printStackTrace(); + Log.e(TAG, "Error closing ParcelFile Descriptor"); + } + } + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + mDialog = super.onCreateDialog(savedInstanceState); + // To optimize for the "lightbox" style layout. Since we're not actually displaying a + // title, remove the bar along the top of the fragment where a dialog title would + // normally go. + mDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); + final ImageView imageView = new ImageView(getActivity()); + mDialog.setContentView(imageView); + + // BEGIN_INCLUDE (show_image) + // Loading the image is going to require some sort of I/O, which must occur off the UI + // thread. Changing the ImageView to display the image must occur ON the UI thread. + // The easiest way to divide up this labor is with an AsyncTask. The doInBackground + // method will run in a separate thread, but onPostExecute will run in the main + // UI thread. + AsyncTask<Uri, Void, Bitmap> imageLoadAsyncTask = new AsyncTask<Uri, Void, Bitmap>() { + @Override + protected Bitmap doInBackground(Uri... uris) { + dumpImageMetaData(uris[0]); + return getBitmapFromUri(uris[0]); + } + + @Override + protected void onPostExecute(Bitmap bitmap) { + imageView.setImageBitmap(bitmap); + } + }; + imageLoadAsyncTask.execute(mUri); + // END_INCLUDE (show_image) + + return mDialog; + } + + @Override + public void onStop() { + super.onStop(); + if (getDialog() != null) { + getDialog().dismiss(); + } + } + } +} diff --git a/samples/browseable/Styled/AndroidManifest.xml b/samples/browseable/Styled/AndroidManifest.xml new file mode 100644 index 000000000..06394c705 --- /dev/null +++ b/samples/browseable/Styled/AndroidManifest.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.actionbarcompat.styled" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk + android:minSdkVersion="7" + android:targetSdkVersion="17" /> + + <!-- + Theme is set on the application so that our custom theme is used by + default by all Activities + --> + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/Theme.Styled" > + + <activity android:name=".MainActivity"> + + <!-- Launcher Intent filter --> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + + <!-- + In this example set the Activity to have a split action bar when the device's + display is narrow. In ActionBarCompat this is done by setting the + 'android.support.UI_OPTIONS' metadata field to 'splitActionBarWhenNarrow'. + --> + <meta-data + android:name="android.support.UI_OPTIONS" + android:value="splitActionBarWhenNarrow" /> + + </activity> + </application> + +</manifest> diff --git a/samples/browseable/Styled/_index.jd b/samples/browseable/Styled/_index.jd new file mode 100644 index 000000000..58942bf7c --- /dev/null +++ b/samples/browseable/Styled/_index.jd @@ -0,0 +1,13 @@ + + + +page.tags="Styled" +sample.group=UI +@jd:body + +<p> + + This sample shows you how to use ActionBarCompat with a customized theme. It utilizes a + split action bar when running on a device with a narrow display, and show three tabs. + + </p> diff --git a/samples/browseable/Styled/res/drawable-hdpi/ab_bottom_solid_styled.9.png b/samples/browseable/Styled/res/drawable-hdpi/ab_bottom_solid_styled.9.png Binary files differnew file mode 100644 index 000000000..f1d56b04a --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/ab_bottom_solid_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/ab_solid_styled.9.png b/samples/browseable/Styled/res/drawable-hdpi/ab_solid_styled.9.png Binary files differnew file mode 100644 index 000000000..b9790a9a8 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/ab_solid_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/ab_stacked_solid_styled.9.png b/samples/browseable/Styled/res/drawable-hdpi/ab_stacked_solid_styled.9.png Binary files differnew file mode 100644 index 000000000..caa80ca14 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/ab_stacked_solid_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/ic_action_location.png b/samples/browseable/Styled/res/drawable-hdpi/ic_action_location.png Binary files differnew file mode 100644 index 000000000..9d75c316e --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/ic_action_location.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/ic_action_refresh.png b/samples/browseable/Styled/res/drawable-hdpi/ic_action_refresh.png Binary files differnew file mode 100644 index 000000000..021651472 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/ic_action_refresh.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/ic_action_settings.png b/samples/browseable/Styled/res/drawable-hdpi/ic_action_settings.png Binary files differnew file mode 100644 index 000000000..8e30d967a --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/ic_action_settings.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/ic_launcher.png b/samples/browseable/Styled/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..ba841fa3d --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/list_focused_styled.9.png b/samples/browseable/Styled/res/drawable-hdpi/list_focused_styled.9.png Binary files differnew file mode 100644 index 000000000..1189239dd --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/list_focused_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/menu_dropdown_panel_styled.9.png b/samples/browseable/Styled/res/drawable-hdpi/menu_dropdown_panel_styled.9.png Binary files differnew file mode 100644 index 000000000..a11485984 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/menu_dropdown_panel_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/progress_bg_styled.9.png b/samples/browseable/Styled/res/drawable-hdpi/progress_bg_styled.9.png Binary files differnew file mode 100644 index 000000000..3b183e07b --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/progress_bg_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/progress_primary_styled.9.png b/samples/browseable/Styled/res/drawable-hdpi/progress_primary_styled.9.png Binary files differnew file mode 100644 index 000000000..d9879bdcd --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/progress_primary_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/progress_secondary_styled.9.png b/samples/browseable/Styled/res/drawable-hdpi/progress_secondary_styled.9.png Binary files differnew file mode 100644 index 000000000..7a6ee50d6 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/progress_secondary_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/spinner_ab_default_styled.9.png b/samples/browseable/Styled/res/drawable-hdpi/spinner_ab_default_styled.9.png Binary files differnew file mode 100644 index 000000000..e518eb7d3 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/spinner_ab_default_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/spinner_ab_disabled_styled.9.png b/samples/browseable/Styled/res/drawable-hdpi/spinner_ab_disabled_styled.9.png Binary files differnew file mode 100644 index 000000000..b6febf96b --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/spinner_ab_disabled_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/spinner_ab_focused_styled.9.png b/samples/browseable/Styled/res/drawable-hdpi/spinner_ab_focused_styled.9.png Binary files differnew file mode 100644 index 000000000..c631c2f11 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/spinner_ab_focused_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/spinner_ab_pressed_styled.9.png b/samples/browseable/Styled/res/drawable-hdpi/spinner_ab_pressed_styled.9.png Binary files differnew file mode 100644 index 000000000..8e71d1c67 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/spinner_ab_pressed_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/tab_selected_focused_styled.9.png b/samples/browseable/Styled/res/drawable-hdpi/tab_selected_focused_styled.9.png Binary files differnew file mode 100644 index 000000000..f4d6f2f60 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/tab_selected_focused_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/tab_selected_pressed_styled.9.png b/samples/browseable/Styled/res/drawable-hdpi/tab_selected_pressed_styled.9.png Binary files differnew file mode 100644 index 000000000..2aa7838c0 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/tab_selected_pressed_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/tab_selected_styled.9.png b/samples/browseable/Styled/res/drawable-hdpi/tab_selected_styled.9.png Binary files differnew file mode 100644 index 000000000..e2b390a59 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/tab_selected_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/tab_unselected_focused_styled.9.png b/samples/browseable/Styled/res/drawable-hdpi/tab_unselected_focused_styled.9.png Binary files differnew file mode 100644 index 000000000..5b8b92845 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/tab_unselected_focused_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/tab_unselected_pressed_styled.9.png b/samples/browseable/Styled/res/drawable-hdpi/tab_unselected_pressed_styled.9.png Binary files differnew file mode 100644 index 000000000..18d205355 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/tab_unselected_pressed_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-hdpi/tile.9.png b/samples/browseable/Styled/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/ab_bottom_solid_styled.9.png b/samples/browseable/Styled/res/drawable-mdpi/ab_bottom_solid_styled.9.png Binary files differnew file mode 100644 index 000000000..79da2b0be --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/ab_bottom_solid_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/ab_solid_styled.9.png b/samples/browseable/Styled/res/drawable-mdpi/ab_solid_styled.9.png Binary files differnew file mode 100644 index 000000000..617c08b4f --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/ab_solid_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/ab_stacked_solid_styled.9.png b/samples/browseable/Styled/res/drawable-mdpi/ab_stacked_solid_styled.9.png Binary files differnew file mode 100644 index 000000000..407382a3e --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/ab_stacked_solid_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/ic_action_location.png b/samples/browseable/Styled/res/drawable-mdpi/ic_action_location.png Binary files differnew file mode 100644 index 000000000..b637f527d --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/ic_action_location.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/ic_action_refresh.png b/samples/browseable/Styled/res/drawable-mdpi/ic_action_refresh.png Binary files differnew file mode 100644 index 000000000..206314bf9 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/ic_action_refresh.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/ic_action_settings.png b/samples/browseable/Styled/res/drawable-mdpi/ic_action_settings.png Binary files differnew file mode 100644 index 000000000..0e65c6824 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/ic_action_settings.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/ic_launcher.png b/samples/browseable/Styled/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..2901fa669 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/list_focused_styled.9.png b/samples/browseable/Styled/res/drawable-mdpi/list_focused_styled.9.png Binary files differnew file mode 100644 index 000000000..30095e64c --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/list_focused_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/menu_dropdown_panel_styled.9.png b/samples/browseable/Styled/res/drawable-mdpi/menu_dropdown_panel_styled.9.png Binary files differnew file mode 100644 index 000000000..ea341b50a --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/menu_dropdown_panel_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/progress_bg_styled.9.png b/samples/browseable/Styled/res/drawable-mdpi/progress_bg_styled.9.png Binary files differnew file mode 100644 index 000000000..71753a4b7 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/progress_bg_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/progress_primary_styled.9.png b/samples/browseable/Styled/res/drawable-mdpi/progress_primary_styled.9.png Binary files differnew file mode 100644 index 000000000..375aff252 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/progress_primary_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/progress_secondary_styled.9.png b/samples/browseable/Styled/res/drawable-mdpi/progress_secondary_styled.9.png Binary files differnew file mode 100644 index 000000000..d1dbb3b0c --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/progress_secondary_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/spinner_ab_default_styled.9.png b/samples/browseable/Styled/res/drawable-mdpi/spinner_ab_default_styled.9.png Binary files differnew file mode 100644 index 000000000..5e1dd470f --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/spinner_ab_default_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/spinner_ab_disabled_styled.9.png b/samples/browseable/Styled/res/drawable-mdpi/spinner_ab_disabled_styled.9.png Binary files differnew file mode 100644 index 000000000..38025ad3d --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/spinner_ab_disabled_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/spinner_ab_focused_styled.9.png b/samples/browseable/Styled/res/drawable-mdpi/spinner_ab_focused_styled.9.png Binary files differnew file mode 100644 index 000000000..37b457607 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/spinner_ab_focused_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/spinner_ab_pressed_styled.9.png b/samples/browseable/Styled/res/drawable-mdpi/spinner_ab_pressed_styled.9.png Binary files differnew file mode 100644 index 000000000..8b9946360 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/spinner_ab_pressed_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/tab_selected_focused_styled.9.png b/samples/browseable/Styled/res/drawable-mdpi/tab_selected_focused_styled.9.png Binary files differnew file mode 100644 index 000000000..83daafb0c --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/tab_selected_focused_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/tab_selected_pressed_styled.9.png b/samples/browseable/Styled/res/drawable-mdpi/tab_selected_pressed_styled.9.png Binary files differnew file mode 100644 index 000000000..d50ffaf8d --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/tab_selected_pressed_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/tab_selected_styled.9.png b/samples/browseable/Styled/res/drawable-mdpi/tab_selected_styled.9.png Binary files differnew file mode 100644 index 000000000..6fdd7f41b --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/tab_selected_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/tab_unselected_focused_styled.9.png b/samples/browseable/Styled/res/drawable-mdpi/tab_unselected_focused_styled.9.png Binary files differnew file mode 100644 index 000000000..dc77e6df3 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/tab_unselected_focused_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-mdpi/tab_unselected_pressed_styled.9.png b/samples/browseable/Styled/res/drawable-mdpi/tab_unselected_pressed_styled.9.png Binary files differnew file mode 100644 index 000000000..637d22d27 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-mdpi/tab_unselected_pressed_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/ab_bottom_solid_styled.9.png b/samples/browseable/Styled/res/drawable-xhdpi/ab_bottom_solid_styled.9.png Binary files differnew file mode 100644 index 000000000..64f17a830 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/ab_bottom_solid_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/ab_solid_styled.9.png b/samples/browseable/Styled/res/drawable-xhdpi/ab_solid_styled.9.png Binary files differnew file mode 100644 index 000000000..c557360c6 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/ab_solid_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/ab_stacked_solid_styled.9.png b/samples/browseable/Styled/res/drawable-xhdpi/ab_stacked_solid_styled.9.png Binary files differnew file mode 100644 index 000000000..0ef2ec000 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/ab_stacked_solid_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/ic_action_location.png b/samples/browseable/Styled/res/drawable-xhdpi/ic_action_location.png Binary files differnew file mode 100644 index 000000000..e9bf9f367 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/ic_action_location.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/ic_action_refresh.png b/samples/browseable/Styled/res/drawable-xhdpi/ic_action_refresh.png Binary files differnew file mode 100644 index 000000000..ccd4b0792 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/ic_action_refresh.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/ic_action_settings.png b/samples/browseable/Styled/res/drawable-xhdpi/ic_action_settings.png Binary files differnew file mode 100644 index 000000000..d0a733e52 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/ic_action_settings.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/Styled/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..866f1460b --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/list_focused_styled.9.png b/samples/browseable/Styled/res/drawable-xhdpi/list_focused_styled.9.png Binary files differnew file mode 100644 index 000000000..c02fe1358 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/list_focused_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/menu_dropdown_panel_styled.9.png b/samples/browseable/Styled/res/drawable-xhdpi/menu_dropdown_panel_styled.9.png Binary files differnew file mode 100644 index 000000000..3d9f61496 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/menu_dropdown_panel_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/progress_bg_styled.9.png b/samples/browseable/Styled/res/drawable-xhdpi/progress_bg_styled.9.png Binary files differnew file mode 100644 index 000000000..5ffc2accd --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/progress_bg_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/progress_primary_styled.9.png b/samples/browseable/Styled/res/drawable-xhdpi/progress_primary_styled.9.png Binary files differnew file mode 100644 index 000000000..8f6636180 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/progress_primary_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/progress_secondary_styled.9.png b/samples/browseable/Styled/res/drawable-xhdpi/progress_secondary_styled.9.png Binary files differnew file mode 100644 index 000000000..f28f10b42 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/progress_secondary_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_default_styled.9.png b/samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_default_styled.9.png Binary files differnew file mode 100644 index 000000000..f738a44c4 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_default_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_disabled_styled.9.png b/samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_disabled_styled.9.png Binary files differnew file mode 100644 index 000000000..79d24c9f2 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_disabled_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_focused_styled.9.png b/samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_focused_styled.9.png Binary files differnew file mode 100644 index 000000000..8be8d7100 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_focused_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_pressed_styled.9.png b/samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_pressed_styled.9.png Binary files differnew file mode 100644 index 000000000..774602c40 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/spinner_ab_pressed_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/tab_selected_focused_styled.9.png b/samples/browseable/Styled/res/drawable-xhdpi/tab_selected_focused_styled.9.png Binary files differnew file mode 100644 index 000000000..c174424a0 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/tab_selected_focused_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/tab_selected_pressed_styled.9.png b/samples/browseable/Styled/res/drawable-xhdpi/tab_selected_pressed_styled.9.png Binary files differnew file mode 100644 index 000000000..62cbd048a --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/tab_selected_pressed_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/tab_selected_styled.9.png b/samples/browseable/Styled/res/drawable-xhdpi/tab_selected_styled.9.png Binary files differnew file mode 100644 index 000000000..5009ce04d --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/tab_selected_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/tab_unselected_focused_styled.9.png b/samples/browseable/Styled/res/drawable-xhdpi/tab_unselected_focused_styled.9.png Binary files differnew file mode 100644 index 000000000..2c2a56755 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/tab_unselected_focused_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xhdpi/tab_unselected_pressed_styled.9.png b/samples/browseable/Styled/res/drawable-xhdpi/tab_unselected_pressed_styled.9.png Binary files differnew file mode 100644 index 000000000..81eba4c67 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xhdpi/tab_unselected_pressed_styled.9.png diff --git a/samples/browseable/Styled/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/Styled/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..cb301f236 --- /dev/null +++ b/samples/browseable/Styled/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/Styled/res/drawable/pressed_background.xml b/samples/browseable/Styled/res/drawable/pressed_background.xml new file mode 100644 index 000000000..9de1ff713 --- /dev/null +++ b/samples/browseable/Styled/res/drawable/pressed_background.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> + +<!-- + This drawable is used in our custom selected item background drawable: selectable_background.xml. + It is required as selector items need to be drawables, and not a raw color value as we are using. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + + <solid android:color="@color/pressed_styled"/> + +</shape>
\ No newline at end of file diff --git a/samples/browseable/Styled/res/drawable/progress_horizontal.xml b/samples/browseable/Styled/res/drawable/progress_horizontal.xml new file mode 100644 index 000000000..bef8c578d --- /dev/null +++ b/samples/browseable/Styled/res/drawable/progress_horizontal.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> + +<!-- + This drawable is used in our custom horizontal Progress Bar style: + Widget.Styled.ProgressBar.Horizontal +--> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + + <item + android:id="@android:id/background" + android:drawable="@drawable/progress_bg_styled" /> + + <item android:id="@android:id/secondaryProgress"> + <scale + android:drawable="@drawable/progress_secondary_styled" + android:scaleWidth="100%" /> + </item> + + <item android:id="@android:id/progress"> + <scale + android:drawable="@drawable/progress_primary_styled" + android:scaleWidth="100%" /> + </item> + +</layer-list>
\ No newline at end of file diff --git a/samples/browseable/Styled/res/drawable/selectable_background.xml b/samples/browseable/Styled/res/drawable/selectable_background.xml new file mode 100644 index 000000000..776dbb7a4 --- /dev/null +++ b/samples/browseable/Styled/res/drawable/selectable_background.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> + +<!-- + This drawable is used as the main touch feedback drawable for the Action Bar. By default it is + used as the action item button background, amongst other things. + + The different items in this drawable are displayed when their selector state matches the view's + state. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + + <item android:drawable="@drawable/list_focused_styled" android:state_focused="true" + android:state_pressed="false"/> + <item android:drawable="@drawable/pressed_background" android:state_pressed="true"/> + <item android:drawable="@android:color/transparent"/> + +</selector>
\ No newline at end of file diff --git a/samples/browseable/Styled/res/drawable/spinner_background_ab.xml b/samples/browseable/Styled/res/drawable/spinner_background_ab.xml new file mode 100644 index 000000000..a12db6e34 --- /dev/null +++ b/samples/browseable/Styled/res/drawable/spinner_background_ab.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> + +<!-- + This drawable is used to style the list navigation spinner in our custom Action Bar theme. + + The different items in this drawable are displayed when their selector state matches the view's + state. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + + <item android:drawable="@drawable/spinner_ab_disabled_styled" android:state_enabled="false" /> + <item android:drawable="@drawable/spinner_ab_pressed_styled" android:state_pressed="true" /> + <item android:drawable="@drawable/spinner_ab_focused_styled" android:state_focused="true" + android:state_pressed="false" /> + <item android:drawable="@drawable/spinner_ab_default_styled" /> + +</selector>
\ No newline at end of file diff --git a/samples/browseable/Styled/res/drawable/tab_indicator_ab.xml b/samples/browseable/Styled/res/drawable/tab_indicator_ab.xml new file mode 100644 index 000000000..baa649232 --- /dev/null +++ b/samples/browseable/Styled/res/drawable/tab_indicator_ab.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> + +<!-- + This drawable is used as the background drawable for each tab displayed on the Action Bar. + + The different items in this drawable are displayed when their selector state matches the view's + state. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + + <!-- Non focused states --> + <item android:drawable="@android:color/transparent" android:state_focused="false" + android:state_pressed="false" android:state_selected="false" /> + <item android:drawable="@drawable/tab_selected_styled" android:state_focused="false" + android:state_pressed="false" android:state_selected="true" /> + + <!-- Focused states --> + <item android:drawable="@drawable/tab_unselected_focused_styled" android:state_focused="true" + android:state_pressed="false" android:state_selected="false" /> + <item android:drawable="@drawable/tab_selected_focused_styled" android:state_focused="true" + android:state_pressed="false" android:state_selected="true" /> + + <!-- Pressed & Non-focused --> + <item android:drawable="@drawable/tab_unselected_pressed_styled" android:state_focused="false" + android:state_pressed="true" android:state_selected="false" /> + <item android:drawable="@drawable/tab_selected_pressed_styled" android:state_focused="false" + android:state_pressed="true" android:state_selected="true" /> + + <!-- Pressed & focused states --> + <item android:drawable="@drawable/tab_unselected_pressed_styled" android:state_focused="true" + android:state_pressed="true" android:state_selected="false" /> + <item android:drawable="@drawable/tab_selected_pressed_styled" android:state_focused="true" + android:state_pressed="true" android:state_selected="true" /> + +</selector>
\ No newline at end of file diff --git a/samples/browseable/Styled/res/layout/activity_main.xml b/samples/browseable/Styled/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/Styled/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/Styled/res/layout/sample_main.xml b/samples/browseable/Styled/res/layout/sample_main.xml new file mode 100644 index 000000000..a162d3f19 --- /dev/null +++ b/samples/browseable/Styled/res/layout/sample_main.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:text="@string/main_description" + android:padding="16dp" + android:gravity="center"/> + diff --git a/samples/browseable/Styled/res/menu/main.xml b/samples/browseable/Styled/res/menu/main.xml new file mode 100644 index 000000000..778a443b3 --- /dev/null +++ b/samples/browseable/Styled/res/menu/main.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> + +<!-- + As we're using ActionBarCompat, any action item attributes come from ActionBarCompat's XML + namespace instead of the android namespace. Here we've added a new support namespace added to + the menu element allowing us to use the 'showAsAction' attribute in a backwards compatible way. + Any other action item attributes used should be referenced from this namespace too + (actionProviderClass, actionViewClass, actionLayout). +--> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:support="http://schemas.android.com/apk/res-auto" > + + <!-- + Here we create all of the items to be displayed in the menu, setting support:showAsAction to + define how the item should be displayed on the compatible Action Bar. + --> + <item + android:id="@+id/menu_refresh" + android:icon="@drawable/ic_action_refresh" + android:title="@string/menu_refresh" + support:showAsAction="ifRoom"/> + + <item + android:id="@+id/menu_location" + android:icon="@drawable/ic_action_location" + android:title="@string/menu_location" + support:showAsAction="ifRoom"/> + + <item + android:id="@+id/menu_settings" + android:icon="@drawable/ic_action_settings" + android:title="@string/menu_settings" + support:showAsAction="never"/> + +</menu>
\ No newline at end of file diff --git a/samples/browseable/Styled/res/values-sw600dp/dimens.xml b/samples/browseable/Styled/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/Styled/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/Styled/res/values-sw600dp/styles.xml b/samples/browseable/Styled/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/Styled/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/Styled/res/values-v14/styles.xml b/samples/browseable/Styled/res/values-v14/styles.xml new file mode 100644 index 000000000..4bfec4841 --- /dev/null +++ b/samples/browseable/Styled/res/values-v14/styles.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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> + + <!-- + This is the styled theme. + + It extends from Theme.AppCompat.Light, but it could extend from any of + the Theme.AppCompat themes depending on your color scheme. This theme can be applied to + your application or individual activities in the AndroidManifest.xml. In this sample it is + set on the application. + + This differs from the version of this theme in 'res/values', as we revert back to + setting the attributes from the android namespace in ICS+. + --> + + <style name="Theme.Styled" parent="@style/Theme.AppCompat.Light"> + <item name="android:actionBarItemBackground">@drawable/selectable_background</item> + <item name="android:actionBarTabStyle">@style/Widget.Styled.ActionBar.TabView</item> + <item name="android:actionBarStyle">@style/Widget.Styled.ActionBar</item> + <item name="android:actionDropDownStyle"> + @style/Widget.Styled.Spinner.DropDown.ActionBar + </item> + <item name="android:dropDownListViewStyle">@style/Widget.Styled.ListView.DropDown</item> + <item name="android:popupMenuStyle">@style/Widget.Styled.PopupMenu</item> + </style> + + <style name="Widget.Styled.ActionBar" parent="@style/Widget.AppCompat.Light.ActionBar.Solid"> + <item name="android:background">@drawable/ab_solid_styled</item> + <item name="android:backgroundStacked">@drawable/ab_stacked_solid_styled</item> + <item name="android:backgroundSplit">@drawable/ab_bottom_solid_styled</item> + <item name="android:progressBarStyle">@style/Widget.Styled.ProgressBar.Horizontal</item> + </style> + +</resources>
\ No newline at end of file diff --git a/samples/browseable/Styled/res/values/base-strings.xml b/samples/browseable/Styled/res/values/base-strings.xml new file mode 100644 index 000000000..985b43391 --- /dev/null +++ b/samples/browseable/Styled/res/values/base-strings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">Styled</string> + <string name="intro_message"> + <![CDATA[ + + + This sample shows you how to use ActionBarCompat with a customized theme. It utilizes a + split action bar when running on a device with a narrow display, and show three tabs. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/Styled/res/values/colors.xml b/samples/browseable/Styled/res/values/colors.xml new file mode 100644 index 000000000..e111f592a --- /dev/null +++ b/samples/browseable/Styled/res/values/colors.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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="pressed_styled">#CC669900</color> + +</resources>
\ No newline at end of file diff --git a/samples/browseable/Styled/res/values/dimens.xml b/samples/browseable/Styled/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/Styled/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/Styled/res/values/strings.xml b/samples/browseable/Styled/res/values/strings.xml new file mode 100644 index 000000000..de698d4ba --- /dev/null +++ b/samples/browseable/Styled/res/values/strings.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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="menu_refresh">Refresh</string> + <string name="menu_location">Location</string> + <string name="menu_settings">Settings</string> + <string name="main_description">This is a basic Activity showing an Action Bar which has been + styled. + </string> + +</resources>
\ No newline at end of file diff --git a/samples/browseable/Styled/res/values/styles.xml b/samples/browseable/Styled/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/Styled/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/Styled/src/com.example.android.actionbarcompat.styled/MainActivity.java b/samples/browseable/Styled/src/com.example.android.actionbarcompat.styled/MainActivity.java new file mode 100644 index 000000000..19fe3a131 --- /dev/null +++ b/samples/browseable/Styled/src/com.example.android.actionbarcompat.styled/MainActivity.java @@ -0,0 +1,80 @@ +/* + * 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.actionbarcompat.styled; + +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarActivity; +import android.view.Menu; + +/** + * This sample shows you how to use ActionBarCompat with a customized theme. It utilizes a split + * action bar when running on a device with a narrow display, and show three tabs. + * + * This Activity extends from {@link ActionBarActivity}, which provides all of the function + * necessary to display a compatible Action Bar on devices running Android v2.1+. + * + * The interesting bits of this sample start in the theme files + * ('res/values/styles.xml' and 'res/values-v14</styles.xml'). + * + * Many of the drawables used in this sample were generated with the + * 'Android Action Bar Style Generator': http://jgilfelt.github.io/android-actionbarstylegenerator + */ +public class MainActivity extends ActionBarActivity implements ActionBar.TabListener { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.sample_main); + + // Set the Action Bar to use tabs for navigation + ActionBar ab = getSupportActionBar(); + ab.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + + // Add three tabs to the Action Bar for display + ab.addTab(ab.newTab().setText("Tab 1").setTabListener(this)); + ab.addTab(ab.newTab().setText("Tab 2").setTabListener(this)); + ab.addTab(ab.newTab().setText("Tab 3").setTabListener(this)); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate menu from menu resource (res/menu/main) + getMenuInflater().inflate(R.menu.main, menu); + + return super.onCreateOptionsMenu(menu); + } + + // Implemented from ActionBar.TabListener + @Override + public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { + // This is called when a tab is selected. + } + + // Implemented from ActionBar.TabListener + @Override + public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { + // This is called when a previously selected tab is unselected. + } + + // Implemented from ActionBar.TabListener + @Override + public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { + // This is called when a previously selected tab is selected again. + } +} diff --git a/samples/browseable/TextLinkify/AndroidManifest.xml b/samples/browseable/TextLinkify/AndroidManifest.xml new file mode 100644 index 000000000..49b4eae0b --- /dev/null +++ b/samples/browseable/TextLinkify/AndroidManifest.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.textlinkify" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk + android:minSdkVersion="4" + android:targetSdkVersion="17" /> + + <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" > + <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/browseable/TextLinkify/_index.jd b/samples/browseable/TextLinkify/_index.jd new file mode 100644 index 000000000..194c3f684 --- /dev/null +++ b/samples/browseable/TextLinkify/_index.jd @@ -0,0 +1,13 @@ + + + +page.tags="TextLinkify" +sample.group=Views +@jd:body + +<p> + + This sample illustrates how links can be added to a TextView. This can be done either + automatically by setting the "autoLink" property or explicitly. + + </p> diff --git a/samples/browseable/TextLinkify/res/drawable-hdpi/ic_launcher.png b/samples/browseable/TextLinkify/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..eae36bc19 --- /dev/null +++ b/samples/browseable/TextLinkify/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/TextLinkify/res/drawable-hdpi/tile.9.png b/samples/browseable/TextLinkify/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/TextLinkify/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/TextLinkify/res/drawable-mdpi/ic_launcher.png b/samples/browseable/TextLinkify/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f727f489b --- /dev/null +++ b/samples/browseable/TextLinkify/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/TextLinkify/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/TextLinkify/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..51199d308 --- /dev/null +++ b/samples/browseable/TextLinkify/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/TextLinkify/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/TextLinkify/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..c49ec6a74 --- /dev/null +++ b/samples/browseable/TextLinkify/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/TextLinkify/res/layout/activity_main.xml b/samples/browseable/TextLinkify/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/TextLinkify/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/TextLinkify/res/layout/sample_main.xml b/samples/browseable/TextLinkify/res/layout/sample_main.xml new file mode 100644 index 000000000..8f35a7058 --- /dev/null +++ b/samples/browseable/TextLinkify/res/layout/sample_main.xml @@ -0,0 +1,72 @@ +<!-- + 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. +--> + + <ScrollView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + tools:context=".MainActivity"> + + <LinearLayout + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin"> + + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/intro" /> + + <!-- text_auto_linkify automatically linkifies things like URLs and phone numbers. --> + <TextView + android:id="@+id/text_auto_linkify" + style="@style/LinkText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:autoLink="all" + android:text="@string/link_text_auto" /> + + <!-- + text_html_resource uses a string resource containing explicit anchor tags (<a>) + to specify links. + --> + <TextView + android:id="@+id/text_html_resource" + style="@style/LinkText" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + + <!-- text_html_program builds the text in the Java code using HTML. --> + <TextView + android:id="@+id/text_html_program" + style="@style/LinkText" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + <!-- text_spannable builds the text in the Java code without using HTML. --> + <TextView + android:id="@+id/text_spannable" + style="@style/LinkText" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </LinearLayout> + </ScrollView> diff --git a/samples/browseable/TextLinkify/res/values-sw600dp/dimens.xml b/samples/browseable/TextLinkify/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/TextLinkify/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/TextLinkify/res/values-sw600dp/styles.xml b/samples/browseable/TextLinkify/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/TextLinkify/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/TextLinkify/res/values-sw720dp-land/dimens.xml b/samples/browseable/TextLinkify/res/values-sw720dp-land/dimens.xml new file mode 100644 index 000000000..560bd44e5 --- /dev/null +++ b/samples/browseable/TextLinkify/res/values-sw720dp-land/dimens.xml @@ -0,0 +1,25 @@ +<!-- + 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> + + <!-- + Customize dimensions originally defined in res/values/dimens.xml (such as + screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here. + --> + <dimen name="activity_horizontal_margin">128dp</dimen> + +</resources> diff --git a/samples/browseable/TextLinkify/res/values/base-strings.xml b/samples/browseable/TextLinkify/res/values/base-strings.xml new file mode 100644 index 000000000..f2eb104c4 --- /dev/null +++ b/samples/browseable/TextLinkify/res/values/base-strings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">TextLinkify</string> + <string name="intro_message"> + <![CDATA[ + + + This sample illustrates how links can be added to a TextView. This can be done either + automatically by setting the "autoLink" property or explicitly. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/TextLinkify/res/values/dimens.xml b/samples/browseable/TextLinkify/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/TextLinkify/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/TextLinkify/res/values/strings.xml b/samples/browseable/TextLinkify/res/values/strings.xml new file mode 100644 index 000000000..c50774edf --- /dev/null +++ b/samples/browseable/TextLinkify/res/values/strings.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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="intro">This sample illustrates how links can be added to a TextView. + \nThis can be done either automatically by setting the <i>autoLink</i> property + or explicitly.</string> + <string name="link_text_auto"><b>text_auto_linkify: Various kinds + of data that will be auto-linked.</b> + In this text are some things that are actionable. For instance, + you can click on http://www.google.com and it will launch the + web browser. You can click on google.com too. If you + click on (415) 555-1212 it should dial the phone. Or just write + foobar@example.com for an e-mail link. If you have a URI like + http://www.example.com/lala/foobar@example.com you should get the + full link not the e-mail address. Or you can put a location + like 1600 Amphitheatre Parkway, Mountain View, CA 94043. To summarize: + https://www.google.com, or 650-253-0000, somebody@example.com, + or 9606 North MoPac Expressway, Suite 400, Austin, TX 78759.</string> + <string name="link_text_manual"><![CDATA[<b>text_html_resource: + Explicit links using <a> markup.</b> + This has markup for a <a href="http://www.google.com">link</a> specified + via an <a> tag. Use a \"tel:\" URL + to <a href="tel:4155551212">dial a phone number</a>.]]></string> + +</resources> diff --git a/samples/browseable/TextLinkify/res/values/styles.xml b/samples/browseable/TextLinkify/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/TextLinkify/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/TextLinkify/src/com.example.android.common.logger/Log.java b/samples/browseable/TextLinkify/src/com.example.android.common.logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/TextLinkify/src/com.example.android.common.logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/TextLinkify/src/com.example.android.common.logger/LogFragment.java b/samples/browseable/TextLinkify/src/com.example.android.common.logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/TextLinkify/src/com.example.android.common.logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/TextLinkify/src/com.example.android.common.logger/LogNode.java b/samples/browseable/TextLinkify/src/com.example.android.common.logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/TextLinkify/src/com.example.android.common.logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/TextLinkify/src/com.example.android.common.logger/LogView.java b/samples/browseable/TextLinkify/src/com.example.android.common.logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/TextLinkify/src/com.example.android.common.logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/TextLinkify/src/com.example.android.common.logger/LogWrapper.java b/samples/browseable/TextLinkify/src/com.example.android.common.logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/TextLinkify/src/com.example.android.common.logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/TextLinkify/src/com.example.android.common.logger/MessageOnlyLogFilter.java b/samples/browseable/TextLinkify/src/com.example.android.common.logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/TextLinkify/src/com.example.android.common.logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/TextLinkify/src/com.example.android.textlinkify/MainActivity.java b/samples/browseable/TextLinkify/src/com.example.android.textlinkify/MainActivity.java new file mode 100644 index 000000000..c9325a95e --- /dev/null +++ b/samples/browseable/TextLinkify/src/com.example.android.textlinkify/MainActivity.java @@ -0,0 +1,138 @@ +/* + * 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.textlinkify; + +import android.app.Activity; +import android.graphics.Typeface; +import android.os.Bundle; +import android.text.Html; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.method.LinkMovementMethod; +import android.text.style.StyleSpan; +import android.text.style.URLSpan; +import android.widget.TextView; + +/** + * This sample demonstrates how clickable links can be added to a + * {@link android.widget.TextView}. + * + * <p>This can be done in three ways: + * <ul> + * <li><b>Automatically:</b> Text added to a TextView can automatically be linkified by enabling + * autoLinking. In XML, use the android:autoLink property, programatically call + * {@link android.widget.TextView#setAutoLinkMask(int)} using an option from + * {@link android.text.util.Linkify}</li> + * + * <li><b>Parsing a String as HTML:</b> See {@link android.text.Html#fromHtml(String)})</li> + * + * <li><b>Manually by constructing a {@link android.text.SpannableString}:</b> Consisting of + * {@link android.text.style.StyleSpan} and {@link android.text.style.URLSpan} objects that + * are contained within a {@link android.text.SpannableString}</li> + * </ul></p> + * + */ +public class MainActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.sample_main); + + // BEGIN_INCLUDE(text_auto_linkify) + /* + * text_auto_linkify shows the android:autoLink property, which + * automatically linkifies things like URLs and phone numbers + * found in the text. No java code is needed to make this + * work. + * This can also be enabled programmatically by calling + * .setAutoLinkMask(Linkify.ALL) before the text is set on the TextView. + * + * See android.text.util.Linkify for other options, for example only + * auto-linking email addresses or phone numbers + */ + // END_INCLUDE(text_auto_linkify) + + // BEGIN_INCLUDE(text_html_resource) + /* + * text_html_resource has links specified by putting anchor tags (<a>) in the string + * resource. By default these links will appear but not + * respond to user input. To make them active, you need to + * call setMovementMethod() on the TextView object. + */ + TextView textViewResource = (TextView) findViewById(R.id.text_html_resource); + textViewResource.setText( + Html.fromHtml(getResources().getString(R.string.link_text_manual))); + textViewResource.setMovementMethod(LinkMovementMethod.getInstance()); + // END_INCLUDE(text_html_resource) + + // BEGIN_INCLUDE(text_html_program) + /* + * text_html_program shows creating text with links from HTML in the Java + * code, rather than from a string resource. Note that for a + * fixed string, using a (localizable) resource as shown above + * is usually a better way to go; this example is intended to + * illustrate how you might display text that came from a + * dynamic source (eg, the network). + */ + TextView textViewHtml = (TextView) findViewById(R.id.text_html_program); + textViewHtml.setText( + Html.fromHtml( + "<b>text_html_program: Constructed from HTML programmatically.</b>" + + " Text with a <a href=\"http://www.google.com\">link</a> " + + "created in the Java source code using HTML.")); + textViewHtml.setMovementMethod(LinkMovementMethod.getInstance()); + // END_INCLUDE(text_html_program) + + // BEGIN_INCLUDE(text_spannable) + /* + * text_spannable illustrates constructing a styled string containing a + * link without using HTML at all. Again, for a fixed string + * you should probably be using a string resource, not a + * hardcoded value. + */ + SpannableString ss = new SpannableString( + "text_spannable: Manually created spans. Click here to dial the phone."); + + /* + * Make the first 38 characters bold by applying a StyleSpan with bold typeface. + * + * Characters 45 to 49 (the word "here") is made clickable by applying a URLSpan + * pointing to a telephone number. Clicking it opens the "tel:" URL that starts the dialer. + * + * The SPAN_EXCLUSIVE_EXCLUSIVE flag defines this span as exclusive, which means + * that it will not expand to include text inserted on either side of this span. + */ + ss.setSpan(new StyleSpan(Typeface.BOLD), 0, 39, + Spanned.SPAN_INCLUSIVE_INCLUSIVE); + ss.setSpan(new URLSpan("tel:4155551212"), 40 + 6, 40 + 10, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + TextView textViewSpan = (TextView) findViewById(R.id.text_spannable); + textViewSpan.setText(ss); + + /* + * Set the movement method to move between links in this TextView. + * This means that the user traverses through links in this TextView, automatically + * handling appropriate scrolling and key commands. + */ + textViewSpan.setMovementMethod(LinkMovementMethod.getInstance()); + // END_INCLUDE(text_spannable) + } + +} diff --git a/samples/browseable/TextSwitcher/AndroidManifest.xml b/samples/browseable/TextSwitcher/AndroidManifest.xml new file mode 100644 index 000000000..7f512a335 --- /dev/null +++ b/samples/browseable/TextSwitcher/AndroidManifest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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" + package="com.example.android.textswitcher" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk + android:minSdkVersion="4" + android:targetSdkVersion="17" /> + + <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" > + <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/browseable/TextSwitcher/_index.jd b/samples/browseable/TextSwitcher/_index.jd new file mode 100644 index 000000000..40e991b13 --- /dev/null +++ b/samples/browseable/TextSwitcher/_index.jd @@ -0,0 +1,14 @@ + + + +page.tags="TextSwitcher" +sample.group=UI +@jd:body + +<p> + + This sample illustrates the use of a TextSwitcher to display text. + \n\nClick the button below to set new text in the TextSwitcher and observe the + in and out fade animations. + + </p> diff --git a/samples/browseable/TextSwitcher/res/drawable-hdpi/ic_launcher.png b/samples/browseable/TextSwitcher/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..641bab20c --- /dev/null +++ b/samples/browseable/TextSwitcher/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/TextSwitcher/res/drawable-hdpi/tile.9.png b/samples/browseable/TextSwitcher/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/TextSwitcher/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/TextSwitcher/res/drawable-mdpi/ic_launcher.png b/samples/browseable/TextSwitcher/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..e29c59fb1 --- /dev/null +++ b/samples/browseable/TextSwitcher/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/TextSwitcher/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/TextSwitcher/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..d23193b90 --- /dev/null +++ b/samples/browseable/TextSwitcher/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/TextSwitcher/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/TextSwitcher/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..22bfd4567 --- /dev/null +++ b/samples/browseable/TextSwitcher/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/TextSwitcher/res/layout/activity_main.xml b/samples/browseable/TextSwitcher/res/layout/activity_main.xml new file mode 100755 index 000000000..be1aa49d9 --- /dev/null +++ b/samples/browseable/TextSwitcher/res/layout/activity_main.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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout style="@style/Widget.SampleMessageTile" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView style="@style/Widget.SampleMessage" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/horizontal_page_margin" + android:layout_marginRight="@dimen/horizontal_page_margin" + android:layout_marginTop="@dimen/vertical_page_margin" + android:layout_marginBottom="@dimen/vertical_page_margin" + android:text="@string/intro_message" /> + </LinearLayout> +</LinearLayout> diff --git a/samples/browseable/TextSwitcher/res/layout/sample_main.xml b/samples/browseable/TextSwitcher/res/layout/sample_main.xml new file mode 100644 index 000000000..a5a705341 --- /dev/null +++ b/samples/browseable/TextSwitcher/res/layout/sample_main.xml @@ -0,0 +1,47 @@ +<!-- + 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/LinearLayout1" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="top|center_horizontal" + android:gravity="center_horizontal" + android:orientation="vertical" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + tools:context=".MainActivity" > + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/intro" /> + + <Button + android:id="@+id/button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/next" /> + + <TextSwitcher + android:id="@+id/switcher" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + +</LinearLayout> diff --git a/samples/browseable/TextSwitcher/res/values-sw600dp/dimens.xml b/samples/browseable/TextSwitcher/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/TextSwitcher/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/TextSwitcher/res/values-sw600dp/styles.xml b/samples/browseable/TextSwitcher/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/TextSwitcher/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/TextSwitcher/res/values-sw720dp-land/dimens.xml b/samples/browseable/TextSwitcher/res/values-sw720dp-land/dimens.xml new file mode 100644 index 000000000..560bd44e5 --- /dev/null +++ b/samples/browseable/TextSwitcher/res/values-sw720dp-land/dimens.xml @@ -0,0 +1,25 @@ +<!-- + 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> + + <!-- + Customize dimensions originally defined in res/values/dimens.xml (such as + screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here. + --> + <dimen name="activity_horizontal_margin">128dp</dimen> + +</resources> diff --git a/samples/browseable/TextSwitcher/res/values-v11/styles.xml b/samples/browseable/TextSwitcher/res/values-v11/styles.xml new file mode 100644 index 000000000..91f452322 --- /dev/null +++ b/samples/browseable/TextSwitcher/res/values-v11/styles.xml @@ -0,0 +1,27 @@ +<!-- + 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> + + <!-- + 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/browseable/TextSwitcher/res/values-v14/styles.xml b/samples/browseable/TextSwitcher/res/values-v14/styles.xml new file mode 100644 index 000000000..c2b6a6b30 --- /dev/null +++ b/samples/browseable/TextSwitcher/res/values-v14/styles.xml @@ -0,0 +1,28 @@ +<!-- + 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> + + <!-- + Base application theme for API 14+. This theme completely replaces + AppBaseTheme from BOTH res/values/styles.xml and + res/values-v11/styles.xml on API 14+ devices. + --> + <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar"> + <!-- API 14 theme customizations can go here. --> + </style> + +</resources> diff --git a/samples/browseable/TextSwitcher/res/values/base-strings.xml b/samples/browseable/TextSwitcher/res/values/base-strings.xml new file mode 100644 index 000000000..2497e8610 --- /dev/null +++ b/samples/browseable/TextSwitcher/res/values/base-strings.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">TextSwitcher</string> + <string name="intro_message"> + <![CDATA[ + + + This sample illustrates the use of a TextSwitcher to display text. + \n\nClick the button below to set new text in the TextSwitcher and observe the + in and out fade animations. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/TextSwitcher/res/values/dimens.xml b/samples/browseable/TextSwitcher/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/TextSwitcher/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/TextSwitcher/res/values/strings.xml b/samples/browseable/TextSwitcher/res/values/strings.xml new file mode 100644 index 000000000..612b6cbd3 --- /dev/null +++ b/samples/browseable/TextSwitcher/res/values/strings.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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="intro">This sample illustrates the use of a <b>TextSwitcher</b> to display text. +\n\n<b>Click the button</b> below to set new text in the TextSwitcher and observe the in and out + fade animations.</string> + <string name="next">Next</string> + +</resources> diff --git a/samples/browseable/TextSwitcher/res/values/styles.xml b/samples/browseable/TextSwitcher/res/values/styles.xml new file mode 100644 index 000000000..404623e3d --- /dev/null +++ b/samples/browseable/TextSwitcher/res/values/styles.xml @@ -0,0 +1,42 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/TextSwitcher/src/com.example.android.common.logger/Log.java b/samples/browseable/TextSwitcher/src/com.example.android.common.logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/TextSwitcher/src/com.example.android.common.logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/TextSwitcher/src/com.example.android.common.logger/LogFragment.java b/samples/browseable/TextSwitcher/src/com.example.android.common.logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/TextSwitcher/src/com.example.android.common.logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/TextSwitcher/src/com.example.android.common.logger/LogNode.java b/samples/browseable/TextSwitcher/src/com.example.android.common.logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/TextSwitcher/src/com.example.android.common.logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/TextSwitcher/src/com.example.android.common.logger/LogView.java b/samples/browseable/TextSwitcher/src/com.example.android.common.logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/TextSwitcher/src/com.example.android.common.logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/TextSwitcher/src/com.example.android.common.logger/LogWrapper.java b/samples/browseable/TextSwitcher/src/com.example.android.common.logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/TextSwitcher/src/com.example.android.common.logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/TextSwitcher/src/com.example.android.common.logger/MessageOnlyLogFilter.java b/samples/browseable/TextSwitcher/src/com.example.android.common.logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/TextSwitcher/src/com.example.android.common.logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/TextSwitcher/src/com.example.android.textswitcher/MainActivity.java b/samples/browseable/TextSwitcher/src/com.example.android.textswitcher/MainActivity.java new file mode 100644 index 000000000..a8cbb5ec4 --- /dev/null +++ b/samples/browseable/TextSwitcher/src/com.example.android.textswitcher/MainActivity.java @@ -0,0 +1,104 @@ +/* + * 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.textswitcher; + +import android.app.Activity; +import android.os.Bundle; +import android.view.Gravity; +import android.view.View; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.Button; +import android.widget.TextSwitcher; +import android.widget.TextView; +import android.widget.ViewSwitcher.ViewFactory; + +/** + * This sample shows the use of the {@link android.widget.TextSwitcher} View with animations. A + * {@link android.widget.TextSwitcher} is a special type of {@link android.widget.ViewSwitcher} that animates + * the current text out and new text in when + * {@link android.widget.TextSwitcher#setText(CharSequence)} is called. + */ +public class MainActivity extends Activity { + private TextSwitcher mSwitcher; + private int mCounter = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.sample_main); + + // Get the TextSwitcher view from the layout + mSwitcher = (TextSwitcher) findViewById(R.id.switcher); + + // BEGIN_INCLUDE(setup) + // Set the factory used to create TextViews to switch between. + mSwitcher.setFactory(mFactory); + + /* + * Set the in and out animations. Using the fade_in/out animations + * provided by the framework. + */ + Animation in = AnimationUtils.loadAnimation(this, + android.R.anim.fade_in); + Animation out = AnimationUtils.loadAnimation(this, + android.R.anim.fade_out); + mSwitcher.setInAnimation(in); + mSwitcher.setOutAnimation(out); + // END_INCLUDE(setup) + + /* + * Setup the 'next' button. The counter is incremented when clicked and + * the new value is displayed in the TextSwitcher. The change of text is + * automatically animated using the in/out animations set above. + */ + Button nextButton = (Button) findViewById(R.id.button); + nextButton.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + mCounter++; + // BEGIN_INCLUDE(settext) + mSwitcher.setText(String.valueOf(mCounter)); + // END_INCLUDE(settext) + } + }); + + // Set the initial text without an animation + mSwitcher.setCurrentText(String.valueOf(mCounter)); + + } + + // BEGIN_INCLUDE(factory) + /** + * The {@link android.widget.ViewSwitcher.ViewFactory} used to create {@link android.widget.TextView}s that the + * {@link android.widget.TextSwitcher} will switch between. + */ + private ViewFactory mFactory = new ViewFactory() { + + @Override + public View makeView() { + + // Create a new TextView + TextView t = new TextView(MainActivity.this); + t.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL); + t.setTextAppearance(MainActivity.this, android.R.style.TextAppearance_Large); + return t; + } + }; + // END_INCLUDE(factory) +} diff --git a/samples/browseable/repeatingAlarm/AndroidManifest.xml b/samples/browseable/repeatingAlarm/AndroidManifest.xml new file mode 100644 index 000000000..b7d02e570 --- /dev/null +++ b/samples/browseable/repeatingAlarm/AndroidManifest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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" + package="com.example.android.repeatingalarm" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="17" /> + + <application android:allowBackup="true" + android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@style/AppTheme"> + + <activity android:name=".MainActivity" + android:label="@string/app_name" + android:uiOptions="splitActionBarWhenNarrow"> + <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/browseable/repeatingAlarm/_index.jd b/samples/browseable/repeatingAlarm/_index.jd new file mode 100644 index 000000000..6abb305c1 --- /dev/null +++ b/samples/browseable/repeatingAlarm/_index.jd @@ -0,0 +1,13 @@ + + + +page.tags="repeatingAlarm" +sample.group=Background +@jd:body + +<p> + + Introductory text that explains what the sample is intended to demonstrate. Edit + in template-params.xml. + + </p> diff --git a/samples/browseable/repeatingAlarm/res/drawable-hdpi/ic_launcher.png b/samples/browseable/repeatingAlarm/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..b1efaf4b2 --- /dev/null +++ b/samples/browseable/repeatingAlarm/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/repeatingAlarm/res/drawable-hdpi/tile.9.png b/samples/browseable/repeatingAlarm/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/repeatingAlarm/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/repeatingAlarm/res/drawable-mdpi/ic_launcher.png b/samples/browseable/repeatingAlarm/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f5f9244f2 --- /dev/null +++ b/samples/browseable/repeatingAlarm/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/repeatingAlarm/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/repeatingAlarm/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..5d07b3f06 --- /dev/null +++ b/samples/browseable/repeatingAlarm/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/repeatingAlarm/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/repeatingAlarm/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6ef21e1f4 --- /dev/null +++ b/samples/browseable/repeatingAlarm/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/repeatingAlarm/res/layout/activity_main.xml b/samples/browseable/repeatingAlarm/res/layout/activity_main.xml new file mode 100755 index 000000000..bc5a57591 --- /dev/null +++ b/samples/browseable/repeatingAlarm/res/layout/activity_main.xml @@ -0,0 +1,38 @@ +<!-- + 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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:id="@+id/sample_main_layout"> + <TextView android:id="@+id/sample_output" + style="@style/Widget.SampleMessage" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:text="@string/intro_message" /> + <View + android:layout_width="fill_parent" + android:layout_height="1dp" + android:background="@android:color/darker_gray"/> + <fragment + android:name="com.example.android.common.logger.LogFragment" + android:id="@+id/log_fragment" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="match_parent" /> +</LinearLayout> diff --git a/samples/browseable/repeatingAlarm/res/menu/main.xml b/samples/browseable/repeatingAlarm/res/menu/main.xml new file mode 100644 index 000000000..2c3515dd4 --- /dev/null +++ b/samples/browseable/repeatingAlarm/res/menu/main.xml @@ -0,0 +1,21 @@ +<!-- + 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. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/sample_action" + android:showAsAction="ifRoom|withText" + android:title="@string/sample_action" /> +</menu> diff --git a/samples/browseable/repeatingAlarm/res/values-sw600dp/dimens.xml b/samples/browseable/repeatingAlarm/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/repeatingAlarm/res/values-sw600dp/dimens.xml @@ -0,0 +1,24 @@ +<!-- + 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> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/repeatingAlarm/res/values-sw600dp/styles.xml b/samples/browseable/repeatingAlarm/res/values-sw600dp/styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/repeatingAlarm/res/values-sw600dp/styles.xml @@ -0,0 +1,25 @@ +<!-- + 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="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/repeatingAlarm/res/values/base-strings.xml b/samples/browseable/repeatingAlarm/res/values/base-strings.xml new file mode 100644 index 000000000..c11b89b86 --- /dev/null +++ b/samples/browseable/repeatingAlarm/res/values/base-strings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="app_name">repeatingAlarm</string> + <string name="intro_message"> + <![CDATA[ + + + Introductory text that explains what the sample is intended to demonstrate. Edit + in template-params.xml. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/repeatingAlarm/res/values/dimens.xml b/samples/browseable/repeatingAlarm/res/values/dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/repeatingAlarm/res/values/dimens.xml @@ -0,0 +1,32 @@ +<!-- + 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> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/repeatingAlarm/res/values/strings.xml b/samples/browseable/repeatingAlarm/res/values/strings.xml new file mode 100644 index 000000000..2013d95c1 --- /dev/null +++ b/samples/browseable/repeatingAlarm/res/values/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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="sample_action">Set Alarm</string> +</resources> diff --git a/samples/browseable/repeatingAlarm/res/values/styles.xml b/samples/browseable/repeatingAlarm/res/values/styles.xml new file mode 100644 index 000000000..d3f82ff64 --- /dev/null +++ b/samples/browseable/repeatingAlarm/res/values/styles.xml @@ -0,0 +1,51 @@ +<!-- + 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> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + + <style name="AppTheme" parent="Theme.Base" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + + + <style name="Widget.SampleOutput"> + <item name="android:padding">@dimen/margin_medium</item> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Log" parent="Widget.SampleOutput"> + <item name="android:typeface">monospace</item> + </style> + +</resources> diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java new file mode 100644 index 000000000..3228927b7 --- /dev/null +++ b/samples/browseable/repeatingAlarm/src/com.example.android.common/activities/SampleActivityBase.java @@ -0,0 +1,52 @@ +/* +* 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.common.activities; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogWrapper; + +/** + * Base launcher activity, to handle most of the common plumbing for samples. + */ +public class SampleActivityBase extends FragmentActivity { + + public static final String TAG = "SampleActivityBase"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + initializeLogging(); + } + + /** Set up targets to receive log data */ + public void initializeLogging() { + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java b/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java new file mode 100644 index 000000000..17503c568 --- /dev/null +++ b/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/Log.java @@ -0,0 +1,236 @@ +/* + * 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.common.logger; + +/** + * Helper class for a list (or tree) of LoggerNodes. + * + * <p>When this is set as the head of the list, + * an instance of it can function as a drop-in replacement for {@link android.util.Log}. + * Most of the methods in this class server only to map a method call in Log to its equivalent + * in LogNode.</p> + */ +public class Log { + // Grabbing the native values from Android's native logging facilities, + // to make for easy migration and interop. + public static final int NONE = -1; + public static final int VERBOSE = android.util.Log.VERBOSE; + public static final int DEBUG = android.util.Log.DEBUG; + public static final int INFO = android.util.Log.INFO; + public static final int WARN = android.util.Log.WARN; + public static final int ERROR = android.util.Log.ERROR; + public static final int ASSERT = android.util.Log.ASSERT; + + // Stores the beginning of the LogNode topology. + private static LogNode mLogNode; + + /** + * Returns the next LogNode in the linked list. + */ + public static LogNode getLogNode() { + return mLogNode; + } + + /** + * Sets the LogNode data will be sent to. + */ + public static void setLogNode(LogNode node) { + mLogNode = node; + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void println(int priority, String tag, String msg, Throwable tr) { + if (mLogNode != null) { + mLogNode.println(priority, tag, msg, tr); + } + } + + /** + * Instructs the LogNode to print the log data provided. Other LogNodes can + * be chained to the end of the LogNode as desired. + * + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + */ + public static void println(int priority, String tag, String msg) { + println(priority, tag, msg, null); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void v(String tag, String msg, Throwable tr) { + println(VERBOSE, tag, msg, tr); + } + + /** + * Prints a message at VERBOSE priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void v(String tag, String msg) { + v(tag, msg, null); + } + + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void d(String tag, String msg, Throwable tr) { + println(DEBUG, tag, msg, tr); + } + + /** + * Prints a message at DEBUG priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void d(String tag, String msg) { + d(tag, msg, null); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void i(String tag, String msg, Throwable tr) { + println(INFO, tag, msg, tr); + } + + /** + * Prints a message at INFO priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void i(String tag, String msg) { + i(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, String msg, Throwable tr) { + println(WARN, tag, msg, tr); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void w(String tag, String msg) { + w(tag, msg, null); + } + + /** + * Prints a message at WARN priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void w(String tag, Throwable tr) { + w(tag, null, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void e(String tag, String msg, Throwable tr) { + println(ERROR, tag, msg, tr); + } + + /** + * Prints a message at ERROR priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void e(String tag, String msg) { + e(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, String msg, Throwable tr) { + println(ASSERT, tag, msg, tr); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. + */ + public static void wtf(String tag, String msg) { + wtf(tag, msg, null); + } + + /** + * Prints a message at ASSERT priority. + * + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public static void wtf(String tag, Throwable tr) { + wtf(tag, null, tr); + } +} diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java new file mode 100644 index 000000000..b302acd4b --- /dev/null +++ b/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogFragment.java @@ -0,0 +1,109 @@ +/* +* 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. +*/ +/* + * 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.common.logger; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +/** + * Simple fraggment which contains a LogView and uses is to output log data it receives + * through the LogNode interface. + */ +public class LogFragment extends Fragment { + + private LogView mLogView; + private ScrollView mScrollView; + + public LogFragment() {} + + public View inflateViews() { + mScrollView = new ScrollView(getActivity()); + ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + mScrollView.setLayoutParams(scrollParams); + + mLogView = new LogView(getActivity()); + ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); + logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + mLogView.setLayoutParams(logParams); + mLogView.setClickable(true); + mLogView.setFocusable(true); + mLogView.setTypeface(Typeface.MONOSPACE); + + // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! + int paddingDips = 16; + double scale = getResources().getDisplayMetrics().density; + int paddingPixels = (int) ((paddingDips * (scale)) + .5); + mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); + mLogView.setCompoundDrawablePadding(paddingPixels); + + mLogView.setGravity(Gravity.BOTTOM); + mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); + + mScrollView.addView(mLogView); + return mScrollView; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View result = inflateViews(); + + mLogView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + mScrollView.fullScroll(ScrollView.FOCUS_DOWN); + } + }); + return result; + } + + public LogView getLogView() { + return mLogView; + } +}
\ No newline at end of file diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java b/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java new file mode 100644 index 000000000..bc37cabc0 --- /dev/null +++ b/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogNode.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 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.common.logger; + +/** + * Basic interface for a logging system that can output to one or more targets. + * Note that in addition to classes that will output these logs in some format, + * one can also implement this interface over a filter and insert that in the chain, + * such that no targets further down see certain data, or see manipulated forms of the data. + * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data + * it received to HTML and sent it along to the next node in the chain, without printing it + * anywhere. + */ +public interface LogNode { + + /** + * Instructs first LogNode in the list to print the log data provided. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + public void println(int priority, String tag, String msg, Throwable tr); + +} diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java b/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java new file mode 100644 index 000000000..c01542b91 --- /dev/null +++ b/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogView.java @@ -0,0 +1,145 @@ +/* + * 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.common.logger; + +import android.app.Activity; +import android.content.Context; +import android.util.*; +import android.widget.TextView; + +/** Simple TextView which is used to output log data received through the LogNode interface. +*/ +public class LogView extends TextView implements LogNode { + + public LogView(Context context) { + super(context); + } + + public LogView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LogView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Formats the log data and prints it out to the LogView. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + + + String priorityStr = null; + + // For the purposes of this View, we want to print the priority as readable text. + switch(priority) { + case android.util.Log.VERBOSE: + priorityStr = "VERBOSE"; + break; + case android.util.Log.DEBUG: + priorityStr = "DEBUG"; + break; + case android.util.Log.INFO: + priorityStr = "INFO"; + break; + case android.util.Log.WARN: + priorityStr = "WARN"; + break; + case android.util.Log.ERROR: + priorityStr = "ERROR"; + break; + case android.util.Log.ASSERT: + priorityStr = "ASSERT"; + break; + default: + break; + } + + // Handily, the Log class has a facility for converting a stack trace into a usable string. + String exceptionStr = null; + if (tr != null) { + exceptionStr = android.util.Log.getStackTraceString(tr); + } + + // Take the priority, tag, message, and exception, and concatenate as necessary + // into one usable line of text. + final StringBuilder outputBuilder = new StringBuilder(); + + String delimiter = "\t"; + appendIfNotNull(outputBuilder, priorityStr, delimiter); + appendIfNotNull(outputBuilder, tag, delimiter); + appendIfNotNull(outputBuilder, msg, delimiter); + appendIfNotNull(outputBuilder, exceptionStr, delimiter); + + // In case this was originally called from an AsyncTask or some other off-UI thread, + // make sure the update occurs within the UI thread. + ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { + @Override + public void run() { + // Display the text we just generated within the LogView. + appendToLog(outputBuilder.toString()); + } + }))); + + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } + + public LogNode getNext() { + return mNext; + } + + public void setNext(LogNode node) { + mNext = node; + } + + /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since + * the logger takes so many arguments that might be null, this method helps cut out some of the + * agonizing tedium of writing the same 3 lines over and over. + * @param source StringBuilder containing the text to append to. + * @param addStr The String to append + * @param delimiter The String to separate the source and appended strings. A tab or comma, + * for instance. + * @return The fully concatenated String as a StringBuilder + */ + private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { + if (addStr != null) { + if (addStr.length() == 0) { + delimiter = ""; + } + + return source.append(addStr).append(delimiter); + } + return source; + } + + // The next LogNode in the chain. + LogNode mNext; + + /** Outputs the string as a new line of log data in the LogView. */ + public void appendToLog(String s) { + append("\n" + s); + } + + +} diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java new file mode 100644 index 000000000..16a9e7ba2 --- /dev/null +++ b/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/LogWrapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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.common.logger; + +import android.util.Log; + +/** + * Helper class which wraps Android's native Log utility in the Logger interface. This way + * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. + */ +public class LogWrapper implements LogNode { + + // For piping: The next node to receive Log data after this one has done its work. + private LogNode mNext; + + /** + * Returns the next LogNode in the linked list. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + + /** + * Prints data out to the console using Android's native log mechanism. + * @param priority Log level of the data being logged. Verbose, Error, etc. + * @param tag Tag for for the log data. Can be used to organize log statements. + * @param msg The actual message to be logged. The actual message to be logged. + * @param tr If an exception was thrown, this can be sent along for the logging facilities + * to extract and print useful information. + */ + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + // There actually are log methods that don't take a msg parameter. For now, + // if that's the case, just convert null to the empty string and move on. + String useMsg = msg; + if (useMsg == null) { + useMsg = ""; + } + + // If an exeption was provided, convert that exception to a usable string and attach + // it to the end of the msg method. + if (tr != null) { + msg += "\n" + Log.getStackTraceString(tr); + } + + // This is functionally identical to Log.x(tag, useMsg); + // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) + Log.println(priority, tag, useMsg); + + // If this isn't the last node in the chain, move things along. + if (mNext != null) { + mNext.println(priority, tag, msg, tr); + } + } +} diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java new file mode 100644 index 000000000..19967dcd4 --- /dev/null +++ b/samples/browseable/repeatingAlarm/src/com.example.android.common/logger/MessageOnlyLogFilter.java @@ -0,0 +1,60 @@ +/* + * 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.common.logger; + +/** + * Simple {@link LogNode} filter, removes everything except the message. + * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, + * just easy-to-read message updates as they're happening. + */ +public class MessageOnlyLogFilter implements LogNode { + + LogNode mNext; + + /** + * Takes the "next" LogNode as a parameter, to simplify chaining. + * + * @param next The next LogNode in the pipeline. + */ + public MessageOnlyLogFilter(LogNode next) { + mNext = next; + } + + public MessageOnlyLogFilter() { + } + + @Override + public void println(int priority, String tag, String msg, Throwable tr) { + if (mNext != null) { + getNext().println(Log.NONE, null, msg, null); + } + } + + /** + * Returns the next LogNode in the chain. + */ + public LogNode getNext() { + return mNext; + } + + /** + * Sets the LogNode data will be sent to.. + */ + public void setNext(LogNode node) { + mNext = node; + } + +} diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.repeatingalarm/MainActivity.java b/samples/browseable/repeatingAlarm/src/com.example.android.repeatingalarm/MainActivity.java new file mode 100644 index 000000000..2d2a6aaad --- /dev/null +++ b/samples/browseable/repeatingAlarm/src/com.example.android.repeatingalarm/MainActivity.java @@ -0,0 +1,80 @@ +/* +* 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.repeatingalarm; + +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.view.Menu; + +import com.example.android.common.activities.SampleActivityBase; +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogFragment; +import com.example.android.common.logger.LogWrapper; +import com.example.android.common.logger.MessageOnlyLogFilter; + +/** + * A simple launcher activity containing a summary sample description + * and a few action bar buttons. + */ +public class MainActivity extends SampleActivityBase { + + public static final String TAG = "MainActivity"; + + public static final String FRAGTAG = "RepeatingAlarmFragment"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + if (getSupportFragmentManager().findFragmentByTag(FRAGTAG) == null ) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + RepeatingAlarmFragment fragment = new RepeatingAlarmFragment(); + transaction.add(fragment, FRAGTAG); + transaction.commit(); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + /** Create a chain of targets that will receive log data */ + @Override + public void initializeLogging() { + // Wraps Android's native log framework. + LogWrapper logWrapper = new LogWrapper(); + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + Log.setLogNode(logWrapper); + + // Filter strips out everything except the message text. + MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); + logWrapper.setNext(msgFilter); + + // On screen logging via a fragment with a TextView. + LogFragment logFragment = (LogFragment) getSupportFragmentManager() + .findFragmentById(R.id.log_fragment); + msgFilter.setNext(logFragment.getLogView()); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/repeatingAlarm/src/com.example.android.repeatingalarm/RepeatingAlarmFragment.java b/samples/browseable/repeatingAlarm/src/com.example.android.repeatingalarm/RepeatingAlarmFragment.java new file mode 100644 index 000000000..81b1e447f --- /dev/null +++ b/samples/browseable/repeatingAlarm/src/com.example.android.repeatingalarm/RepeatingAlarmFragment.java @@ -0,0 +1,96 @@ +/* +* 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.repeatingalarm; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.Intent; +import android.os.Bundle; +import android.os.SystemClock; +import android.support.v4.app.Fragment; +import android.view.MenuItem; +import com.example.android.common.logger.*; + + +public class RepeatingAlarmFragment extends Fragment { + + // This value is defined and consumed by app code, so any value will work. + // There's no significance to this sample using 0. + public static final int REQUEST_CODE = 0; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if(item.getItemId() == R.id.sample_action) { + + // BEGIN_INCLUDE (intent_fired_by_alarm) + // First create an intent for the alarm to activate. + // This code simply starts an Activity, or brings it to the front if it has already + // been created. + Intent intent = new Intent(getActivity(), MainActivity.class); + intent.setAction(Intent.ACTION_MAIN); + intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + // END_INCLUDE (intent_fired_by_alarm) + + // BEGIN_INCLUDE (pending_intent_for_alarm) + // Because the intent must be fired by a system service from outside the application, + // it's necessary to wrap it in a PendingIntent. Providing a different process with + // a PendingIntent gives that other process permission to fire the intent that this + // application has created. + // Also, this code creates a PendingIntent to start an Activity. To create a + // BroadcastIntent instead, simply call getBroadcast instead of getIntent. + PendingIntent pendingIntent = PendingIntent.getActivity(getActivity(), REQUEST_CODE, + intent, 0); + + // END_INCLUDE (pending_intent_for_alarm) + + // BEGIN_INCLUDE (configure_alarm_manager) + // There are two clock types for alarms, ELAPSED_REALTIME and RTC. + // ELAPSED_REALTIME uses time since system boot as a reference, and RTC uses UTC (wall + // clock) time. This means ELAPSED_REALTIME is suited to setting an alarm according to + // passage of time (every 15 seconds, 15 minutes, etc), since it isn't affected by + // timezone/locale. RTC is better suited for alarms that should be dependant on current + // locale. + + // Both types have a WAKEUP version, which says to wake up the device if the screen is + // off. This is useful for situations such as alarm clocks. Abuse of this flag is an + // efficient way to skyrocket the uninstall rate of an application, so use with care. + // For most situations, ELAPSED_REALTIME will suffice. + int alarmType = AlarmManager.ELAPSED_REALTIME; + final int FIFTEEN_SEC_MILLIS = 15000; + + // The AlarmManager, like most system services, isn't created by application code, but + // requested from the system. + AlarmManager alarmManager = (AlarmManager) + getActivity().getSystemService(getActivity().ALARM_SERVICE); + + // setRepeating takes a start delay and period between alarms as arguments. + // The below code fires after 15 seconds, and repeats every 15 seconds. This is very + // useful for demonstration purposes, but horrendous for production. Don't be that dev. + alarmManager.setRepeating(alarmType, SystemClock.elapsedRealtime() + FIFTEEN_SEC_MILLIS, + FIFTEEN_SEC_MILLIS, pendingIntent); + // END_INCLUDE (configure_alarm_manager); + Log.i("RepeatingAlarmFragment", "Alarm set."); + } + return true; + } +} |
