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 @@ + + + + + + + + + + + + + + + + + + + + + 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 + +

+ + This sample provides a basic example of using an InstrumentationTest to probe the + internal state of an Activity. + +

diff --git a/samples/browseable/ActivityInstrumentation/res/drawable-hdpi/ic_launcher.png b/samples/browseable/ActivityInstrumentation/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 000000000..b1efaf4b2 Binary files /dev/null and b/samples/browseable/ActivityInstrumentation/res/drawable-hdpi/ic_launcher.png differ diff --git a/samples/browseable/ActivityInstrumentation/res/drawable-hdpi/tile.9.png b/samples/browseable/ActivityInstrumentation/res/drawable-hdpi/tile.9.png new file mode 100644 index 000000000..135862883 Binary files /dev/null and b/samples/browseable/ActivityInstrumentation/res/drawable-hdpi/tile.9.png differ diff --git a/samples/browseable/ActivityInstrumentation/res/drawable-mdpi/ic_launcher.png b/samples/browseable/ActivityInstrumentation/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 000000000..f5f9244f2 Binary files /dev/null and b/samples/browseable/ActivityInstrumentation/res/drawable-mdpi/ic_launcher.png differ diff --git a/samples/browseable/ActivityInstrumentation/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/ActivityInstrumentation/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 000000000..5d07b3f06 Binary files /dev/null and b/samples/browseable/ActivityInstrumentation/res/drawable-xhdpi/ic_launcher.png differ diff --git a/samples/browseable/ActivityInstrumentation/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/ActivityInstrumentation/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..6ef21e1f4 Binary files /dev/null and b/samples/browseable/ActivityInstrumentation/res/drawable-xxhdpi/ic_launcher.png differ 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 @@ + + + + + + + + + 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 @@ + + + + + + + + + + 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 @@ + + + + + + + @dimen/margin_huge + @dimen/margin_medium + + 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 @@ + + + + + + + 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 @@ + + + + + 128dp + 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 @@ + + + + + + + ActivityInstrumentation + + + + 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 @@ + + + + + + + 4dp + 8dp + 16dp + 32dp + 64dp + + + + @dimen/margin_medium + @dimen/margin_medium + + 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 @@ + + + + + The value of the spinner below should be persisted when this activity is destroyed. + 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 @@ + + + + + + + + + + + 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( + this, // Context + android.R.layout.simple_list_item_1, // Layout + new ArrayList(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. + * + *

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.

+ */ +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 @@ + + + + + + + + + + + + + + + + + + + + + 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 + +

+ + \"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. + +

diff --git a/samples/browseable/AdvancedImmersiveMode/res/drawable-hdpi/ic_launcher.png b/samples/browseable/AdvancedImmersiveMode/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 000000000..b1efaf4b2 Binary files /dev/null and b/samples/browseable/AdvancedImmersiveMode/res/drawable-hdpi/ic_launcher.png differ diff --git a/samples/browseable/AdvancedImmersiveMode/res/drawable-hdpi/tile.9.png b/samples/browseable/AdvancedImmersiveMode/res/drawable-hdpi/tile.9.png new file mode 100644 index 000000000..135862883 Binary files /dev/null and b/samples/browseable/AdvancedImmersiveMode/res/drawable-hdpi/tile.9.png differ diff --git a/samples/browseable/AdvancedImmersiveMode/res/drawable-mdpi/ic_launcher.png b/samples/browseable/AdvancedImmersiveMode/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 000000000..f5f9244f2 Binary files /dev/null and b/samples/browseable/AdvancedImmersiveMode/res/drawable-mdpi/ic_launcher.png differ diff --git a/samples/browseable/AdvancedImmersiveMode/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/AdvancedImmersiveMode/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 000000000..5d07b3f06 Binary files /dev/null and b/samples/browseable/AdvancedImmersiveMode/res/drawable-xhdpi/ic_launcher.png differ diff --git a/samples/browseable/AdvancedImmersiveMode/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/AdvancedImmersiveMode/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..6ef21e1f4 Binary files /dev/null and b/samples/browseable/AdvancedImmersiveMode/res/drawable-xxhdpi/ic_launcher.png differ 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 @@ + + + + + + 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 @@ + + + + + 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 @@ + + + + + + + @dimen/margin_huge + @dimen/margin_medium + + 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 @@ + + + + + + + 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 @@ + + + + + + + AdvancedImmersiveMode + + + + 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 @@ + + + + + + + 4dp + 8dp + 16dp + 32dp + 64dp + + + + @dimen/margin_medium + @dimen/margin_medium + + 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 @@ + + + + + + + Try these settings! + 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 @@ + + + + + + + + + + + + + + + + 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. + * + *

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.

+ */ +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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + +

+ + 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. + +

diff --git a/samples/browseable/AppRestrictions/res/drawable-hdpi/ic_launcher.png b/samples/browseable/AppRestrictions/res/drawable-hdpi/ic_launcher.png new file mode 100755 index 000000000..f36c473a1 Binary files /dev/null and b/samples/browseable/AppRestrictions/res/drawable-hdpi/ic_launcher.png differ diff --git a/samples/browseable/AppRestrictions/res/drawable-hdpi/tile.9.png b/samples/browseable/AppRestrictions/res/drawable-hdpi/tile.9.png new file mode 100644 index 000000000..135862883 Binary files /dev/null and b/samples/browseable/AppRestrictions/res/drawable-hdpi/tile.9.png differ diff --git a/samples/browseable/AppRestrictions/res/drawable-mdpi/ic_launcher.png b/samples/browseable/AppRestrictions/res/drawable-mdpi/ic_launcher.png new file mode 100755 index 000000000..5ab2e0d33 Binary files /dev/null and b/samples/browseable/AppRestrictions/res/drawable-mdpi/ic_launcher.png differ diff --git a/samples/browseable/AppRestrictions/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/AppRestrictions/res/drawable-xhdpi/ic_launcher.png new file mode 100755 index 000000000..76228388e Binary files /dev/null and b/samples/browseable/AppRestrictions/res/drawable-xhdpi/ic_launcher.png differ diff --git a/samples/browseable/AppRestrictions/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/AppRestrictions/res/drawable-xxhdpi/ic_launcher.png new file mode 100755 index 000000000..7f55feff2 Binary files /dev/null and b/samples/browseable/AppRestrictions/res/drawable-xxhdpi/ic_launcher.png differ 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 @@ + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 @@ + + + + + + + @dimen/margin_huge + @dimen/margin_medium + + 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 @@ + + + + + + + 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 @@ + + + + + + + AppRestrictions + + + + 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 @@ + + + + + + + 4dp + 8dp + 16dp + 32dp + 64dp + + + + @dimen/margin_medium + @dimen/margin_medium + + 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 @@ + + + + Custom app restrictions + Test boolean type + Test choice type + Test multi-select type + If checked, use a custom app restriction Activity. Otherwise, + use standard restriction types. + + 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 + + Go to Settings + Current app restriction settings: + N/A + Your app can restrict its content based on these + settings, which can be configured through the primary user\'s Users Settings. + + + + Ice Cream + Jelly Bean + More Jelly Bean + + + + 1 + 2 + 3 + + + + Ice Cream + Jelly Bean + More Jelly Bean + + + + 1 + 2 + 3 + + + \ 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 @@ + + + + + + + + + + + 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 @@ + + + + + + + + + + + + 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 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 set = new HashSet(); + for (String value : entry.getAllSelectedStrings()) { + set.add(value); + } + mMultiPref.setValues(set); + mMultiEntry = entry; + } + } + } else { + mRestrictions = new ArrayList(); + + // 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 set = new HashSet(); + 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(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)newValue).size()]; + int i = 0; + for (String value : (Set) 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(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 initRestrictions(Context context) { + ArrayList newRestrictions = new ArrayList(); + 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 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 @@ + + + + + + + + + + + + + + + + + + + + \ 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 + +

+ + 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. + +

diff --git a/samples/browseable/Basic/res/drawable-hdpi/ic_action_location.png b/samples/browseable/Basic/res/drawable-hdpi/ic_action_location.png new file mode 100644 index 000000000..a42b3ea9f Binary files /dev/null and b/samples/browseable/Basic/res/drawable-hdpi/ic_action_location.png differ diff --git a/samples/browseable/Basic/res/drawable-hdpi/ic_action_refresh.png b/samples/browseable/Basic/res/drawable-hdpi/ic_action_refresh.png new file mode 100644 index 000000000..c9d295d6b Binary files /dev/null and b/samples/browseable/Basic/res/drawable-hdpi/ic_action_refresh.png differ diff --git a/samples/browseable/Basic/res/drawable-hdpi/ic_action_settings.png b/samples/browseable/Basic/res/drawable-hdpi/ic_action_settings.png new file mode 100644 index 000000000..d3f981d09 Binary files /dev/null and b/samples/browseable/Basic/res/drawable-hdpi/ic_action_settings.png differ diff --git a/samples/browseable/Basic/res/drawable-hdpi/ic_launcher.png b/samples/browseable/Basic/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 000000000..5bb19fbe8 Binary files /dev/null and b/samples/browseable/Basic/res/drawable-hdpi/ic_launcher.png differ diff --git a/samples/browseable/Basic/res/drawable-hdpi/tile.9.png b/samples/browseable/Basic/res/drawable-hdpi/tile.9.png new file mode 100644 index 000000000..135862883 Binary files /dev/null and b/samples/browseable/Basic/res/drawable-hdpi/tile.9.png differ diff --git a/samples/browseable/Basic/res/drawable-mdpi/ic_action_location.png b/samples/browseable/Basic/res/drawable-mdpi/ic_action_location.png new file mode 100644 index 000000000..eaf97745c Binary files /dev/null and b/samples/browseable/Basic/res/drawable-mdpi/ic_action_location.png differ diff --git a/samples/browseable/Basic/res/drawable-mdpi/ic_action_refresh.png b/samples/browseable/Basic/res/drawable-mdpi/ic_action_refresh.png new file mode 100644 index 000000000..eef97e9ec Binary files /dev/null and b/samples/browseable/Basic/res/drawable-mdpi/ic_action_refresh.png differ diff --git a/samples/browseable/Basic/res/drawable-mdpi/ic_action_settings.png b/samples/browseable/Basic/res/drawable-mdpi/ic_action_settings.png new file mode 100644 index 000000000..fc2bf8c39 Binary files /dev/null and b/samples/browseable/Basic/res/drawable-mdpi/ic_action_settings.png differ diff --git a/samples/browseable/Basic/res/drawable-mdpi/ic_launcher.png b/samples/browseable/Basic/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 000000000..5737b3631 Binary files /dev/null and b/samples/browseable/Basic/res/drawable-mdpi/ic_launcher.png differ diff --git a/samples/browseable/Basic/res/drawable-xhdpi/ic_action_location.png b/samples/browseable/Basic/res/drawable-xhdpi/ic_action_location.png new file mode 100644 index 000000000..5f11cce1b Binary files /dev/null and b/samples/browseable/Basic/res/drawable-xhdpi/ic_action_location.png differ diff --git a/samples/browseable/Basic/res/drawable-xhdpi/ic_action_refresh.png b/samples/browseable/Basic/res/drawable-xhdpi/ic_action_refresh.png new file mode 100644 index 000000000..1027c9a47 Binary files /dev/null and b/samples/browseable/Basic/res/drawable-xhdpi/ic_action_refresh.png differ diff --git a/samples/browseable/Basic/res/drawable-xhdpi/ic_action_settings.png b/samples/browseable/Basic/res/drawable-xhdpi/ic_action_settings.png new file mode 100644 index 000000000..1b9acf26d Binary files /dev/null and b/samples/browseable/Basic/res/drawable-xhdpi/ic_action_settings.png differ diff --git a/samples/browseable/Basic/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/Basic/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 000000000..31df0432a Binary files /dev/null and b/samples/browseable/Basic/res/drawable-xhdpi/ic_launcher.png differ diff --git a/samples/browseable/Basic/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/Basic/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..508743576 Binary files /dev/null and b/samples/browseable/Basic/res/drawable-xxhdpi/ic_launcher.png differ 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 @@ + + + + + + + + + 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 @@ + + + \ 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 @@ + + + + + + + + + + + + + + + \ 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 @@ + + + + + + + @dimen/margin_huge + @dimen/margin_medium + + 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 @@ + + + + + + + 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 @@ + + + + + + + Basic + + + + 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 @@ + + + + + + + 4dp + 8dp + 16dp + 32dp + 64dp + + + + @dimen/margin_medium + @dimen/margin_medium + + 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 @@ + + + + + + + + \ 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 @@ + + + + Refresh + Location + Settings + \ 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 @@ + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + 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 + +

+ + 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. + +

diff --git a/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_action_discard.png b/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_action_discard.png new file mode 100644 index 000000000..ece5ad8d6 Binary files /dev/null and b/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_action_discard.png differ diff --git a/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_action_info.png b/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_action_info.png new file mode 100644 index 000000000..da65dea1d Binary files /dev/null and b/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_action_info.png differ diff --git a/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 000000000..6c0b5ee4e Binary files /dev/null and b/samples/browseable/BasicAccessibility/res/drawable-hdpi/ic_launcher.png differ diff --git a/samples/browseable/BasicAccessibility/res/drawable-hdpi/partly_cloudy.png b/samples/browseable/BasicAccessibility/res/drawable-hdpi/partly_cloudy.png new file mode 100644 index 000000000..b1b380c96 Binary files /dev/null and b/samples/browseable/BasicAccessibility/res/drawable-hdpi/partly_cloudy.png differ diff --git a/samples/browseable/BasicAccessibility/res/drawable-hdpi/tile.9.png b/samples/browseable/BasicAccessibility/res/drawable-hdpi/tile.9.png new file mode 100644 index 000000000..135862883 Binary files /dev/null and b/samples/browseable/BasicAccessibility/res/drawable-hdpi/tile.9.png differ diff --git a/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_action_discard.png b/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_action_discard.png new file mode 100644 index 000000000..93483b6cb Binary files /dev/null and b/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_action_discard.png differ diff --git a/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_action_info.png b/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_action_info.png new file mode 100644 index 000000000..7f7e0a3dc Binary files /dev/null and b/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_action_info.png differ diff --git a/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 000000000..4ce0b8226 Binary files /dev/null and b/samples/browseable/BasicAccessibility/res/drawable-mdpi/ic_launcher.png differ diff --git a/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_action_discard.png b/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_action_discard.png new file mode 100644 index 000000000..94f7c8c1c Binary files /dev/null and b/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_action_discard.png differ diff --git a/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_action_info.png b/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_action_info.png new file mode 100644 index 000000000..4ede9ce3f Binary files /dev/null and b/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_action_info.png differ diff --git a/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 000000000..6ded70731 Binary files /dev/null and b/samples/browseable/BasicAccessibility/res/drawable-xhdpi/ic_launcher.png differ diff --git a/samples/browseable/BasicAccessibility/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicAccessibility/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..74ae891e3 Binary files /dev/null and b/samples/browseable/BasicAccessibility/res/drawable-xxhdpi/ic_launcher.png differ 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 @@ + + + + + + + + + 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 @@ + + + + + + + + + + +