summaryrefslogtreecommitdiff
path: root/samples/ApiDemos/src
diff options
context:
space:
mode:
Diffstat (limited to 'samples/ApiDemos/src')
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/accessibility/AccessibilityNodeProviderActivity.java484
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/accessibility/_index.html6
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/animation/Transitions.java91
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/ActionBarDisplayOptions.java30
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/AppUpdateSspReceiver.java33
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.java39
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/DoNothing.java27
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/FragmentArgumentsFragment.java53
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/FragmentContextMenu.java5
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java6
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/FragmentMenuFragment.java95
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/FragmentNestingTabs.java118
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/FragmentStack.java6
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/FragmentStackFragment.java89
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/FragmentTabsFragment.java251
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/Intents.java26
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.java38
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java38
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/LoaderRetained.java40
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/OverscanActivity.java39
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/PresentationActivity.java487
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/PresentationWithMediaRouterActivity.java290
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/PrintBitmap.java82
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/PrintCustomContent.java562
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/PrintHtmlFromScreen.java93
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/PrintHtmlOffScreen.java132
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/RotationAnimation.java91
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/ScreenOrientation.java4
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/SecureDialogActivity.java94
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/SecureSurfaceViewActivity.java101
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/app/SecureWindowActivity.java71
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/content/ChangedContacts.java348
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/content/FileProvider.java41
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/content/TextUndoActivity.java73
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/graphics/CubeRenderer.java2
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/hardware/ConsumerIr.java121
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/media/VideoViewDemo.java27
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/os/TriggerSensors.java113
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/security/KeyStoreUsage.java526
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/view/ContentBrowserNavActivity.java266
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/view/CustomLayout.java245
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/view/CustomLayoutActivity.java33
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/view/DateWidgets1.java40
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/view/ExpandableList1.java4
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/view/GameControllerInput.java10
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/view/GameView.java4
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/view/GridLayout1.java6
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/view/GridLayout2.java32
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/view/GridLayout3.java (renamed from samples/ApiDemos/src/com/example/android/apis/view/GridLayout0.java)66
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/view/SystemUIModes.java (renamed from samples/ApiDemos/src/com/example/android/apis/view/OverscanActivity.java)85
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/view/SystemUIModesOverlay.java31
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/view/TextClockDemo.java38
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/view/TranslucentBarsActivity.java36
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/view/VideoPlayerActivity.java2
-rw-r--r--samples/ApiDemos/src/com/example/android/apis/view/WindowFocusObserver.java106
55 files changed, 5155 insertions, 621 deletions
diff --git a/samples/ApiDemos/src/com/example/android/apis/accessibility/AccessibilityNodeProviderActivity.java b/samples/ApiDemos/src/com/example/android/apis/accessibility/AccessibilityNodeProviderActivity.java
deleted file mode 100644
index 1ca036a55..000000000
--- a/samples/ApiDemos/src/com/example/android/apis/accessibility/AccessibilityNodeProviderActivity.java
+++ /dev/null
@@ -1,484 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.apis.accessibility;
-
-import com.example.android.apis.R;
-
-import android.app.Activity;
-import android.app.Service;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeProvider;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * This sample demonstrates how a View can expose a virtual view sub-tree
- * rooted at it. A virtual sub-tree is composed of imaginary Views
- * that are reported as a part of the view hierarchy for accessibility
- * purposes. This enables custom views that draw complex content to report
- * them selves as a tree of virtual views, thus conveying their logical
- * structure.
- * <p>
- * For example, a View may draw a monthly calendar as a grid of days while
- * each such day may contains some events. From a perspective of the View
- * hierarchy the calendar is composed of a single View but an accessibility
- * service would benefit of traversing the logical structure of the calendar
- * by examining each day and each event on that day.
- * </p>
- */
-public class AccessibilityNodeProviderActivity extends Activity {
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.accessibility_node_provider);
- }
-
- /**
- * This class presents a View that is composed of three virtual children
- * each of which is drawn with a different color and represents a region
- * of the View that has different semantics compared to other such regions.
- * While the virtual view tree exposed by this class is one level deep
- * for simplicity, there is no bound on the complexity of that virtual
- * sub-tree.
- */
- public static class VirtualSubtreeRootView extends View {
-
- /** Paint object for drawing the virtual sub-tree */
- private final Paint mPaint = new Paint();
-
- /** Temporary rectangle to minimize object creation. */
- private final Rect mTempRect = new Rect();
-
- /** Handle to the system accessibility service. */
- private final AccessibilityManager mAccessibilityManager;
-
- /** The virtual children of this View. */
- private final List<VirtualView> mChildren = new ArrayList<VirtualView>();
-
- /** The instance of the node provider for the virtual tree - lazily instantiated. */
- private AccessibilityNodeProvider mAccessibilityNodeProvider;
-
- /** The last hovered child used for event dispatching. */
- private VirtualView mLastHoveredChild;
-
- public VirtualSubtreeRootView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mAccessibilityManager = (AccessibilityManager) context.getSystemService(
- Service.ACCESSIBILITY_SERVICE);
- createVirtualChildren();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public AccessibilityNodeProvider getAccessibilityNodeProvider() {
- // Instantiate the provide only when requested. Since the system
- // will call this method multiple times it is a good practice to
- // cache the provider instance.
- if (mAccessibilityNodeProvider == null) {
- mAccessibilityNodeProvider = new VirtualDescendantsProvider();
- }
- return mAccessibilityNodeProvider;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean dispatchHoverEvent(MotionEvent event) {
- // This implementation assumes that the virtual children
- // cannot overlap and are always visible. Do NOT use this
- // code as a reference of how to implement hover event
- // dispatch. Instead, refer to ViewGroup#dispatchHoverEvent.
- boolean handled = false;
- List<VirtualView> children = mChildren;
- final int childCount = children.size();
- for (int i = 0; i < childCount; i++) {
- VirtualView child = children.get(i);
- Rect childBounds = child.mBounds;
- final int childCoordsX = (int) event.getX() + getScrollX();
- final int childCoordsY = (int) event.getY() + getScrollY();
- if (!childBounds.contains(childCoordsX, childCoordsY)) {
- continue;
- }
- final int action = event.getAction();
- switch (action) {
- case MotionEvent.ACTION_HOVER_ENTER: {
- mLastHoveredChild = child;
- handled |= onHoverVirtualView(child, event);
- event.setAction(action);
- } break;
- case MotionEvent.ACTION_HOVER_MOVE: {
- if (child == mLastHoveredChild) {
- handled |= onHoverVirtualView(child, event);
- event.setAction(action);
- } else {
- MotionEvent eventNoHistory = event.getHistorySize() > 0
- ? MotionEvent.obtainNoHistory(event) : event;
- eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
- onHoverVirtualView(mLastHoveredChild, eventNoHistory);
- eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
- onHoverVirtualView(child, eventNoHistory);
- mLastHoveredChild = child;
- eventNoHistory.setAction(MotionEvent.ACTION_HOVER_MOVE);
- handled |= onHoverVirtualView(child, eventNoHistory);
- if (eventNoHistory != event) {
- eventNoHistory.recycle();
- } else {
- event.setAction(action);
- }
- }
- } break;
- case MotionEvent.ACTION_HOVER_EXIT: {
- mLastHoveredChild = null;
- handled |= onHoverVirtualView(child, event);
- event.setAction(action);
- } break;
- }
- }
- if (!handled) {
- handled |= onHoverEvent(event);
- }
- return handled;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- // The virtual children are ordered horizontally next to
- // each other and take the entire space of this View.
- int offsetX = 0;
- List<VirtualView> children = mChildren;
- final int childCount = children.size();
- for (int i = 0; i < childCount; i++) {
- VirtualView child = children.get(i);
- Rect childBounds = child.mBounds;
- childBounds.set(offsetX, 0, offsetX + childBounds.width(), childBounds.height());
- offsetX += childBounds.width();
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // The virtual children are ordered horizontally next to
- // each other and take the entire space of this View.
- int width = 0;
- int height = 0;
- List<VirtualView> children = mChildren;
- final int childCount = children.size();
- for (int i = 0; i < childCount; i++) {
- VirtualView child = children.get(i);
- width += child.mBounds.width();
- height = Math.max(height, child.mBounds.height());
- }
- setMeasuredDimension(width, height);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void onDraw(Canvas canvas) {
- // Draw the virtual children with the reusable Paint object
- // and with the bounds and color which are child specific.
- Rect drawingRect = mTempRect;
- List<VirtualView> children = mChildren;
- final int childCount = children.size();
- for (int i = 0; i < childCount; i++) {
- VirtualView child = children.get(i);
- drawingRect.set(child.mBounds);
- mPaint.setColor(child.mColor);
- mPaint.setAlpha(child.mAlpha);
- canvas.drawRect(drawingRect, mPaint);
- }
- }
-
- /**
- * Creates the virtual children of this View.
- */
- private void createVirtualChildren() {
- // The virtual portion of the tree is one level deep. Note
- // that implementations can use any way of representing and
- // drawing virtual view.
- VirtualView firstChild = new VirtualView(0, new Rect(0, 0, 150, 150), Color.RED,
- "Virtual view 1");
- mChildren.add(firstChild);
- VirtualView secondChild = new VirtualView(1, new Rect(0, 0, 150, 150), Color.GREEN,
- "Virtual view 2");
- mChildren.add(secondChild);
- VirtualView thirdChild = new VirtualView(2, new Rect(0, 0, 150, 150), Color.BLUE,
- "Virtual view 3");
- mChildren.add(thirdChild);
- }
-
- /**
- * Set the selected state of a virtual view.
- *
- * @param virtualView The virtual view whose selected state to set.
- * @param selected Whether the virtual view is selected.
- */
- private void setVirtualViewSelected(VirtualView virtualView, boolean selected) {
- virtualView.mAlpha = selected ? VirtualView.ALPHA_SELECTED : VirtualView.ALPHA_NOT_SELECTED;
- }
-
- /**
- * Handle a hover over a virtual view.
- *
- * @param virtualView The virtual view over which is hovered.
- * @param event The event to dispatch.
- * @return Whether the event was handled.
- */
- private boolean onHoverVirtualView(VirtualView virtualView, MotionEvent event) {
- // The implementation of hover event dispatch can be implemented
- // in any way that is found suitable. However, each virtual View
- // should fire a corresponding accessibility event whose source
- // is that virtual view. Accessibility services get the event source
- // as the entry point of the APIs for querying the window content.
- final int action = event.getAction();
- switch (action) {
- case MotionEvent.ACTION_HOVER_ENTER: {
- sendAccessibilityEventForVirtualView(virtualView,
- AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
- } break;
- case MotionEvent.ACTION_HOVER_EXIT: {
- sendAccessibilityEventForVirtualView(virtualView,
- AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
- } break;
- }
- return true;
- }
-
- /**
- * Sends a properly initialized accessibility event for a virtual view..
- *
- * @param virtualView The virtual view.
- * @param eventType The type of the event to send.
- */
- private void sendAccessibilityEventForVirtualView(VirtualView virtualView, int eventType) {
- // If touch exploration, i.e. the user gets feedback while touching
- // the screen, is enabled we fire accessibility events.
- if (mAccessibilityManager.isTouchExplorationEnabled()) {
- AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
- event.setPackageName(getContext().getPackageName());
- event.setClassName(virtualView.getClass().getName());
- event.setSource(VirtualSubtreeRootView.this, virtualView.mId);
- event.getText().add(virtualView.mText);
- getParent().requestSendAccessibilityEvent(VirtualSubtreeRootView.this, event);
- }
- }
-
- /**
- * Finds a virtual view given its id.
- *
- * @param id The virtual view id.
- * @return The found virtual view.
- */
- private VirtualView findVirtualViewById(int id) {
- List<VirtualView> children = mChildren;
- final int childCount = children.size();
- for (int i = 0; i < childCount; i++) {
- VirtualView child = children.get(i);
- if (child.mId == id) {
- return child;
- }
- }
- return null;
- }
-
- /**
- * Represents a virtual View.
- */
- private class VirtualView {
- public static final int ALPHA_SELECTED = 255;
- public static final int ALPHA_NOT_SELECTED = 127;
-
- public final int mId;
- public final int mColor;
- public final Rect mBounds;
- public final String mText;
- public int mAlpha;
-
- public VirtualView(int id, Rect bounds, int color, String text) {
- mId = id;
- mColor = color;
- mBounds = bounds;
- mText = text;
- mAlpha = ALPHA_NOT_SELECTED;
- }
- }
-
- /**
- * This is the provider that exposes the virtual View tree to accessibility
- * services. From the perspective of an accessibility service the
- * {@link AccessibilityNodeInfo}s it receives while exploring the sub-tree
- * rooted at this View will be the same as the ones it received while
- * exploring a View containing a sub-tree composed of real Views.
- */
- private class VirtualDescendantsProvider extends AccessibilityNodeProvider {
-
- /**
- * {@inheritDoc}
- */
- @Override
- public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
- AccessibilityNodeInfo info = null;
- if (virtualViewId == View.NO_ID) {
- // We are requested to create an AccessibilityNodeInfo describing
- // this View, i.e. the root of the virtual sub-tree. Note that the
- // host View has an AccessibilityNodeProvider which means that this
- // provider is responsible for creating the node info for that root.
- info = AccessibilityNodeInfo.obtain(VirtualSubtreeRootView.this);
- onInitializeAccessibilityNodeInfo(info);
- // Add the virtual children of the root View.
- List<VirtualView> children = mChildren;
- final int childCount = children.size();
- for (int i = 0; i < childCount; i++) {
- VirtualView child = children.get(i);
- info.addChild(VirtualSubtreeRootView.this, child.mId);
- }
- } else {
- // Find the view that corresponds to the given id.
- VirtualView virtualView = findVirtualViewById(virtualViewId);
- if (virtualView == null) {
- return null;
- }
- // Obtain and initialize an AccessibilityNodeInfo with
- // information about the virtual view.
- info = AccessibilityNodeInfo.obtain();
- info.addAction(AccessibilityNodeInfo.ACTION_SELECT);
- info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION);
- info.setPackageName(getContext().getPackageName());
- info.setClassName(virtualView.getClass().getName());
- info.setSource(VirtualSubtreeRootView.this, virtualViewId);
- info.setBoundsInParent(virtualView.mBounds);
- info.setParent(VirtualSubtreeRootView.this);
- info.setText(virtualView.mText);
- }
- return info;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String searched,
- int virtualViewId) {
- if (TextUtils.isEmpty(searched)) {
- return Collections.emptyList();
- }
- String searchedLowerCase = searched.toLowerCase();
- List<AccessibilityNodeInfo> result = null;
- if (virtualViewId == View.NO_ID) {
- // If the search is from the root, i.e. this View, go over the virtual
- // children and look for ones that contain the searched string since
- // this View does not contain text itself.
- List<VirtualView> children = mChildren;
- final int childCount = children.size();
- for (int i = 0; i < childCount; i++) {
- VirtualView child = children.get(i);
- String textToLowerCase = child.mText.toLowerCase();
- if (textToLowerCase.contains(searchedLowerCase)) {
- if (result == null) {
- result = new ArrayList<AccessibilityNodeInfo>();
- }
- result.add(createAccessibilityNodeInfo(child.mId));
- }
- }
- } else {
- // If the search is from a virtual view, find the view. Since the tree
- // is one level deep we add a node info for the child to the result if
- // the child contains the searched text.
- VirtualView virtualView = findVirtualViewById(virtualViewId);
- if (virtualView != null) {
- String textToLowerCase = virtualView.mText.toLowerCase();
- if (textToLowerCase.contains(searchedLowerCase)) {
- result = new ArrayList<AccessibilityNodeInfo>();
- result.add(createAccessibilityNodeInfo(virtualViewId));
- }
- }
- }
- if (result == null) {
- return Collections.emptyList();
- }
- return result;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean performAction(int virtualViewId, int action, Bundle arguments) {
- if (virtualViewId == View.NO_ID) {
- // Perform the action on the host View.
- switch (action) {
- case AccessibilityNodeInfo.ACTION_SELECT:
- if (!isSelected()) {
- setSelected(true);
- return isSelected();
- }
- break;
- case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION:
- if (isSelected()) {
- setSelected(false);
- return !isSelected();
- }
- break;
- }
- } else {
- // Find the view that corresponds to the given id.
- VirtualView child = findVirtualViewById(virtualViewId);
- if (child == null) {
- return false;
- }
- // Perform the action on a virtual view.
- switch (action) {
- case AccessibilityNodeInfo.ACTION_SELECT:
- setVirtualViewSelected(child, true);
- invalidate();
- return true;
- case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION:
- setVirtualViewSelected(child, false);
- invalidate();
- return true;
- }
- }
- return false;
- }
- }
- }
-}
diff --git a/samples/ApiDemos/src/com/example/android/apis/accessibility/_index.html b/samples/ApiDemos/src/com/example/android/apis/accessibility/_index.html
index df54e966b..1324f8682 100644
--- a/samples/ApiDemos/src/com/example/android/apis/accessibility/_index.html
+++ b/samples/ApiDemos/src/com/example/android/apis/accessibility/_index.html
@@ -21,12 +21,6 @@
xml files, and adding additional information to AccessibilityEvents using
AccessibilityRecords.
</dd>
- <dt><a href="AccessibilityNodeProviderActivity.html">Accessibility Node Provider</a></dt>
- <dd>Demonstrates how to develop an accessibility node provider which manages a virtual
- View tree reported to accessibility services. The virtual subtree is rooted at a View
- that draws complex content and reports itself as a tree of virtual views, thus conveying
- its logical structure.
- </dd>
</dl>
<dl>
diff --git a/samples/ApiDemos/src/com/example/android/apis/animation/Transitions.java b/samples/ApiDemos/src/com/example/android/apis/animation/Transitions.java
new file mode 100644
index 000000000..4878c5f19
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/animation/Transitions.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.apis.animation;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.transition.Scene;
+import android.transition.TransitionInflater;
+import android.transition.TransitionManager;
+import com.example.android.apis.R;
+
+/**
+ * This application demonstrates some of the capabilities and uses of the
+ * {@link android.transition transitions} APIs. Scenes and a TransitionManager
+ * are loaded from resource files and transitions are run between those scenes
+ * as well as a dynamically-configured scene.
+ */
+public class Transitions extends Activity {
+
+ Scene mScene1, mScene2, mScene3;
+ ViewGroup mSceneRoot;
+ TransitionManager mTransitionManager;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.transition);
+
+ mSceneRoot = (ViewGroup) findViewById(R.id.sceneRoot);
+
+ TransitionInflater inflater = TransitionInflater.from(this);
+
+ // Note that this is not the only way to create a Scene object, but that
+ // loading them from layout resources cooperates with the
+ // TransitionManager that we are also loading from resources, and which
+ // uses the same layout resource files to determine the scenes to transition
+ // from/to.
+ mScene1 = Scene.getSceneForLayout(mSceneRoot, R.layout.transition_scene1, this);
+ mScene2 = Scene.getSceneForLayout(mSceneRoot, R.layout.transition_scene2, this);
+ mScene3 = Scene.getSceneForLayout(mSceneRoot, R.layout.transition_scene3, this);
+ mTransitionManager = inflater.inflateTransitionManager(R.transition.transitions_mgr,
+ mSceneRoot);
+ }
+
+ public void selectScene(View view) {
+ switch (view.getId()) {
+ case R.id.scene1:
+ mTransitionManager.transitionTo(mScene1);
+ break;
+ case R.id.scene2:
+ mTransitionManager.transitionTo(mScene2);
+ break;
+ case R.id.scene3:
+ mTransitionManager.transitionTo(mScene3);
+ break;
+ case R.id.scene4:
+ // scene4 is not an actual 'Scene', but rather a dynamic change in the UI,
+ // transitioned to using beginDelayedTransition() to tell the TransitionManager
+ // to get ready to run a transition at the next frame
+ TransitionManager.beginDelayedTransition(mSceneRoot);
+ setNewSize(R.id.view1, 150, 25);
+ setNewSize(R.id.view2, 150, 25);
+ setNewSize(R.id.view3, 150, 25);
+ setNewSize(R.id.view4, 150, 25);
+ break;
+ }
+ }
+
+ private void setNewSize(int id, int width, int height) {
+ View view = findViewById(id);
+ ViewGroup.LayoutParams params = view.getLayoutParams();
+ params.width = width;
+ params.height = height;
+ view.setLayoutParams(params);
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/ActionBarDisplayOptions.java b/samples/ApiDemos/src/com/example/android/apis/app/ActionBarDisplayOptions.java
index 5585c91a3..73d8db967 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/ActionBarDisplayOptions.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/ActionBarDisplayOptions.java
@@ -46,6 +46,8 @@ public class ActionBarDisplayOptions extends Activity
findViewById(R.id.toggle_show_custom).setOnClickListener(this);
findViewById(R.id.toggle_navigation).setOnClickListener(this);
findViewById(R.id.cycle_custom_gravity).setOnClickListener(this);
+ findViewById(R.id.toggle_visibility).setOnClickListener(this);
+ findViewById(R.id.toggle_system_ui).setOnClickListener(this);
mCustomView = getLayoutInflater().inflate(R.layout.action_bar_display_options_custom, null);
// Configure several action bar elements that will be toggled by display options.
@@ -93,20 +95,36 @@ public class ActionBarDisplayOptions extends Activity
case R.id.cycle_custom_gravity:
ActionBar.LayoutParams lp = (ActionBar.LayoutParams) mCustomView.getLayoutParams();
int newGravity = 0;
- switch (lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.LEFT:
+ switch (lp.gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.START:
newGravity = Gravity.CENTER_HORIZONTAL;
break;
case Gravity.CENTER_HORIZONTAL:
- newGravity = Gravity.RIGHT;
+ newGravity = Gravity.END;
break;
- case Gravity.RIGHT:
- newGravity = Gravity.LEFT;
+ case Gravity.END:
+ newGravity = Gravity.START;
break;
}
- lp.gravity = lp.gravity & ~Gravity.HORIZONTAL_GRAVITY_MASK | newGravity;
+ lp.gravity = lp.gravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK | newGravity;
bar.setCustomView(mCustomView, lp);
return;
+ case R.id.toggle_visibility:
+ if (bar.isShowing()) {
+ bar.hide();
+ } else {
+ bar.show();
+ }
+ return;
+ case R.id.toggle_system_ui:
+ if ((getWindow().getDecorView().getSystemUiVisibility()
+ & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) {
+ getWindow().getDecorView().setSystemUiVisibility(0);
+ } else {
+ getWindow().getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_FULLSCREEN);
+ }
+ return;
}
int change = bar.getDisplayOptions() ^ flags;
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/AppUpdateSspReceiver.java b/samples/ApiDemos/src/com/example/android/apis/app/AppUpdateSspReceiver.java
new file mode 100644
index 000000000..dfa265c64
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/AppUpdateSspReceiver.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.app;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.Toast;
+
+/**
+ * Executed when a new version of the application is is installed.
+ */
+public class AppUpdateSspReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String msg = "Ssp update received: " + intent.getData();
+ Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.java b/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.java
index 324b8ce6a..db501859e 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.java
@@ -68,6 +68,9 @@ public class DeviceAdminSample extends PreferenceActivity {
// The following keys are used to find each preference item
private static final String KEY_ENABLE_ADMIN = "key_enable_admin";
private static final String KEY_DISABLE_CAMERA = "key_disable_camera";
+ private static final String KEY_DISABLE_KEYGUARD_WIDGETS = "key_disable_keyguard_widgets";
+ private static final String KEY_DISABLE_KEYGUARD_SECURE_CAMERA
+ = "key_disable_keyguard_secure_camera";
private static final String KEY_CATEGORY_QUALITY = "key_category_quality";
private static final String KEY_SET_PASSWORD = "key_set_password";
@@ -245,6 +248,8 @@ public class DeviceAdminSample extends PreferenceActivity {
// UI elements
private CheckBoxPreference mEnableCheckbox;
private CheckBoxPreference mDisableCameraCheckbox;
+ private CheckBoxPreference mDisableKeyguardWidgetsCheckbox;
+ private CheckBoxPreference mDisableKeyguardSecureCameraCheckbox;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -254,6 +259,12 @@ public class DeviceAdminSample extends PreferenceActivity {
mEnableCheckbox.setOnPreferenceChangeListener(this);
mDisableCameraCheckbox = (CheckBoxPreference) findPreference(KEY_DISABLE_CAMERA);
mDisableCameraCheckbox.setOnPreferenceChangeListener(this);
+ mDisableKeyguardWidgetsCheckbox =
+ (CheckBoxPreference) findPreference(KEY_DISABLE_KEYGUARD_WIDGETS);
+ mDisableKeyguardWidgetsCheckbox.setOnPreferenceChangeListener(this);
+ mDisableKeyguardSecureCameraCheckbox =
+ (CheckBoxPreference) findPreference(KEY_DISABLE_KEYGUARD_SECURE_CAMERA);
+ mDisableKeyguardSecureCameraCheckbox.setOnPreferenceChangeListener(this);
}
// At onResume time, reload UI with current values as required
@@ -265,10 +276,20 @@ public class DeviceAdminSample extends PreferenceActivity {
if (mAdminActive) {
mDPM.setCameraDisabled(mDeviceAdminSample, mDisableCameraCheckbox.isChecked());
+ mDPM.setKeyguardDisabledFeatures(mDeviceAdminSample, createKeyguardDisabledFlag());
reloadSummaries();
}
}
+ int createKeyguardDisabledFlag() {
+ int flags = DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
+ flags |= mDisableKeyguardWidgetsCheckbox.isChecked() ?
+ DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL : 0;
+ flags |= mDisableKeyguardSecureCameraCheckbox.isChecked() ?
+ DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA : 0;
+ return flags;
+ }
+
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (super.onPreferenceChange(preference, newValue)) {
@@ -295,6 +316,10 @@ public class DeviceAdminSample extends PreferenceActivity {
} else if (preference == mDisableCameraCheckbox) {
mDPM.setCameraDisabled(mDeviceAdminSample, value);
reloadSummaries();
+ } else if (preference == mDisableKeyguardWidgetsCheckbox
+ || preference == mDisableKeyguardSecureCameraCheckbox) {
+ mDPM.setKeyguardDisabledFeatures(mDeviceAdminSample, createKeyguardDisabledFlag());
+ reloadSummaries();
}
return true;
}
@@ -305,11 +330,25 @@ public class DeviceAdminSample extends PreferenceActivity {
String cameraSummary = getString(mDPM.getCameraDisabled(mDeviceAdminSample)
? R.string.camera_disabled : R.string.camera_enabled);
mDisableCameraCheckbox.setSummary(cameraSummary);
+
+ int disabled = mDPM.getKeyguardDisabledFeatures(mDeviceAdminSample);
+
+ String keyguardWidgetSummary = getString(
+ (disabled & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0 ?
+ R.string.keyguard_widgets_disabled : R.string.keyguard_widgets_enabled);
+ mDisableKeyguardWidgetsCheckbox.setSummary(keyguardWidgetSummary);
+
+ String keyguardSecureCameraSummary = getString(
+ (disabled & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0 ?
+ R.string.keyguard_secure_camera_disabled : R.string.keyguard_secure_camera_enabled);
+ mDisableKeyguardSecureCameraCheckbox.setSummary(keyguardSecureCameraSummary);
}
/** Updates the device capabilities area (dis/enabling) as the admin is (de)activated */
private void enableDeviceCapabilitiesArea(boolean enabled) {
mDisableCameraCheckbox.setEnabled(enabled);
+ mDisableKeyguardWidgetsCheckbox.setEnabled(enabled);
+ mDisableKeyguardSecureCameraCheckbox.setEnabled(enabled);
}
}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/DoNothing.java b/samples/ApiDemos/src/com/example/android/apis/app/DoNothing.java
new file mode 100644
index 000000000..133a80260
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/DoNothing.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.app;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class DoNothing extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentArgumentsFragment.java b/samples/ApiDemos/src/com/example/android/apis/app/FragmentArgumentsFragment.java
new file mode 100644
index 000000000..7023c4380
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/FragmentArgumentsFragment.java
@@ -0,0 +1,53 @@
+/*
+ * 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.apis.app;
+
+import com.example.android.apis.R;
+
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Demonstrates a fragment that can be configured through both Bundle arguments
+ * and layout attributes.
+ */
+public class FragmentArgumentsFragment extends Fragment {
+ @Override public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState == null) {
+ // First-time init; create fragment to embed in activity.
+ FragmentTransaction ft = getChildFragmentManager().beginTransaction();
+ Fragment newFragment = FragmentArguments.MyFragment.newInstance("From Arguments 1");
+ ft.add(R.id.created1, newFragment);
+ newFragment = FragmentArguments.MyFragment.newInstance("From Arguments 2");
+ ft.add(R.id.created2, newFragment);
+ ft.commit();
+ }
+ }
+
+ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_arguments_fragment, container, false);
+ return v;
+ }
+//END_INCLUDE(create)
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentContextMenu.java b/samples/ApiDemos/src/com/example/android/apis/app/FragmentContextMenu.java
index 6bc73e038..420e67f35 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentContextMenu.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/FragmentContextMenu.java
@@ -29,6 +29,7 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.Toast;
/**
* Demonstration of displaying a context menu from a fragment.
@@ -65,10 +66,10 @@ public class FragmentContextMenu extends Activity {
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.a_item:
- Log.i("ContextMenu", "Item 1a was chosen");
+ Toast.makeText(getActivity(), "Item 1a was chosen", Toast.LENGTH_SHORT).show();
return true;
case R.id.b_item:
- Log.i("ContextMenu", "Item 1b was chosen");
+ Toast.makeText(getActivity(), "Item 1b was chosen", Toast.LENGTH_SHORT).show();
return true;
}
return super.onContextItemSelected(item);
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java b/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java
index 730f4d44b..572173fb2 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java
@@ -150,7 +150,11 @@ public class FragmentLayout extends Activity {
// Execute a transaction, replacing any existing fragment
// with this one inside the frame.
FragmentTransaction ft = getFragmentManager().beginTransaction();
- ft.replace(R.id.details, details);
+ if (index == 0) {
+ ft.replace(R.id.details, details);
+ } else {
+ ft.replace(R.id.a_item, details);
+ }
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentMenuFragment.java b/samples/ApiDemos/src/com/example/android/apis/app/FragmentMenuFragment.java
new file mode 100644
index 000000000..6ac700355
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/FragmentMenuFragment.java
@@ -0,0 +1,95 @@
+/*
+ * 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.apis.app;
+
+import com.example.android.apis.R;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+
+/**
+ * Demonstrates how fragments can participate in the options menu.
+ */
+public class FragmentMenuFragment extends Fragment {
+ Fragment mFragment1;
+ Fragment mFragment2;
+ CheckBox mCheckBox1;
+ CheckBox mCheckBox2;
+
+ // Update fragment visibility when check boxes are changed.
+ final OnClickListener mClickListener = new OnClickListener() {
+ public void onClick(View v) {
+ updateFragmentVisibility();
+ }
+ };
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_menu, container, false);
+
+ // Make sure the two menu fragments are created.
+ FragmentManager fm = getChildFragmentManager();
+ FragmentTransaction ft = fm.beginTransaction();
+ mFragment1 = fm.findFragmentByTag("f1");
+ if (mFragment1 == null) {
+ mFragment1 = new FragmentMenu.MenuFragment();
+ ft.add(mFragment1, "f1");
+ }
+ mFragment2 = fm.findFragmentByTag("f2");
+ if (mFragment2 == null) {
+ mFragment2 = new FragmentMenu.Menu2Fragment();
+ ft.add(mFragment2, "f2");
+ }
+ ft.commit();
+
+ // Watch check box clicks.
+ mCheckBox1 = (CheckBox)v.findViewById(R.id.menu1);
+ mCheckBox1.setOnClickListener(mClickListener);
+ mCheckBox2 = (CheckBox)v.findViewById(R.id.menu2);
+ mCheckBox2.setOnClickListener(mClickListener);
+
+ // Make sure fragments start out with correct visibility.
+ updateFragmentVisibility();
+
+ return v;
+ }
+
+ @Override
+ public void onViewStateRestored(Bundle savedInstanceState) {
+ super.onViewStateRestored(savedInstanceState);
+ // Make sure fragments are updated after check box view state is restored.
+ updateFragmentVisibility();
+ }
+
+ // Update fragment visibility based on current check box state.
+ void updateFragmentVisibility() {
+ FragmentTransaction ft = getChildFragmentManager().beginTransaction();
+ if (mCheckBox1.isChecked()) ft.show(mFragment1);
+ else ft.hide(mFragment1);
+ if (mCheckBox2.isChecked()) ft.show(mFragment2);
+ else ft.hide(mFragment2);
+ ft.commit();
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentNestingTabs.java b/samples/ApiDemos/src/com/example/android/apis/app/FragmentNestingTabs.java
new file mode 100644
index 000000000..4331c2aec
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/FragmentNestingTabs.java
@@ -0,0 +1,118 @@
+/*
+ * 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.apis.app;
+
+//BEGIN_INCLUDE(complete)
+import android.app.ActionBar;
+import android.app.ActionBar.Tab;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.os.Bundle;
+import android.widget.Toast;
+
+/**
+ * This demonstrates the use of action bar tabs and how they interact
+ * with other action bar features.
+ */
+public class FragmentNestingTabs extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ FragmentManager.enableDebugLogging(true);
+ super.onCreate(savedInstanceState);
+
+ final ActionBar bar = getActionBar();
+ bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+ bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
+
+ bar.addTab(bar.newTab()
+ .setText("Menus")
+ .setTabListener(new TabListener<FragmentMenuFragment>(
+ this, "menus", FragmentMenuFragment.class)));
+ bar.addTab(bar.newTab()
+ .setText("Args")
+ .setTabListener(new TabListener<FragmentArgumentsFragment>(
+ this, "args", FragmentArgumentsFragment.class)));
+ bar.addTab(bar.newTab()
+ .setText("Stack")
+ .setTabListener(new TabListener<FragmentStackFragment>(
+ this, "stack", FragmentStackFragment.class)));
+ bar.addTab(bar.newTab()
+ .setText("Tabs")
+ .setTabListener(new TabListener<FragmentTabsFragment>(
+ this, "tabs", FragmentTabsFragment.class)));
+
+ if (savedInstanceState != null) {
+ bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
+ }
+
+ public static class TabListener<T extends Fragment> implements ActionBar.TabListener {
+ private final Activity mActivity;
+ private final String mTag;
+ private final Class<T> mClass;
+ private final Bundle mArgs;
+ private Fragment mFragment;
+
+ public TabListener(Activity activity, String tag, Class<T> clz) {
+ this(activity, tag, clz, null);
+ }
+
+ public TabListener(Activity activity, String tag, Class<T> clz, Bundle args) {
+ mActivity = activity;
+ mTag = tag;
+ mClass = clz;
+ mArgs = args;
+
+ // Check to see if we already have a fragment for this tab, probably
+ // from a previously saved state. If so, deactivate it, because our
+ // initial state is that a tab isn't shown.
+ mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);
+ if (mFragment != null && !mFragment.isDetached()) {
+ FragmentTransaction ft = mActivity.getFragmentManager().beginTransaction();
+ ft.detach(mFragment);
+ ft.commit();
+ }
+ }
+
+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
+ if (mFragment == null) {
+ mFragment = Fragment.instantiate(mActivity, mClass.getName(), mArgs);
+ ft.add(android.R.id.content, mFragment, mTag);
+ } else {
+ ft.attach(mFragment);
+ }
+ }
+
+ public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+ if (mFragment != null) {
+ ft.detach(mFragment);
+ }
+ }
+
+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ Toast.makeText(mActivity, "Reselected!", Toast.LENGTH_SHORT).show();
+ }
+ }
+}
+//END_INCLUDE(complete)
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentStack.java b/samples/ApiDemos/src/com/example/android/apis/app/FragmentStack.java
index 891eda486..242d67074 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentStack.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/FragmentStack.java
@@ -44,6 +44,12 @@ public class FragmentStack extends Activity {
addFragmentToStack();
}
});
+ button = (Button)findViewById(R.id.delete_fragment);
+ button.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ getFragmentManager().popBackStack();
+ }
+ });
if (savedInstanceState == null) {
// Do first time initialization -- add initial fragment.
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentStackFragment.java b/samples/ApiDemos/src/com/example/android/apis/app/FragmentStackFragment.java
new file mode 100644
index 000000000..d33af64a8
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/FragmentStackFragment.java
@@ -0,0 +1,89 @@
+/*
+ * 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.apis.app;
+
+import com.example.android.apis.R;
+
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+public class FragmentStackFragment extends Fragment {
+ int mStackLevel = 1;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState == null) {
+ // Do first time initialization -- add initial fragment.
+ Fragment newFragment = FragmentStack.CountingFragment.newInstance(mStackLevel);
+ FragmentTransaction ft = getChildFragmentManager().beginTransaction();
+ ft.add(R.id.simple_fragment, newFragment).commit();
+ } else {
+ mStackLevel = savedInstanceState.getInt("level");
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_stack, container, false);
+
+ // Watch for button clicks.
+ Button button = (Button)v.findViewById(R.id.new_fragment);
+ button.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ addFragmentToStack();
+ }
+ });
+ button = (Button)v.findViewById(R.id.delete_fragment);
+ button.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ getChildFragmentManager().popBackStack();
+ }
+ });
+
+ return v;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt("level", mStackLevel);
+ }
+
+ void addFragmentToStack() {
+ mStackLevel++;
+
+ // Instantiate a new fragment.
+ Fragment newFragment = FragmentStack.CountingFragment.newInstance(mStackLevel);
+
+ // Add the fragment to the activity, pushing this transaction
+ // on to the back stack.
+ FragmentTransaction ft = getChildFragmentManager().beginTransaction();
+ ft.replace(R.id.simple_fragment, newFragment);
+ ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
+ ft.addToBackStack(null);
+ ft.commit();
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabsFragment.java b/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabsFragment.java
new file mode 100644
index 000000000..1d45e4dbe
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabsFragment.java
@@ -0,0 +1,251 @@
+/*
+ * 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.apis.app;
+
+import java.util.ArrayList;
+
+import com.example.android.apis.R;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TabHost;
+
+/**
+ * Sample fragment that contains tabs of other fragments.
+ */
+public class FragmentTabsFragment extends Fragment {
+ TabManager mTabManager;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mTabManager = new TabManager(getActivity(), getChildFragmentManager(),
+ R.id.realtabcontent);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_tabs_fragment, container, false);
+ TabHost host = mTabManager.handleCreateView(v);
+
+ mTabManager.addTab(host.newTabSpec("result").setIndicator("Result"),
+ FragmentReceiveResult.ReceiveResultFragment.class, null);
+ mTabManager.addTab(host.newTabSpec("contacts").setIndicator("Contacts"),
+ LoaderCursor.CursorLoaderListFragment.class, null);
+ mTabManager.addTab(host.newTabSpec("apps").setIndicator("Apps"),
+ LoaderCustom.AppListFragment.class, null);
+ mTabManager.addTab(host.newTabSpec("throttle").setIndicator("Throttle"),
+ LoaderThrottle.ThrottledLoaderListFragment.class, null);
+
+ return v;
+ }
+
+ @Override
+ public void onViewStateRestored(Bundle savedInstanceState) {
+ super.onViewStateRestored(savedInstanceState);
+ mTabManager.handleViewStateRestored(savedInstanceState);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mTabManager.handleDestroyView();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mTabManager.handleSaveInstanceState(outState);
+ }
+
+ /**
+ * This is a helper class that implements a generic mechanism for
+ * associating fragments with the tabs in a tab host. DO NOT USE THIS.
+ * If you want tabs in a fragment, use the support v13 library's
+ * FragmentTabHost class, which takes care of all of this for you (in
+ * a simpler way even).
+ */
+ public static class TabManager implements TabHost.OnTabChangeListener {
+ private final Context mContext;
+ private final FragmentManager mManager;
+ private final int mContainerId;
+ private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
+ private TabHost mTabHost;
+ private TabInfo mLastTab;
+ private boolean mInitialized;
+ private String mCurrentTabTag;
+
+ static final class TabInfo {
+ private final String tag;
+ private final Class<?> clss;
+ private final Bundle args;
+ private Fragment fragment;
+
+ TabInfo(String _tag, Class<?> _class, Bundle _args) {
+ tag = _tag;
+ clss = _class;
+ args = _args;
+ }
+ }
+
+ static class DummyTabFactory implements TabHost.TabContentFactory {
+ private final Context mContext;
+
+ public DummyTabFactory(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public View createTabContent(String tag) {
+ View v = new View(mContext);
+ v.setMinimumWidth(0);
+ v.setMinimumHeight(0);
+ return v;
+ }
+ }
+
+ public TabManager(Context context, FragmentManager manager, int containerId) {
+ mContext = context;
+ mManager = manager;
+ mContainerId = containerId;
+ }
+
+ public TabHost handleCreateView(View root) {
+ if (mTabHost != null) {
+ throw new IllegalStateException("TabHost already set");
+ }
+ mTabHost = (TabHost)root.findViewById(android.R.id.tabhost);
+ mTabHost.setup();
+ mTabHost.setOnTabChangedListener(this);
+ return mTabHost;
+ }
+
+ public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
+ tabSpec.setContent(new DummyTabFactory(mContext));
+ String tag = tabSpec.getTag();
+ TabInfo info = new TabInfo(tag, clss, args);
+ mTabs.add(info);
+ mTabHost.addTab(tabSpec);
+ }
+
+ public void handleViewStateRestored(Bundle savedInstanceState) {
+ if (savedInstanceState != null) {
+ mCurrentTabTag = savedInstanceState.getString("tab");
+ }
+ mTabHost.setCurrentTabByTag(mCurrentTabTag);
+
+ String currentTab = mTabHost.getCurrentTabTag();
+
+ // Go through all tabs and make sure their fragments match
+ // the correct state.
+ FragmentTransaction ft = null;
+ for (int i=0; i<mTabs.size(); i++) {
+ TabInfo tab = mTabs.get(i);
+ tab.fragment = mManager.findFragmentByTag(tab.tag);
+ if (tab.fragment != null && !tab.fragment.isDetached()) {
+ if (tab.tag.equals(currentTab)) {
+ // The fragment for this tab is already there and
+ // active, and it is what we really want to have
+ // as the current tab. Nothing to do.
+ mLastTab = tab;
+ } else {
+ // This fragment was restored in the active state,
+ // but is not the current tab. Deactivate it.
+ if (ft == null) {
+ ft = mManager.beginTransaction();
+ }
+ ft.detach(tab.fragment);
+ }
+ }
+ }
+
+ // We are now ready to go. Make sure we are switched to the
+ // correct tab.
+ mInitialized = true;
+ ft = doTabChanged(currentTab, ft);
+ if (ft != null) {
+ ft.commit();
+ mManager.executePendingTransactions();
+ }
+ }
+
+ public void handleDestroyView() {
+ mCurrentTabTag = mTabHost.getCurrentTabTag();
+ mTabHost = null;
+ mTabs.clear();
+ mInitialized = false;
+ }
+
+ public void handleSaveInstanceState(Bundle outState) {
+ outState.putString("tab", mTabHost != null
+ ? mTabHost.getCurrentTabTag() : mCurrentTabTag);
+ }
+
+ @Override
+ public void onTabChanged(String tabId) {
+ if (!mInitialized) {
+ return;
+ }
+ FragmentTransaction ft = doTabChanged(tabId, null);
+ if (ft != null) {
+ ft.commit();
+ }
+ }
+
+ private FragmentTransaction doTabChanged(String tabId, FragmentTransaction ft) {
+ TabInfo newTab = null;
+ for (int i=0; i<mTabs.size(); i++) {
+ TabInfo tab = mTabs.get(i);
+ if (tab.tag.equals(tabId)) {
+ newTab = tab;
+ }
+ }
+ if (newTab == null) {
+ throw new IllegalStateException("No tab known for tag " + tabId);
+ }
+ if (mLastTab != newTab) {
+ if (ft == null) {
+ ft = mManager.beginTransaction();
+ }
+ if (mLastTab != null) {
+ if (mLastTab.fragment != null) {
+ ft.detach(mLastTab.fragment);
+ }
+ }
+ if (newTab != null) {
+ if (newTab.fragment == null) {
+ newTab.fragment = Fragment.instantiate(mContext,
+ newTab.clss.getName(), newTab.args);
+ ft.add(mContainerId, newTab.fragment, newTab.tag);
+ } else {
+ ft.attach(newTab.fragment);
+ }
+ }
+
+ mLastTab = newTab;
+ }
+ return ft;
+ }
+ }
+}
+//END_INCLUDE(complete)
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/Intents.java b/samples/ApiDemos/src/com/example/android/apis/app/Intents.java
index 8f02b8322..6207dd805 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/Intents.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/Intents.java
@@ -31,17 +31,23 @@ public class Intents extends Activity {
super.onCreate(savedInstanceState);
setContentView(R.layout.intents);
+ }
+
+ public void onGetMusic(View view) {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType("audio/*");
+ startActivity(Intent.createChooser(intent, "Select music"));
+ }
- // Watch for button clicks.
- Button button = (Button)findViewById(R.id.get_music);
- button.setOnClickListener(mGetMusicListener);
+ public void onGetImage(View view) {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType("image/*");
+ startActivity(Intent.createChooser(intent, "Select image"));
}
- private OnClickListener mGetMusicListener = new OnClickListener() {
- public void onClick(View v) {
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
- intent.setType("audio/*");
- startActivity(Intent.createChooser(intent, "Select music"));
- }
- };
+ public void onGetStream(View view) {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType("*/*");
+ startActivity(Intent.createChooser(intent, "Select stream"));
+ }
}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.java b/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.java
index f30919146..4d2a4d057 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.java
@@ -20,6 +20,7 @@ import android.app.Activity;
import android.app.FragmentManager;
import android.app.ListFragment;
import android.app.LoaderManager;
+import android.content.Context;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
@@ -34,6 +35,7 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import android.widget.SearchView;
+import android.widget.SearchView.OnCloseListener;
import android.widget.SimpleCursorAdapter;
import android.widget.SearchView.OnQueryTextListener;
@@ -58,11 +60,15 @@ public class LoaderCursor extends Activity {
//BEGIN_INCLUDE(fragment_cursor)
public static class CursorLoaderListFragment extends ListFragment
- implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {
+ implements OnQueryTextListener, OnCloseListener,
+ LoaderManager.LoaderCallbacks<Cursor> {
// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;
+ // The SearchView for doing filtering.
+ SearchView mSearchView;
+
// If non-null, this is the current filter the user has provided.
String mCurFilter;
@@ -91,15 +97,31 @@ public class LoaderCursor extends Activity {
getLoaderManager().initLoader(0, null, this);
}
+ public static class MySearchView extends SearchView {
+ public MySearchView(Context context) {
+ super(context);
+ }
+
+ // The normal SearchView doesn't clear its search text when
+ // collapsed, so we will do this for it.
+ @Override
+ public void onActionViewCollapsed() {
+ setQuery("", false);
+ super.onActionViewCollapsed();
+ }
+ }
+
@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Place an action bar item for searching.
MenuItem item = menu.add("Search");
item.setIcon(android.R.drawable.ic_menu_search);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
| MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
- SearchView sv = new SearchView(getActivity());
- sv.setOnQueryTextListener(this);
- item.setActionView(sv);
+ mSearchView = new MySearchView(getActivity());
+ mSearchView.setOnQueryTextListener(this);
+ mSearchView.setOnCloseListener(this);
+ mSearchView.setIconifiedByDefault(true);
+ item.setActionView(mSearchView);
}
public boolean onQueryTextChange(String newText) {
@@ -125,6 +147,14 @@ public class LoaderCursor extends Activity {
return true;
}
+ @Override
+ public boolean onClose() {
+ if (!TextUtils.isEmpty(mSearchView.getQuery())) {
+ mSearchView.setQuery(null, true);
+ }
+ return true;
+ }
+
@Override public void onListItemClick(ListView l, View v, int position, long id) {
// Insert desired behavior here.
Log.i("FragmentComplexList", "Item clicked: " + id);
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java b/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
index 7c16fb359..ffcf890fb 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
@@ -17,6 +17,7 @@
package com.example.android.apis.app;
import com.example.android.apis.R;
+import com.example.android.apis.app.LoaderCursor.CursorLoaderListFragment.MySearchView;
import java.io.File;
import java.text.Collator;
@@ -55,6 +56,7 @@ import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SearchView;
import android.widget.TextView;
+import android.widget.SearchView.OnCloseListener;
import android.widget.SearchView.OnQueryTextListener;
/**
@@ -397,11 +399,15 @@ public class LoaderCustom extends Activity {
}
public static class AppListFragment extends ListFragment
- implements OnQueryTextListener, LoaderManager.LoaderCallbacks<List<AppEntry>> {
+ implements OnQueryTextListener, OnCloseListener,
+ LoaderManager.LoaderCallbacks<List<AppEntry>> {
// This is the Adapter being used to display the list's data.
AppListAdapter mAdapter;
+ // The SearchView for doing filtering.
+ SearchView mSearchView;
+
// If non-null, this is the current filter the user has provided.
String mCurFilter;
@@ -427,15 +433,31 @@ public class LoaderCustom extends Activity {
getLoaderManager().initLoader(0, null, this);
}
+ public static class MySearchView extends SearchView {
+ public MySearchView(Context context) {
+ super(context);
+ }
+
+ // The normal SearchView doesn't clear its search text when
+ // collapsed, so we will do this for it.
+ @Override
+ public void onActionViewCollapsed() {
+ setQuery("", false);
+ super.onActionViewCollapsed();
+ }
+ }
+
@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Place an action bar item for searching.
MenuItem item = menu.add("Search");
item.setIcon(android.R.drawable.ic_menu_search);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
| MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
- SearchView sv = new SearchView(getActivity());
- sv.setOnQueryTextListener(this);
- item.setActionView(sv);
+ mSearchView = new MySearchView(getActivity());
+ mSearchView.setOnQueryTextListener(this);
+ mSearchView.setOnCloseListener(this);
+ mSearchView.setIconifiedByDefault(true);
+ item.setActionView(mSearchView);
}
@Override public boolean onQueryTextChange(String newText) {
@@ -451,6 +473,14 @@ public class LoaderCustom extends Activity {
return true;
}
+ @Override
+ public boolean onClose() {
+ if (!TextUtils.isEmpty(mSearchView.getQuery())) {
+ mSearchView.setQuery(null, true);
+ }
+ return true;
+ }
+
@Override public void onListItemClick(ListView l, View v, int position, long id) {
// Insert desired behavior here.
Log.i("LoaderCustom", "Item clicked: " + id);
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/LoaderRetained.java b/samples/ApiDemos/src/com/example/android/apis/app/LoaderRetained.java
index 521f6e5b2..7ddb737d0 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/LoaderRetained.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/LoaderRetained.java
@@ -16,10 +16,13 @@
package com.example.android.apis.app;
+import com.example.android.apis.app.LoaderCursor.CursorLoaderListFragment.MySearchView;
+
import android.app.Activity;
import android.app.FragmentManager;
import android.app.ListFragment;
import android.app.LoaderManager;
+import android.content.Context;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
@@ -35,6 +38,7 @@ import android.view.View;
import android.widget.ListView;
import android.widget.SearchView;
import android.widget.SimpleCursorAdapter;
+import android.widget.SearchView.OnCloseListener;
import android.widget.SearchView.OnQueryTextListener;
/**
@@ -58,11 +62,15 @@ public class LoaderRetained extends Activity {
//BEGIN_INCLUDE(fragment_cursor)
public static class CursorLoaderListFragment extends ListFragment
- implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {
+ implements OnQueryTextListener, OnCloseListener,
+ LoaderManager.LoaderCallbacks<Cursor> {
// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;
+ // The SearchView for doing filtering.
+ SearchView mSearchView;
+
// If non-null, this is the current filter the user has provided.
String mCurFilter;
@@ -94,15 +102,31 @@ public class LoaderRetained extends Activity {
getLoaderManager().initLoader(0, null, this);
}
+ public static class MySearchView extends SearchView {
+ public MySearchView(Context context) {
+ super(context);
+ }
+
+ // The normal SearchView doesn't clear its search text when
+ // collapsed, so we will do this for it.
+ @Override
+ public void onActionViewCollapsed() {
+ setQuery("", false);
+ super.onActionViewCollapsed();
+ }
+ }
+
@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Place an action bar item for searching.
MenuItem item = menu.add("Search");
item.setIcon(android.R.drawable.ic_menu_search);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
| MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
- SearchView sv = new SearchView(getActivity());
- sv.setOnQueryTextListener(this);
- item.setActionView(sv);
+ mSearchView = new MySearchView(getActivity());
+ mSearchView.setOnQueryTextListener(this);
+ mSearchView.setOnCloseListener(this);
+ mSearchView.setIconifiedByDefault(true);
+ item.setActionView(mSearchView);
}
public boolean onQueryTextChange(String newText) {
@@ -128,6 +152,14 @@ public class LoaderRetained extends Activity {
return true;
}
+ @Override
+ public boolean onClose() {
+ if (!TextUtils.isEmpty(mSearchView.getQuery())) {
+ mSearchView.setQuery(null, true);
+ }
+ return true;
+ }
+
@Override public void onListItemClick(ListView l, View v, int position, long id) {
// Insert desired behavior here.
Log.i("FragmentComplexList", "Item clicked: " + id);
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/OverscanActivity.java b/samples/ApiDemos/src/com/example/android/apis/app/OverscanActivity.java
new file mode 100644
index 000000000..38f4a0a64
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/OverscanActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.app;
+
+// Need the following import to get access to the app resources, since this
+// class is in a sub-package.
+import android.widget.ImageView;
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * <h3>Overscan Activity</h3>
+ *
+ * <p>This demonstrates the how to write an activity that extends into the
+ * overscan region.</p>
+ */
+public class OverscanActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.overscan_activity);
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/PresentationActivity.java b/samples/ApiDemos/src/com/example/android/apis/app/PresentationActivity.java
new file mode 100644
index 000000000..c626022be
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/PresentationActivity.java
@@ -0,0 +1,487 @@
+/*
+ * 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.apis.app;
+
+// Need the following import to get access to the app resources, since this
+// class is in a sub-package.
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Presentation;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.drawable.GradientDrawable;
+import android.hardware.display.DisplayManager;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.Parcelable.Creator;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+//BEGIN_INCLUDE(activity)
+/**
+ * <h3>Presentation Activity</h3>
+ *
+ * <p>
+ * This demonstrates how to create an activity that shows some content
+ * on a secondary display using a {@link Presentation}.
+ * </p><p>
+ * The activity uses the {@link DisplayManager} API to enumerate displays.
+ * When the user selects a display, the activity opens a {@link Presentation}
+ * on that display. We show a different photograph in each presentation
+ * on a unique background along with a label describing the display.
+ * We also write information about displays and display-related events to
+ * the Android log which you can read using <code>adb logcat</code>.
+ * </p><p>
+ * You can try this out using an HDMI or Wifi display or by using the
+ * "Simulate secondary displays" feature in Development Settings to create a few
+ * simulated secondary displays. Each display will appear in the list along with a
+ * checkbox to show a presentation on that display.
+ * </p><p>
+ * See also the {@link PresentationWithMediaRouterActivity} sample which
+ * uses the media router to automatically select a secondary display
+ * on which to show content based on the currently selected route.
+ * </p>
+ */
+public class PresentationActivity extends Activity
+ implements OnCheckedChangeListener, OnClickListener {
+ private final String TAG = "PresentationActivity";
+
+ // Key for storing saved instance state.
+ private static final String PRESENTATION_KEY = "presentation";
+
+ // The content that we want to show on the presentation.
+ private static final int[] PHOTOS = new int[] {
+ R.drawable.frantic,
+ R.drawable.photo1, R.drawable.photo2, R.drawable.photo3,
+ R.drawable.photo4, R.drawable.photo5, R.drawable.photo6,
+ R.drawable.sample_4,
+ };
+
+ private DisplayManager mDisplayManager;
+ private DisplayListAdapter mDisplayListAdapter;
+ private CheckBox mShowAllDisplaysCheckbox;
+ private ListView mListView;
+ private int mNextImageNumber;
+
+ // List of presentation contents indexed by displayId.
+ // This state persists so that we can restore the old presentation
+ // contents when the activity is paused or resumed.
+ private SparseArray<PresentationContents> mSavedPresentationContents;
+
+ // List of all currently visible presentations indexed by display id.
+ private final SparseArray<DemoPresentation> mActivePresentations =
+ new SparseArray<DemoPresentation>();
+
+ /**
+ * Initialization of the Activity after it is first created. Must at least
+ * call {@link android.app.Activity#setContentView setContentView()} to
+ * describe what is to be displayed in the screen.
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // Be sure to call the super class.
+ super.onCreate(savedInstanceState);
+
+ // Restore saved instance state.
+ if (savedInstanceState != null) {
+ mSavedPresentationContents =
+ savedInstanceState.getSparseParcelableArray(PRESENTATION_KEY);
+ } else {
+ mSavedPresentationContents = new SparseArray<PresentationContents>();
+ }
+
+ // Get the display manager service.
+ mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE);
+
+ // See assets/res/any/layout/presentation_activity.xml for this
+ // view layout definition, which is being set here as
+ // the content of our screen.
+ setContentView(R.layout.presentation_activity);
+
+ // Set up checkbox to toggle between showing all displays or only presentation displays.
+ mShowAllDisplaysCheckbox = (CheckBox)findViewById(R.id.show_all_displays);
+ mShowAllDisplaysCheckbox.setOnCheckedChangeListener(this);
+
+ // Set up the list of displays.
+ mDisplayListAdapter = new DisplayListAdapter(this);
+ mListView = (ListView)findViewById(R.id.display_list);
+ mListView.setAdapter(mDisplayListAdapter);
+ }
+
+ @Override
+ protected void onResume() {
+ // Be sure to call the super class.
+ super.onResume();
+
+ // Update our list of displays on resume.
+ mDisplayListAdapter.updateContents();
+
+ // Restore presentations from before the activity was paused.
+ final int numDisplays = mDisplayListAdapter.getCount();
+ for (int i = 0; i < numDisplays; i++) {
+ final Display display = mDisplayListAdapter.getItem(i);
+ final PresentationContents contents =
+ mSavedPresentationContents.get(display.getDisplayId());
+ if (contents != null) {
+ showPresentation(display, contents);
+ }
+ }
+ mSavedPresentationContents.clear();
+
+ // Register to receive events from the display manager.
+ mDisplayManager.registerDisplayListener(mDisplayListener, null);
+ }
+
+ @Override
+ protected void onPause() {
+ // Be sure to call the super class.
+ super.onPause();
+
+ // Unregister from the display manager.
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+
+ // Dismiss all of our presentations but remember their contents.
+ Log.d(TAG, "Activity is being paused. Dismissing all active presentation.");
+ for (int i = 0; i < mActivePresentations.size(); i++) {
+ DemoPresentation presentation = mActivePresentations.valueAt(i);
+ int displayId = mActivePresentations.keyAt(i);
+ mSavedPresentationContents.put(displayId, presentation.mContents);
+ presentation.dismiss();
+ }
+ mActivePresentations.clear();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ // Be sure to call the super class.
+ super.onSaveInstanceState(outState);
+ outState.putSparseParcelableArray(PRESENTATION_KEY, mSavedPresentationContents);
+ }
+
+ /**
+ * Shows a {@link Presentation} on the specified display.
+ */
+ private void showPresentation(Display display, PresentationContents contents) {
+ final int displayId = display.getDisplayId();
+ if (mActivePresentations.get(displayId) != null) {
+ return;
+ }
+
+ Log.d(TAG, "Showing presentation photo #" + contents.photo
+ + " on display #" + displayId + ".");
+
+ DemoPresentation presentation = new DemoPresentation(this, display, contents);
+ presentation.show();
+ presentation.setOnDismissListener(mOnDismissListener);
+ mActivePresentations.put(displayId, presentation);
+ }
+
+ /**
+ * Hides a {@link Presentation} on the specified display.
+ */
+ private void hidePresentation(Display display) {
+ final int displayId = display.getDisplayId();
+ DemoPresentation presentation = mActivePresentations.get(displayId);
+ if (presentation == null) {
+ return;
+ }
+
+ Log.d(TAG, "Dismissing presentation on display #" + displayId + ".");
+
+ presentation.dismiss();
+ mActivePresentations.delete(displayId);
+ }
+
+ private int getNextPhoto() {
+ final int photo = mNextImageNumber;
+ mNextImageNumber = (mNextImageNumber + 1) % PHOTOS.length;
+ return photo;
+ }
+
+ /**
+ * Called when the show all displays checkbox is toggled or when
+ * an item in the list of displays is checked or unchecked.
+ */
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView == mShowAllDisplaysCheckbox) {
+ // Show all displays checkbox was toggled.
+ mDisplayListAdapter.updateContents();
+ } else {
+ // Display item checkbox was toggled.
+ final Display display = (Display)buttonView.getTag();
+ if (isChecked) {
+ PresentationContents contents = new PresentationContents(getNextPhoto());
+ showPresentation(display, contents);
+ } else {
+ hidePresentation(display);
+ }
+ }
+ }
+
+ /**
+ * Called when the Info button next to a display is clicked to show information
+ * about the display.
+ */
+ @Override
+ public void onClick(View v) {
+ Context context = v.getContext();
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ final Display display = (Display)v.getTag();
+ Resources r = context.getResources();
+ AlertDialog alert = builder
+ .setTitle(r.getString(
+ R.string.presentation_alert_info_text, display.getDisplayId()))
+ .setMessage(display.toString())
+ .setNeutralButton(R.string.presentation_alert_dismiss_text,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ })
+ .create();
+ alert.show();
+ }
+
+ /**
+ * Listens for displays to be added, changed or removed.
+ * We use it to update the list and show a new {@link Presentation} when a
+ * display is connected.
+ *
+ * Note that we don't bother dismissing the {@link Presentation} when a
+ * display is removed, although we could. The presentation API takes care
+ * of doing that automatically for us.
+ */
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ Log.d(TAG, "Display #" + displayId + " added.");
+ mDisplayListAdapter.updateContents();
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ Log.d(TAG, "Display #" + displayId + " changed.");
+ mDisplayListAdapter.updateContents();
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ Log.d(TAG, "Display #" + displayId + " removed.");
+ mDisplayListAdapter.updateContents();
+ }
+ };
+
+ /**
+ * Listens for when presentations are dismissed.
+ */
+ private final DialogInterface.OnDismissListener mOnDismissListener =
+ new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ DemoPresentation presentation = (DemoPresentation)dialog;
+ int displayId = presentation.getDisplay().getDisplayId();
+ Log.d(TAG, "Presentation on display #" + displayId + " was dismissed.");
+ mActivePresentations.delete(displayId);
+ mDisplayListAdapter.notifyDataSetChanged();
+ }
+ };
+
+ /**
+ * List adapter.
+ * Shows information about all displays.
+ */
+ private final class DisplayListAdapter extends ArrayAdapter<Display> {
+ final Context mContext;
+
+ public DisplayListAdapter(Context context) {
+ super(context, R.layout.presentation_list_item);
+ mContext = context;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final View v;
+ if (convertView == null) {
+ v = ((Activity) mContext).getLayoutInflater().inflate(
+ R.layout.presentation_list_item, null);
+ } else {
+ v = convertView;
+ }
+
+ final Display display = getItem(position);
+ final int displayId = display.getDisplayId();
+
+ CheckBox cb = (CheckBox)v.findViewById(R.id.checkbox_presentation);
+ cb.setTag(display);
+ cb.setOnCheckedChangeListener(PresentationActivity.this);
+ cb.setChecked(mActivePresentations.indexOfKey(displayId) >= 0
+ || mSavedPresentationContents.indexOfKey(displayId) >= 0);
+
+ TextView tv = (TextView)v.findViewById(R.id.display_id);
+ tv.setText(v.getContext().getResources().getString(
+ R.string.presentation_display_id_text, displayId, display.getName()));
+
+ Button b = (Button)v.findViewById(R.id.info);
+ b.setTag(display);
+ b.setOnClickListener(PresentationActivity.this);
+
+ return v;
+ }
+
+ /**
+ * Update the contents of the display list adapter to show
+ * information about all current displays.
+ */
+ public void updateContents() {
+ clear();
+
+ String displayCategory = getDisplayCategory();
+ Display[] displays = mDisplayManager.getDisplays(displayCategory);
+ addAll(displays);
+
+ Log.d(TAG, "There are currently " + displays.length + " displays connected.");
+ for (Display display : displays) {
+ Log.d(TAG, " " + display);
+ }
+ }
+
+ private String getDisplayCategory() {
+ return mShowAllDisplaysCheckbox.isChecked() ? null :
+ DisplayManager.DISPLAY_CATEGORY_PRESENTATION;
+ }
+ }
+
+ /**
+ * The presentation to show on the secondary display.
+ *
+ * Note that the presentation display may have different metrics from the display on which
+ * the main activity is showing so we must be careful to use the presentation's
+ * own {@link Context} whenever we load resources.
+ */
+ private final class DemoPresentation extends Presentation {
+
+ final PresentationContents mContents;
+
+ public DemoPresentation(Context context, Display display, PresentationContents contents) {
+ super(context, display);
+ mContents = contents;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // Be sure to call the super class.
+ super.onCreate(savedInstanceState);
+
+ // Get the resources for the context of the presentation.
+ // Notice that we are getting the resources from the context of the presentation.
+ Resources r = getContext().getResources();
+
+ // Inflate the layout.
+ setContentView(R.layout.presentation_content);
+
+ final Display display = getDisplay();
+ final int displayId = display.getDisplayId();
+ final int photo = mContents.photo;
+
+ // Show a caption to describe what's going on.
+ TextView text = (TextView)findViewById(R.id.text);
+ text.setText(r.getString(R.string.presentation_photo_text,
+ photo, displayId, display.getName()));
+
+ // Show a n image for visual interest.
+ ImageView image = (ImageView)findViewById(R.id.image);
+ image.setImageDrawable(r.getDrawable(PHOTOS[photo]));
+
+ GradientDrawable drawable = new GradientDrawable();
+ drawable.setShape(GradientDrawable.RECTANGLE);
+ drawable.setGradientType(GradientDrawable.RADIAL_GRADIENT);
+
+ // Set the background to a random gradient.
+ Point p = new Point();
+ getDisplay().getSize(p);
+ drawable.setGradientRadius(Math.max(p.x, p.y) / 2);
+ drawable.setColors(mContents.colors);
+ findViewById(android.R.id.content).setBackground(drawable);
+ }
+ }
+
+ /**
+ * Information about the content we want to show in a presentation.
+ */
+ private final static class PresentationContents implements Parcelable {
+ final int photo;
+ final int[] colors;
+
+ public static final Creator<PresentationContents> CREATOR =
+ new Creator<PresentationContents>() {
+ @Override
+ public PresentationContents createFromParcel(Parcel in) {
+ return new PresentationContents(in);
+ }
+
+ @Override
+ public PresentationContents[] newArray(int size) {
+ return new PresentationContents[size];
+ }
+ };
+
+ public PresentationContents(int photo) {
+ this.photo = photo;
+ colors = new int[] {
+ ((int) (Math.random() * Integer.MAX_VALUE)) | 0xFF000000,
+ ((int) (Math.random() * Integer.MAX_VALUE)) | 0xFF000000 };
+ }
+
+ private PresentationContents(Parcel in) {
+ photo = in.readInt();
+ colors = new int[] { in.readInt(), in.readInt() };
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(photo);
+ dest.writeInt(colors[0]);
+ dest.writeInt(colors[1]);
+ }
+ }
+}
+//END_INCLUDE(activity)
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/PresentationWithMediaRouterActivity.java b/samples/ApiDemos/src/com/example/android/apis/app/PresentationWithMediaRouterActivity.java
new file mode 100644
index 000000000..5ba476ea8
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/PresentationWithMediaRouterActivity.java
@@ -0,0 +1,290 @@
+/*
+ * 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.apis.app;
+
+import com.example.android.apis.R;
+import com.example.android.apis.graphics.CubeRenderer;
+
+import android.app.Activity;
+import android.app.MediaRouteActionProvider;
+import android.app.Presentation;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.media.MediaRouter;
+import android.media.MediaRouter.RouteInfo;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Display;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+//BEGIN_INCLUDE(activity)
+/**
+ * <h3>Presentation Activity</h3>
+ *
+ * <p>
+ * This demonstrates how to create an activity that shows some content
+ * on a secondary display using a {@link Presentation}.
+ * </p><p>
+ * The activity uses the {@link MediaRouter} API to automatically detect when
+ * a presentation display is available and to allow the user to control the
+ * media routes using a menu item. When a presentation display is available,
+ * we stop showing content in the main activity and instead open up a
+ * {@link Presentation} on the preferred presentation display. When a presentation
+ * display is removed, we revert to showing content in the main activity.
+ * We also write information about displays and display-related events to
+ * the Android log which you can read using <code>adb logcat</code>.
+ * </p><p>
+ * You can try this out using an HDMI or Wifi display or by using the
+ * "Simulate secondary displays" feature in Development Settings to create a few
+ * simulated secondary displays. Each display will appear in the list along with a
+ * checkbox to show a presentation on that display.
+ * </p><p>
+ * See also the {@link PresentationActivity} sample which
+ * uses the low-level display manager to enumerate displays and to show multiple
+ * simultaneous presentations on different displays.
+ * </p>
+ */
+public class PresentationWithMediaRouterActivity extends Activity {
+ private final String TAG = "PresentationWithMediaRouterActivity";
+
+ private MediaRouter mMediaRouter;
+ private DemoPresentation mPresentation;
+ private GLSurfaceView mSurfaceView;
+ private TextView mInfoTextView;
+ private boolean mPaused;
+
+ /**
+ * Initialization of the Activity after it is first created. Must at least
+ * call {@link android.app.Activity#setContentView setContentView()} to
+ * describe what is to be displayed in the screen.
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // Be sure to call the super class.
+ super.onCreate(savedInstanceState);
+
+ // Get the media router service.
+ mMediaRouter = (MediaRouter)getSystemService(Context.MEDIA_ROUTER_SERVICE);
+
+ // See assets/res/any/layout/presentation_with_media_router_activity.xml for this
+ // view layout definition, which is being set here as
+ // the content of our screen.
+ setContentView(R.layout.presentation_with_media_router_activity);
+
+ // Set up the surface view for visual interest.
+ mSurfaceView = (GLSurfaceView)findViewById(R.id.surface_view);
+ mSurfaceView.setRenderer(new CubeRenderer(false));
+
+ // Get a text view where we will show information about what's happening.
+ mInfoTextView = (TextView)findViewById(R.id.info);
+ }
+
+ @Override
+ protected void onResume() {
+ // Be sure to call the super class.
+ super.onResume();
+
+ // Listen for changes to media routes.
+ mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_LIVE_VIDEO, mMediaRouterCallback);
+
+ // Update the presentation based on the currently selected route.
+ mPaused = false;
+ updatePresentation();
+ }
+
+ @Override
+ protected void onPause() {
+ // Be sure to call the super class.
+ super.onPause();
+
+ // Stop listening for changes to media routes.
+ mMediaRouter.removeCallback(mMediaRouterCallback);
+
+ // Pause rendering.
+ mPaused = true;
+ updateContents();
+ }
+
+ @Override
+ protected void onStop() {
+ // Be sure to call the super class.
+ super.onStop();
+
+ // Dismiss the presentation when the activity is not visible.
+ if (mPresentation != null) {
+ Log.i(TAG, "Dismissing presentation because the activity is no longer visible.");
+ mPresentation.dismiss();
+ mPresentation = null;
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Be sure to call the super class.
+ super.onCreateOptionsMenu(menu);
+
+ // Inflate the menu and configure the media router action provider.
+ getMenuInflater().inflate(R.menu.presentation_with_media_router_menu, menu);
+
+ MenuItem mediaRouteMenuItem = menu.findItem(R.id.menu_media_route);
+ MediaRouteActionProvider mediaRouteActionProvider =
+ (MediaRouteActionProvider)mediaRouteMenuItem.getActionProvider();
+ mediaRouteActionProvider.setRouteTypes(MediaRouter.ROUTE_TYPE_LIVE_VIDEO);
+
+ // Return true to show the menu.
+ return true;
+ }
+
+ private void updatePresentation() {
+ // Get the current route and its presentation display.
+ MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(
+ MediaRouter.ROUTE_TYPE_LIVE_VIDEO);
+ Display presentationDisplay = route != null ? route.getPresentationDisplay() : null;
+
+ // Dismiss the current presentation if the display has changed.
+ if (mPresentation != null && mPresentation.getDisplay() != presentationDisplay) {
+ Log.i(TAG, "Dismissing presentation because the current route no longer "
+ + "has a presentation display.");
+ mPresentation.dismiss();
+ mPresentation = null;
+ }
+
+ // Show a new presentation if needed.
+ if (mPresentation == null && presentationDisplay != null) {
+ Log.i(TAG, "Showing presentation on display: " + presentationDisplay);
+ mPresentation = new DemoPresentation(this, presentationDisplay);
+ mPresentation.setOnDismissListener(mOnDismissListener);
+ try {
+ mPresentation.show();
+ } catch (WindowManager.InvalidDisplayException ex) {
+ Log.w(TAG, "Couldn't show presentation! Display was removed in "
+ + "the meantime.", ex);
+ mPresentation = null;
+ }
+ }
+
+ // Update the contents playing in this activity.
+ updateContents();
+ }
+
+ private void updateContents() {
+ // Show either the content in the main activity or the content in the presentation
+ // along with some descriptive text about what is happening.
+ if (mPresentation != null) {
+ mInfoTextView.setText(getResources().getString(
+ R.string.presentation_with_media_router_now_playing_remotely,
+ mPresentation.getDisplay().getName()));
+ mSurfaceView.setVisibility(View.INVISIBLE);
+ mSurfaceView.onPause();
+ if (mPaused) {
+ mPresentation.getSurfaceView().onPause();
+ } else {
+ mPresentation.getSurfaceView().onResume();
+ }
+ } else {
+ mInfoTextView.setText(getResources().getString(
+ R.string.presentation_with_media_router_now_playing_locally,
+ getWindowManager().getDefaultDisplay().getName()));
+ mSurfaceView.setVisibility(View.VISIBLE);
+ if (mPaused) {
+ mSurfaceView.onPause();
+ } else {
+ mSurfaceView.onResume();
+ }
+ }
+ }
+
+ private final MediaRouter.SimpleCallback mMediaRouterCallback =
+ new MediaRouter.SimpleCallback() {
+ @Override
+ public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
+ Log.d(TAG, "onRouteSelected: type=" + type + ", info=" + info);
+ updatePresentation();
+ }
+
+ @Override
+ public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
+ Log.d(TAG, "onRouteUnselected: type=" + type + ", info=" + info);
+ updatePresentation();
+ }
+
+ @Override
+ public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
+ Log.d(TAG, "onRoutePresentationDisplayChanged: info=" + info);
+ updatePresentation();
+ }
+ };
+
+ /**
+ * Listens for when presentations are dismissed.
+ */
+ private final DialogInterface.OnDismissListener mOnDismissListener =
+ new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ if (dialog == mPresentation) {
+ Log.i(TAG, "Presentation was dismissed.");
+ mPresentation = null;
+ updateContents();
+ }
+ }
+ };
+
+ /**
+ * The presentation to show on the secondary display.
+ * <p>
+ * Note that this display may have different metrics from the display on which
+ * the main activity is showing so we must be careful to use the presentation's
+ * own {@link Context} whenever we load resources.
+ * </p>
+ */
+ private final static class DemoPresentation extends Presentation {
+ private GLSurfaceView mSurfaceView;
+
+ public DemoPresentation(Context context, Display display) {
+ super(context, display);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // Be sure to call the super class.
+ super.onCreate(savedInstanceState);
+
+ // Get the resources for the context of the presentation.
+ // Notice that we are getting the resources from the context of the presentation.
+ Resources r = getContext().getResources();
+
+ // Inflate the layout.
+ setContentView(R.layout.presentation_with_media_router_content);
+
+ // Set up the surface view for visual interest.
+ mSurfaceView = (GLSurfaceView)findViewById(R.id.surface_view);
+ mSurfaceView.setRenderer(new CubeRenderer(false));
+ }
+
+ public GLSurfaceView getSurfaceView() {
+ return mSurfaceView;
+ }
+ }
+}
+//END_INCLUDE(activity)
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/PrintBitmap.java b/samples/ApiDemos/src/com/example/android/apis/app/PrintBitmap.java
new file mode 100644
index 000000000..762478a99
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/PrintBitmap.java
@@ -0,0 +1,82 @@
+package com.example.android.apis.app;
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
+import android.print.PrintManager;
+import android.support.v4.print.PrintHelper;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.webkit.WebView;
+import android.widget.ImageView;
+
+import com.example.android.apis.R;
+
+/**
+ * This class demonstrates how to implement bitmap printing.
+ * <p>
+ * This activity shows an image and offers a print option in the overflow
+ * menu. When the user chooses to print a helper class from the support
+ * library is used to print the image.
+ * </p>
+ *
+ * @see PrintManager
+ * @see WebView
+ */
+public class PrintBitmap extends Activity {
+
+ private ImageView mImageView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.print_bitmap);
+ mImageView = (ImageView) findViewById(R.id.image);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getMenuInflater().inflate(R.menu.print_custom_content, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.menu_print) {
+ print();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void print() {
+ // Get the print manager.
+ PrintHelper printHelper = new PrintHelper(this);
+
+ // Set the desired scale mode.
+ printHelper.setScaleMode(PrintHelper.SCALE_MODE_FIT);
+
+ // Get the bitmap for the ImageView's drawable.
+ Bitmap bitmap = ((BitmapDrawable) mImageView.getDrawable()).getBitmap();
+
+ // Print the bitmap.
+ printHelper.printBitmap("Print Bitmap", bitmap);
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/PrintCustomContent.java b/samples/ApiDemos/src/com/example/android/apis/app/PrintCustomContent.java
new file mode 100644
index 000000000..7a7e7ed80
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/PrintCustomContent.java
@@ -0,0 +1,562 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.app;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.pdf.PdfDocument.Page;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.CancellationSignal.OnCancelListener;
+import android.os.ParcelFileDescriptor;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintDocumentInfo;
+import android.print.PrintManager;
+import android.print.pdf.PrintedPdfDocument;
+import android.util.SparseIntArray;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.example.android.apis.R;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class demonstrates how to implement custom printing support.
+ * <p>
+ * This activity shows the list of the MotoGP champions by year and
+ * brand. The print option in the overflow menu allows the user to
+ * print the content. The list list of items is laid out to such that
+ * it fits the options selected by the user from the UI such as page
+ * size. Hence, for different page sizes the printed content will have
+ * different page count.
+ * </p>
+ * <p>
+ * This sample demonstrates how to completely implement a {@link
+ * PrintDocumentAdapter} in which:
+ * <ul>
+ * <li>Layout based on the selected print options is performed.</li>
+ * <li>Layout work is performed only if print options change would change the content.</li>
+ * <li>Layout result is properly reported.</li>
+ * <li>Only requested pages are written.</li>
+ * <li>Write result is properly reported.</li>
+ * <li>Both Layout and write respond to cancellation.</li>
+ * <li>Layout and render of views is demonstrated.</li>
+ * </ul>
+ * </p>
+ *
+ * @see PrintManager
+ * @see PrintDocumentAdapter
+ */
+public class PrintCustomContent extends ListActivity {
+
+ private static final int MILS_IN_INCH = 1000;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setListAdapter(new MotoGpStatAdapter(loadMotoGpStats(),
+ getLayoutInflater()));
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getMenuInflater().inflate(R.menu.print_custom_content, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.menu_print) {
+ print();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void print() {
+ PrintManager printManager = (PrintManager) getSystemService(
+ Context.PRINT_SERVICE);
+
+ printManager.print("MotoGP stats",
+ new PrintDocumentAdapter() {
+ private int mRenderPageWidth;
+ private int mRenderPageHeight;
+
+ private PrintAttributes mPrintAttributes;
+ private PrintDocumentInfo mDocumentInfo;
+ private Context mPrintContext;
+
+ @Override
+ public void onLayout(final PrintAttributes oldAttributes,
+ final PrintAttributes newAttributes,
+ final CancellationSignal cancellationSignal,
+ final LayoutResultCallback callback,
+ final Bundle metadata) {
+
+ // If we are already cancelled, don't do any work.
+ if (cancellationSignal.isCanceled()) {
+ callback.onLayoutCancelled();
+ return;
+ }
+
+ // Now we determined if the print attributes changed in a way that
+ // would change the layout and if so we will do a layout pass.
+ boolean layoutNeeded = false;
+
+ final int density = Math.max(newAttributes.getResolution().getHorizontalDpi(),
+ newAttributes.getResolution().getVerticalDpi());
+
+ // Note that we are using the PrintedPdfDocument class which creates
+ // a PDF generating canvas whose size is in points (1/72") not screen
+ // pixels. Hence, this canvas is pretty small compared to the screen.
+ // The recommended way is to layout the content in the desired size,
+ // in this case as large as the printer can do, and set a translation
+ // to the PDF canvas to shrink in. Note that PDF is a vector format
+ // and you will not lose data during the transformation.
+
+ // The content width is equal to the page width minus the margins times
+ // the horizontal printer density. This way we get the maximal number
+ // of pixels the printer can put horizontally.
+ final int marginLeft = (int) (density * (float) newAttributes.getMinMargins()
+ .getLeftMils() / MILS_IN_INCH);
+ final int marginRight = (int) (density * (float) newAttributes.getMinMargins()
+ .getRightMils() / MILS_IN_INCH);
+ final int contentWidth = (int) (density * (float) newAttributes.getMediaSize()
+ .getWidthMils() / MILS_IN_INCH) - marginLeft - marginRight;
+ if (mRenderPageWidth != contentWidth) {
+ mRenderPageWidth = contentWidth;
+ layoutNeeded = true;
+ }
+
+ // The content height is equal to the page height minus the margins times
+ // the vertical printer resolution. This way we get the maximal number
+ // of pixels the printer can put vertically.
+ final int marginTop = (int) (density * (float) newAttributes.getMinMargins()
+ .getTopMils() / MILS_IN_INCH);
+ final int marginBottom = (int) (density * (float) newAttributes.getMinMargins()
+ .getBottomMils() / MILS_IN_INCH);
+ final int contentHeight = (int) (density * (float) newAttributes.getMediaSize()
+ .getHeightMils() / MILS_IN_INCH) - marginTop - marginBottom;
+ if (mRenderPageHeight != contentHeight) {
+ mRenderPageHeight = contentHeight;
+ layoutNeeded = true;
+ }
+
+ // Create a context for resources at printer density. We will
+ // be inflating views to render them and would like them to use
+ // resources for a density the printer supports.
+ if (mPrintContext == null || mPrintContext.getResources()
+ .getConfiguration().densityDpi != density) {
+ Configuration configuration = new Configuration();
+ configuration.densityDpi = density;
+ mPrintContext = createConfigurationContext(
+ configuration);
+ mPrintContext.setTheme(android.R.style.Theme_Holo_Light);
+ }
+
+ // If no layout is needed that we did a layout at least once and
+ // the document info is not null, also the second argument is false
+ // to notify the system that the content did not change. This is
+ // important as if the system has some pages and the content didn't
+ // change the system will ask, the application to write them again.
+ if (!layoutNeeded) {
+ callback.onLayoutFinished(mDocumentInfo, false);
+ return;
+ }
+
+ // For demonstration purposes we will do the layout off the main
+ // thread but for small content sizes like this one it is OK to do
+ // that on the main thread.
+
+ // Store the data as we will layout off the main thread.
+ final List<MotoGpStatItem> items = ((MotoGpStatAdapter)
+ getListAdapter()).cloneItems();
+
+ new AsyncTask<Void, Void, PrintDocumentInfo>() {
+ @Override
+ protected void onPreExecute() {
+ // First register for cancellation requests.
+ cancellationSignal.setOnCancelListener(new OnCancelListener() {
+ @Override
+ public void onCancel() {
+ cancel(true);
+ }
+ });
+ // Stash the attributes as we will need them for rendering.
+ mPrintAttributes = newAttributes;
+ }
+
+ @Override
+ protected PrintDocumentInfo doInBackground(Void... params) {
+ try {
+ // Create an adapter with the stats and an inflater
+ // to load resources for the printer density.
+ MotoGpStatAdapter adapter = new MotoGpStatAdapter(items,
+ (LayoutInflater) mPrintContext.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE));
+
+ int currentPage = 0;
+ int pageContentHeight = 0;
+ int viewType = -1;
+ View view = null;
+ LinearLayout dummyParent = new LinearLayout(mPrintContext);
+ dummyParent.setOrientation(LinearLayout.VERTICAL);
+
+ final int itemCount = adapter.getCount();
+ for (int i = 0; i < itemCount; i++) {
+ // Be nice and respond to cancellation.
+ if (isCancelled()) {
+ return null;
+ }
+
+ // Get the next view.
+ final int nextViewType = adapter.getItemViewType(i);
+ if (viewType == nextViewType) {
+ view = adapter.getView(i, view, dummyParent);
+ } else {
+ view = adapter.getView(i, null, dummyParent);
+ }
+ viewType = nextViewType;
+
+ // Measure the next view
+ measureView(view);
+
+ // Add the height but if the view crosses the page
+ // boundary we will put it to the next page.
+ pageContentHeight += view.getMeasuredHeight();
+ if (pageContentHeight > mRenderPageHeight) {
+ pageContentHeight = view.getMeasuredHeight();
+ currentPage++;
+ }
+ }
+
+ // Create a document info describing the result.
+ PrintDocumentInfo info = new PrintDocumentInfo
+ .Builder("MotoGP_stats.pdf")
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(currentPage + 1)
+ .build();
+
+ // We completed the layout as a result of print attributes
+ // change. Hence, if we are here the content changed for
+ // sure which is why we pass true as the second argument.
+ callback.onLayoutFinished(info, true);
+ return info;
+ } catch (Exception e) {
+ // An unexpected error, report that we failed and
+ // one may pass in a human readable localized text
+ // for what the error is if known.
+ callback.onLayoutFailed(null);
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected void onPostExecute(PrintDocumentInfo result) {
+ // Update the cached info to send it over if the next
+ // layout pass does not result in a content change.
+ mDocumentInfo = result;
+ }
+
+ @Override
+ protected void onCancelled(PrintDocumentInfo result) {
+ // Task was cancelled, report that.
+ callback.onLayoutCancelled();
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+ }
+
+ @Override
+ public void onWrite(final PageRange[] pages,
+ final ParcelFileDescriptor destination,
+ final CancellationSignal cancellationSignal,
+ final WriteResultCallback callback) {
+
+ // If we are already cancelled, don't do any work.
+ if (cancellationSignal.isCanceled()) {
+ callback.onWriteCancelled();
+ return;
+ }
+
+ // Store the data as we will layout off the main thread.
+ final List<MotoGpStatItem> items = ((MotoGpStatAdapter)
+ getListAdapter()).cloneItems();
+
+ new AsyncTask<Void, Void, Void>() {
+ private final SparseIntArray mWrittenPages = new SparseIntArray();
+ private final PrintedPdfDocument mPdfDocument = new PrintedPdfDocument(
+ PrintCustomContent.this, mPrintAttributes);
+
+ @Override
+ protected void onPreExecute() {
+ // First register for cancellation requests.
+ cancellationSignal.setOnCancelListener(new OnCancelListener() {
+ @Override
+ public void onCancel() {
+ cancel(true);
+ }
+ });
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ // Go over all the pages and write only the requested ones.
+ // Create an adapter with the stats and an inflater
+ // to load resources for the printer density.
+ MotoGpStatAdapter adapter = new MotoGpStatAdapter(items,
+ (LayoutInflater) mPrintContext.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE));
+
+ int currentPage = -1;
+ int pageContentHeight = 0;
+ int viewType = -1;
+ View view = null;
+ Page page = null;
+ LinearLayout dummyParent = new LinearLayout(mPrintContext);
+ dummyParent.setOrientation(LinearLayout.VERTICAL);
+
+ // The content is laid out and rendered in screen pixels with
+ // the width and height of the paper size times the print
+ // density but the PDF canvas size is in points which are 1/72",
+ // so we will scale down the content.
+ final float scale = Math.min(
+ (float) mPdfDocument.getPageContentRect().width()
+ / mRenderPageWidth,
+ (float) mPdfDocument.getPageContentRect().height()
+ / mRenderPageHeight);
+
+ final int itemCount = adapter.getCount();
+ for (int i = 0; i < itemCount; i++) {
+ // Be nice and respond to cancellation.
+ if (isCancelled()) {
+ return null;
+ }
+
+ // Get the next view.
+ final int nextViewType = adapter.getItemViewType(i);
+ if (viewType == nextViewType) {
+ view = adapter.getView(i, view, dummyParent);
+ } else {
+ view = adapter.getView(i, null, dummyParent);
+ }
+ viewType = nextViewType;
+
+ // Measure the next view
+ measureView(view);
+
+ // Add the height but if the view crosses the page
+ // boundary we will put it to the next one.
+ pageContentHeight += view.getMeasuredHeight();
+ if (currentPage < 0 || pageContentHeight > mRenderPageHeight) {
+ pageContentHeight = view.getMeasuredHeight();
+ currentPage++;
+ // Done with the current page - finish it.
+ if (page != null) {
+ mPdfDocument.finishPage(page);
+ }
+ // If the page is requested, render it.
+ if (containsPage(pages, currentPage)) {
+ page = mPdfDocument.startPage(currentPage);
+ page.getCanvas().scale(scale, scale);
+ // Keep track which pages are written.
+ mWrittenPages.append(mWrittenPages.size(), currentPage);
+ } else {
+ page = null;
+ }
+ }
+
+ // If the current view is on a requested page, render it.
+ if (page != null) {
+ // Layout an render the content.
+ view.layout(0, 0, view.getMeasuredWidth(),
+ view.getMeasuredHeight());
+ view.draw(page.getCanvas());
+ // Move the canvas for the next view.
+ page.getCanvas().translate(0, view.getHeight());
+ }
+ }
+
+ // Done with the last page.
+ if (page != null) {
+ mPdfDocument.finishPage(page);
+ }
+
+ // Write the data and return success or failure.
+ try {
+ mPdfDocument.writeTo(new FileOutputStream(
+ destination.getFileDescriptor()));
+ // Compute which page ranges were written based on
+ // the bookkeeping we maintained.
+ PageRange[] pageRanges = computeWrittenPageRanges(mWrittenPages);
+ callback.onWriteFinished(pageRanges);
+ } catch (IOException ioe) {
+ callback.onWriteFailed(null);
+ } finally {
+ mPdfDocument.close();
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onCancelled(Void result) {
+ // Task was cancelled, report that.
+ callback.onWriteCancelled();
+ mPdfDocument.close();
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+ }
+
+ private void measureView(View view) {
+ final int widthMeasureSpec = ViewGroup.getChildMeasureSpec(
+ MeasureSpec.makeMeasureSpec(mRenderPageWidth,
+ MeasureSpec.EXACTLY), 0, view.getLayoutParams().width);
+ final int heightMeasureSpec = ViewGroup.getChildMeasureSpec(
+ MeasureSpec.makeMeasureSpec(mRenderPageHeight,
+ MeasureSpec.EXACTLY), 0, view.getLayoutParams().height);
+ view.measure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ private PageRange[] computeWrittenPageRanges(SparseIntArray writtenPages) {
+ List<PageRange> pageRanges = new ArrayList<PageRange>();
+
+ int start = -1;
+ int end = -1;
+ final int writtenPageCount = writtenPages.size();
+ for (int i = 0; i < writtenPageCount; i++) {
+ if (start < 0) {
+ start = writtenPages.valueAt(i);
+ }
+ int oldEnd = end = start;
+ while (i < writtenPageCount && (end - oldEnd) <= 1) {
+ oldEnd = end;
+ end = writtenPages.valueAt(i);
+ i++;
+ }
+ PageRange pageRange = new PageRange(start, end);
+ pageRanges.add(pageRange);
+ start = end = -1;
+ }
+
+ PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
+ pageRanges.toArray(pageRangesArray);
+ return pageRangesArray;
+ }
+
+ private boolean containsPage(PageRange[] pageRanges, int page) {
+ final int pageRangeCount = pageRanges.length;
+ for (int i = 0; i < pageRangeCount; i++) {
+ if (pageRanges[i].getStart() <= page
+ && pageRanges[i].getEnd() >= page) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }, null);
+ }
+
+ private List<MotoGpStatItem> loadMotoGpStats() {
+ String[] years = getResources().getStringArray(R.array.motogp_years);
+ String[] champions = getResources().getStringArray(R.array.motogp_champions);
+ String[] constructors = getResources().getStringArray(R.array.motogp_constructors);
+
+ List<MotoGpStatItem> items = new ArrayList<MotoGpStatItem>();
+
+ final int itemCount = years.length;
+ for (int i = 0; i < itemCount; i++) {
+ MotoGpStatItem item = new MotoGpStatItem();
+ item.year = years[i];
+ item.champion = champions[i];
+ item.constructor = constructors[i];
+ items.add(item);
+ }
+
+ return items;
+ }
+
+ private static final class MotoGpStatItem {
+ String year;
+ String champion;
+ String constructor;
+ }
+
+ private class MotoGpStatAdapter extends BaseAdapter {
+ private final List<MotoGpStatItem> mItems;
+ private final LayoutInflater mInflater;
+
+ public MotoGpStatAdapter(List<MotoGpStatItem> items, LayoutInflater inflater) {
+ mItems = items;
+ mInflater = inflater;
+ }
+
+ public List<MotoGpStatItem> cloneItems() {
+ return new ArrayList<MotoGpStatItem>(mItems);
+ }
+
+ @Override
+ public int getCount() {
+ return mItems.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mItems.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mInflater.inflate(R.layout.motogp_stat_item, parent, false);
+ }
+
+ MotoGpStatItem item = (MotoGpStatItem) getItem(position);
+
+ TextView yearView = (TextView) convertView.findViewById(R.id.year);
+ yearView.setText(item.year);
+
+ TextView championView = (TextView) convertView.findViewById(R.id.champion);
+ championView.setText(item.champion);
+
+ TextView constructorView = (TextView) convertView.findViewById(R.id.constructor);
+ constructorView.setText(item.constructor);
+
+ return convertView;
+ }
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/PrintHtmlFromScreen.java b/samples/ApiDemos/src/com/example/android/apis/app/PrintHtmlFromScreen.java
new file mode 100644
index 000000000..55c98ffc9
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/PrintHtmlFromScreen.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.app;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.print.PrintManager;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import com.example.android.apis.R;
+
+/**
+ * This class demonstrates how to implement HTML content printing
+ * from a {@link WebView} which is shown on the screen.
+ * <p>
+ * This activity shows a simple HTML content in a {@link WebView}
+ * and allows the user to print that content via an action in the
+ * action bar. The shown {@link WebView} is doing the printing.
+ * </p>
+ *
+ * @see PrintManager
+ * @see WebView
+ */
+public class PrintHtmlFromScreen extends Activity {
+
+ private WebView mWebView;
+
+ private boolean mDataLoaded;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.print_html_from_screen);
+ mWebView = (WebView) findViewById(R.id.web_view);
+
+ // Important: Only enable the print option after the page is loaded.
+ mWebView.setWebViewClient(new WebViewClient() {
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ // Data loaded, so now we want to show the print option.
+ mDataLoaded = true;
+ invalidateOptionsMenu();
+ }
+ });
+
+ // Load an HTML page.
+ mWebView.loadUrl("file:///android_res/raw/motogp_stats.html");
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ if (mDataLoaded) {
+ getMenuInflater().inflate(R.menu.print_custom_content, menu);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.menu_print) {
+ print();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void print() {
+ // Get the print manager.
+ PrintManager printManager = (PrintManager) getSystemService(
+ Context.PRINT_SERVICE);
+ // Pass in the ViewView's document adapter.
+ printManager.print("MotoGP stats", mWebView.createPrintDocumentAdapter(), null);
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/PrintHtmlOffScreen.java b/samples/ApiDemos/src/com/example/android/apis/app/PrintHtmlOffScreen.java
new file mode 100644
index 000000000..9c239b867
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/PrintHtmlOffScreen.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.app;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintManager;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import com.example.android.apis.R;
+
+/**
+ * This class demonstrates how to implement HTML content printing
+ * from a {@link WebView} which is not shown on the screen.
+ * <p>
+ * This activity shows a text prompt and when the user chooses the
+ * print option from the overflow menu an HTML page with content that
+ * is not on the screen is printed via an off-screen {@link WebView}.
+ * </p>
+ *
+ * @see PrintManager
+ * @see WebView
+ */
+public class PrintHtmlOffScreen extends Activity {
+
+ private WebView mWebView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.print_html_off_screen);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getMenuInflater().inflate(R.menu.print_custom_content, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.menu_print) {
+ print();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void print() {
+ // Create a WebView and hold on to it as the printing will start when
+ // load completes and we do not want the WbeView to be garbage collected.
+ mWebView = new WebView(this);
+
+ // Important: Only after the page is loaded we will do the print.
+ mWebView.setWebViewClient(new WebViewClient() {
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ doPrint();
+ }
+ });
+
+ // Load an HTML page.
+ mWebView.loadUrl("file:///android_res/raw/motogp_stats.html");
+ }
+
+ private void doPrint() {
+ // Get the print manager.
+ PrintManager printManager = (PrintManager) getSystemService(
+ Context.PRINT_SERVICE);
+
+ // Create a wrapper PrintDocumentAdapter to clean up when done.
+ PrintDocumentAdapter adapter = new PrintDocumentAdapter() {
+ private final PrintDocumentAdapter mWrappedInstance =
+ mWebView.createPrintDocumentAdapter();
+
+ @Override
+ public void onStart() {
+ mWrappedInstance.onStart();
+ }
+
+ @Override
+ public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+ CancellationSignal cancellationSignal, LayoutResultCallback callback,
+ Bundle extras) {
+ mWrappedInstance.onLayout(oldAttributes, newAttributes, cancellationSignal,
+ callback, extras);
+ }
+
+ @Override
+ public void onWrite(PageRange[] pages, ParcelFileDescriptor destination,
+ CancellationSignal cancellationSignal, WriteResultCallback callback) {
+ mWrappedInstance.onWrite(pages, destination, cancellationSignal, callback);
+ }
+
+ @Override
+ public void onFinish() {
+ mWrappedInstance.onFinish();
+ // Intercept the finish call to know when printing is done
+ // and destroy the WebView as it is expensive to keep around.
+ mWebView.destroy();
+ mWebView = null;
+ }
+ };
+
+ // Pass in the ViewView's document adapter.
+ printManager.print("MotoGP stats", adapter, null);
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/RotationAnimation.java b/samples/ApiDemos/src/com/example/android/apis/app/RotationAnimation.java
new file mode 100644
index 000000000..44bd1e6fa
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/RotationAnimation.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.app;
+
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.RadioGroup;
+
+
+public class RotationAnimation extends Activity {
+
+ private int mRotationAnimation = LayoutParams.ROTATION_ANIMATION_ROTATE;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setRotationAnimation(mRotationAnimation);
+
+ setContentView(R.layout.rotation_animation);
+
+ ((CheckBox)findViewById(R.id.windowFullscreen)).setOnCheckedChangeListener(
+ new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ setFullscreen(isChecked);
+ }
+ }
+ );
+
+ ((RadioGroup)findViewById(R.id.rotation_radio_group)).setOnCheckedChangeListener(
+ new RadioGroup.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(RadioGroup group, int checkedId) {
+ switch (checkedId) {
+ default:
+ case R.id.rotate:
+ mRotationAnimation = LayoutParams.ROTATION_ANIMATION_ROTATE;
+ break;
+ case R.id.crossfade:
+ mRotationAnimation = LayoutParams.ROTATION_ANIMATION_CROSSFADE;
+ break;
+ case R.id.jumpcut:
+ mRotationAnimation = LayoutParams.ROTATION_ANIMATION_JUMPCUT;
+ break;
+ }
+ setRotationAnimation(mRotationAnimation);
+ }
+ }
+ );
+ }
+
+ private void setFullscreen(boolean on) {
+ Window win = getWindow();
+ WindowManager.LayoutParams winParams = win.getAttributes();
+ if (on) {
+ winParams.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
+ } else {
+ winParams.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN;
+ }
+ win.setAttributes(winParams);
+ }
+
+ private void setRotationAnimation(int rotationAnimation) {
+ Window win = getWindow();
+ WindowManager.LayoutParams winParams = win.getAttributes();
+ winParams.rotationAnimation = rotationAnimation;
+ win.setAttributes(winParams);
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/ScreenOrientation.java b/samples/ApiDemos/src/com/example/android/apis/app/ScreenOrientation.java
index 3946b2a9b..01eb5246a 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/ScreenOrientation.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/ScreenOrientation.java
@@ -46,6 +46,10 @@ public class ScreenOrientation extends Activity {
ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE,
ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT,
ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR,
+ ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE,
+ ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT,
+ ActivityInfo.SCREEN_ORIENTATION_FULL_USER,
+ ActivityInfo.SCREEN_ORIENTATION_LOCKED,
};
@Override
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/SecureDialogActivity.java b/samples/ApiDemos/src/com/example/android/apis/app/SecureDialogActivity.java
new file mode 100644
index 000000000..fe871aa8a
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/SecureDialogActivity.java
@@ -0,0 +1,94 @@
+/*
+ * 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.apis.app;
+
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+
+/**
+ * <h3>Secure Dialog Activity</h3>
+ *
+ * <p>
+ * This activity demonstrates how to create a dialog whose window is backed by
+ * a secure surface using {@link WindowManager.LayoutParams#FLAG_SECURE}.
+ * Because the surface is secure, its contents cannot be captured in screenshots
+ * and will not be visible on non-secure displays even when mirrored.
+ * </p><p>
+ * Here are a few things you can do to experiment with secure surfaces and
+ * observe their behavior.
+ * <ul>
+ * <li>Try taking a screenshot. Either the system will prevent you from taking
+ * a screenshot altogether or the screenshot should not contain the contents
+ * of the secure surface.
+ * <li>Try mirroring the secure surface onto a non-secure display such as an
+ * "Overlay Display" created using the "Simulate secondary displays" option in
+ * the "Developer options" section of the Settings application. The non-secure
+ * secondary display should not show the contents of the secure surface.
+ * <li>Try mirroring the secure surface onto a secure display such as an
+ * HDMI display with HDCP enabled. The contents of the secure surface should appear
+ * on the display.
+ * </ul>
+ * </p>
+ */
+public class SecureDialogActivity extends Activity
+ implements View.OnClickListener {
+ /**
+ * Initialization of the Activity after it is first created. Must at least
+ * call {@link android.app.Activity#setContentView setContentView()} to
+ * describe what is to be displayed in the screen.
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // Be sure to call the super class.
+ super.onCreate(savedInstanceState);
+
+ // See assets/res/any/layout/secure_dialog_activity.xml for this
+ // view layout definition, which is being set here as
+ // the content of our screen.
+ setContentView(R.layout.secure_dialog_activity);
+
+ // Handle click events on the button to show the dialog.
+ Button button = (Button)findViewById(R.id.show);
+ button.setOnClickListener(this);
+ }
+
+ /**
+ * Called when the button to show the dialog is clicked.
+ */
+ @Override
+ public void onClick(View v) {
+ // Create a dialog.
+ AlertDialog dialog = new AlertDialog.Builder(this)
+ .setPositiveButton(android.R.string.ok, null)
+ .setMessage(R.string.secure_dialog_dialog_text)
+ .create();
+
+ // Make the dialog secure. This must be done at the time the dialog is
+ // created. It cannot be changed after the dialog has been shown.
+ dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
+ WindowManager.LayoutParams.FLAG_SECURE);
+
+ // Show the dialog.
+ dialog.show();
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/SecureSurfaceViewActivity.java b/samples/ApiDemos/src/com/example/android/apis/app/SecureSurfaceViewActivity.java
new file mode 100644
index 000000000..978c70c78
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/SecureSurfaceViewActivity.java
@@ -0,0 +1,101 @@
+/*
+ * 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.apis.app;
+
+import com.example.android.apis.R;
+import com.example.android.apis.graphics.CubeRenderer;
+
+import android.app.Activity;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.view.SurfaceView;
+import android.view.WindowManager;
+
+/**
+ * <h3>Secure Window Activity</h3>
+ *
+ * <p>
+ * This activity demonstrates how to create a {@link SurfaceView} backed by
+ * a secure surface using {@link SurfaceView#setSecure}.
+ * Because the surface is secure, its contents cannot be captured in screenshots
+ * and will not be visible on non-secure displays even when mirrored.
+ * </p><p>
+ * Here are a few things you can do to experiment with secure surfaces and
+ * observe their behavior.
+ * <ul>
+ * <li>Try taking a screenshot. Either the system will prevent you from taking
+ * a screenshot altogether or the screenshot should not contain the contents
+ * of the secure surface.
+ * <li>Try mirroring the secure surface onto a non-secure display such as an
+ * "Overlay Display" created using the "Simulate secondary displays" option in
+ * the "Developer options" section of the Settings application. The non-secure
+ * secondary display should not show the contents of the secure surface.
+ * <li>Try mirroring the secure surface onto a secure display such as an
+ * HDMI display with HDCP enabled. The contents of the secure surface should appear
+ * on the display.
+ * </ul>
+ * </p>
+ */
+public class SecureSurfaceViewActivity extends Activity {
+ private GLSurfaceView mSurfaceView;
+
+ /**
+ * Initialization of the Activity after it is first created. Must at least
+ * call {@link android.app.Activity#setContentView setContentView()} to
+ * describe what is to be displayed in the screen.
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // Be sure to call the super class.
+ super.onCreate(savedInstanceState);
+
+ // See assets/res/any/layout/secure_surface_view_activity.xml for this
+ // view layout definition, which is being set here as
+ // the content of our screen.
+ setContentView(R.layout.secure_surface_view_activity);
+
+ // Set up the surface view.
+ // We use a GLSurfaceView in this demonstration but ordinary
+ // SurfaceViews also support the same secure surface functionality.
+ mSurfaceView = (GLSurfaceView)findViewById(R.id.surface_view);
+ mSurfaceView.setRenderer(new CubeRenderer(false));
+
+ // Make the surface view secure. This must be done at the time the surface view
+ // is created before the surface view's containing window is attached to
+ // the window manager which happens after onCreate returns.
+ // It cannot be changed later.
+ mSurfaceView.setSecure(true);
+ }
+
+ @Override
+ protected void onResume() {
+ // Be sure to call the super class.
+ super.onResume();
+
+ // Resume rendering.
+ mSurfaceView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ // Be sure to call the super class.
+ super.onPause();
+
+ // Pause rendering.
+ mSurfaceView.onPause();
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/SecureWindowActivity.java b/samples/ApiDemos/src/com/example/android/apis/app/SecureWindowActivity.java
new file mode 100644
index 000000000..4b5e8b672
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/app/SecureWindowActivity.java
@@ -0,0 +1,71 @@
+/*
+ * 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.apis.app;
+
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+/**
+ * <h3>Secure Window Activity</h3>
+ *
+ * <p>
+ * This activity demonstrates how to create an activity whose window is backed by
+ * a secure surface using {@link WindowManager.LayoutParams#FLAG_SECURE}.
+ * Because the surface is secure, its contents cannot be captured in screenshots
+ * and will not be visible on non-secure displays even when mirrored.
+ * </p><p>
+ * Here are a few things you can do to experiment with secure surfaces and
+ * observe their behavior.
+ * <ul>
+ * <li>Try taking a screenshot. Either the system will prevent you from taking
+ * a screenshot altogether or the screenshot should not contain the contents
+ * of the secure surface.
+ * <li>Try mirroring the secure surface onto a non-secure display such as an
+ * "Overlay Display" created using the "Simulate secondary displays" option in
+ * the "Developer options" section of the Settings application. The non-secure
+ * secondary display should not show the contents of the secure surface.
+ * <li>Try mirroring the secure surface onto a secure display such as an
+ * HDMI display with HDCP enabled. The contents of the secure surface should appear
+ * on the display.
+ * </ul>
+ * </p>
+ */
+public class SecureWindowActivity extends Activity {
+ /**
+ * Initialization of the Activity after it is first created. Must at least
+ * call {@link android.app.Activity#setContentView setContentView()} to
+ * describe what is to be displayed in the screen.
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // Be sure to call the super class.
+ super.onCreate(savedInstanceState);
+
+ // See assets/res/any/layout/secure_window_activity.xml for this
+ // view layout definition, which is being set here as
+ // the content of our screen.
+ setContentView(R.layout.secure_window_activity);
+
+ // Make the window secure. This must be done at the time the activity
+ // is created. It cannot be changed later.
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
+ WindowManager.LayoutParams.FLAG_SECURE);
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/content/ChangedContacts.java b/samples/ApiDemos/src/com/example/android/apis/content/ChangedContacts.java
new file mode 100644
index 000000000..412a3c6dc
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/content/ChangedContacts.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.example.android.apis.content;
+
+import android.app.Activity;
+import android.app.LoaderManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.Loader;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.database.CursorWrapper;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CursorAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Demonstrates selecting contacts that have changed since a certain time.
+ */
+public class ChangedContacts extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {
+
+ private static final String CLASS = ChangedContacts.class.getSimpleName();
+
+ private static final String PREF_KEY_CHANGE = "timestamp_change";
+ private static final String PREF_KEY_DELETE = "timestamp_delete";
+
+ private static final int ID_CHANGE_LOADER = 1;
+ private static final int ID_DELETE_LOADER = 2;
+
+ /**
+ * To see this in action, "clear data" for the contacts storage app in the system settings.
+ * Then come into this app and hit any of the delta buttons. This will cause the contacts
+ * database to be re-created.
+ */
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Toast toast = Toast.makeText(context, "Contacts database created.", Toast.LENGTH_SHORT);
+ toast.show();
+ }
+ };
+
+ private DeleteAdapter mDeleteAdapter;
+ private ChangeAdapter mChangeAdapter;
+ private long mSearchTime;
+ private TextView mDisplayView;
+ private ListView mList;
+ private Button mDeleteButton;
+ private Button mChangeButton;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mDeleteAdapter = new DeleteAdapter(this, null, 0);
+ mChangeAdapter = new ChangeAdapter(this, null, 0);
+
+ LinearLayout main = new LinearLayout(this);
+ main.setOrientation(LinearLayout.VERTICAL);
+
+ mChangeButton = new Button(this);
+ mChangeButton.setText("Changed since " + getLastTimestamp(0, PREF_KEY_CHANGE));
+ mChangeButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ changeClick();
+ }
+ });
+
+ mDeleteButton = new Button(this);
+ mDeleteButton.setText("Deleted since " + getLastTimestamp(0, PREF_KEY_DELETE));
+ mDeleteButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ deleteClick();
+ }
+ });
+
+ main.addView(mChangeButton);
+ main.addView(mDeleteButton);
+
+ mDisplayView = new TextView(this);
+ mDisplayView.setPadding(5, 5, 5, 5);
+ main.addView(mDisplayView);
+
+ mList = new ListView(this);
+ mList.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
+ main.addView(mList);
+
+ setContentView(main);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ContactsContract.Intents.CONTACTS_DATABASE_CREATED);
+ registerReceiver(mReceiver, filter);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ unregisterReceiver(mReceiver);
+ }
+
+ private void changeClick() {
+ mChangeAdapter.swapCursor(null);
+ LoaderManager manager = getLoaderManager();
+ manager.destroyLoader(ID_DELETE_LOADER);
+ manager.restartLoader(ID_CHANGE_LOADER, null, this);
+ }
+
+ private void deleteClick() {
+ mChangeAdapter.swapCursor(null);
+ LoaderManager manager = getLoaderManager();
+ manager.destroyLoader(ID_CHANGE_LOADER);
+ manager.restartLoader(ID_DELETE_LOADER, null, this);
+ }
+
+ private void saveLastTimestamp(long time, String key) {
+ SharedPreferences pref = getSharedPreferences(CLASS, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = pref.edit();
+ editor.putLong(key, time);
+ editor.commit();
+ }
+
+ private long getLastTimestamp(long time, String key) {
+ SharedPreferences pref = getSharedPreferences(CLASS, Context.MODE_PRIVATE);
+ return pref.getLong(key, time);
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ switch(id) {
+ case ID_CHANGE_LOADER:
+ return getChangeLoader();
+ case ID_DELETE_LOADER:
+ return getDeleteLoader();
+ }
+ return null;
+ }
+
+ private CursorLoader getChangeLoader() {
+ String[] projection = new String[]{
+ ContactsContract.Data._ID,
+ ContactsContract.Data.CONTACT_ID,
+ ContactsContract.Data.DISPLAY_NAME,
+ ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP
+ };
+
+ mSearchTime = getLastTimestamp(0, PREF_KEY_CHANGE);
+
+ String selection = ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP + " > ?";
+ String[] bindArgs = new String[]{mSearchTime + ""};
+ return new CursorLoader(this, ContactsContract.Data.CONTENT_URI, projection,
+ selection, bindArgs, ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP
+ + " desc, " + ContactsContract.Data.CONTACT_ID + " desc");
+ }
+
+ private CursorLoader getDeleteLoader() {
+ String[] projection = new String[]{
+ ContactsContract.DeletedContacts.CONTACT_ID,
+ ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP
+ };
+
+ mSearchTime = getLastTimestamp(0, PREF_KEY_DELETE);
+
+ String selection = ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP + " > ?";
+ String[] bindArgs = new String[]{mSearchTime + ""};
+ return new CursorLoader(this, ContactsContract.DeletedContacts.CONTENT_URI, projection,
+ selection, bindArgs, ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP +
+ " desc");
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor data) {
+ long timestamp = 0;
+
+
+ switch (cursorLoader.getId()) {
+ case ID_CHANGE_LOADER:
+ mDisplayView.setText(data.getCount() + " change(s) since " + mSearchTime);
+ mList.setAdapter(mChangeAdapter);
+ mChangeAdapter.swapCursor(data);
+
+ // Save the largest timestamp returned. Only need the first one due to the sort
+ // order.
+ if (data.moveToNext()) {
+ timestamp = data.getLong(data.getColumnIndex(
+ ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP));
+ data.moveToPrevious();
+ }
+ if (timestamp > 0) {
+ saveLastTimestamp(timestamp, PREF_KEY_CHANGE);
+ mChangeButton.setText("Changed since " + timestamp);
+ }
+ break;
+ case ID_DELETE_LOADER:
+ mDisplayView.setText(data.getCount() + " delete(s) since " + mSearchTime);
+ mList.setAdapter(mDeleteAdapter);
+ mDeleteAdapter.swapCursor(new DeleteCursorWrapper(data));
+ if (data.moveToNext()) {
+ timestamp = data.getLong(data.getColumnIndex(
+ ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP));
+ data.moveToPrevious();
+ }
+ if (timestamp > 0) {
+ saveLastTimestamp(timestamp, PREF_KEY_DELETE);
+ mDeleteButton.setText("Deleted since " + timestamp);
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> cursorLoader) {
+ mDisplayView.setText("");
+ switch (cursorLoader.getId()) {
+ case ID_CHANGE_LOADER:
+ mChangeAdapter.swapCursor(null);
+ break;
+ case ID_DELETE_LOADER:
+ mDeleteAdapter.swapCursor(null);
+ break;
+ }
+ }
+
+ private class DeleteCursorWrapper extends CursorWrapper {
+
+ /**
+ * Creates a cursor wrapper.
+ *
+ * @param cursor The underlying cursor to wrap.
+ */
+ public DeleteCursorWrapper(Cursor cursor) {
+ super(cursor);
+ }
+
+ @Override
+ public int getColumnIndexOrThrow(String columnName) {
+ if (columnName.equals("_id")) {
+ return super.getColumnIndex(ContactsContract.DeletedContacts.CONTACT_ID);
+ }
+ return super.getColumnIndex(columnName);
+ }
+ }
+
+ private static class DeleteAdapter extends CursorAdapter {
+
+ private Context mContext;
+
+ public DeleteAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+ this.mContext = context;
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ LinearLayout item = new LinearLayout(mContext);
+ item.addView(buildText(context));
+ item.addView(buildText(context));
+ return item;
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ LinearLayout item = (LinearLayout) view;
+ String id = cursor.getString(cursor.getColumnIndex(
+ ContactsContract.DeletedContacts.CONTACT_ID));
+ String timestamp = cursor.getString(cursor.getColumnIndex(
+ ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP));
+
+ setText(item.getChildAt(0), id);
+ setText(item.getChildAt(1), timestamp);
+ }
+ }
+
+ private static class ChangeAdapter extends CursorAdapter {
+
+ private Context mContext;
+
+ public ChangeAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+ mContext = context;
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ LinearLayout item = new LinearLayout(mContext);
+ item.addView(buildText(context));
+ item.addView(buildText(context));
+ item.addView(buildText(context));
+ return item;
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ LinearLayout item = (LinearLayout) view;
+
+ String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.CONTACT_ID));
+ String name = cursor.getString(cursor.getColumnIndex(
+ ContactsContract.Data.DISPLAY_NAME));
+ String timestamp = cursor.getString(cursor.getColumnIndex(
+ ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP));
+
+ setText(item.getChildAt(0), id);
+ setText(item.getChildAt(1), name);
+ setText(item.getChildAt(2), timestamp);
+ }
+ }
+
+ private static void setText(View view, String value) {
+ TextView text = (TextView) view;
+ text.setText(value);
+ }
+
+ private static TextView buildText(Context context) {
+ TextView view = new TextView(context);
+ view.setPadding(3, 3, 3, 3);
+ return view;
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/content/FileProvider.java b/samples/ApiDemos/src/com/example/android/apis/content/FileProvider.java
index 156625a67..f26bcdaf4 100644
--- a/samples/ApiDemos/src/com/example/android/apis/content/FileProvider.java
+++ b/samples/ApiDemos/src/com/example/android/apis/content/FileProvider.java
@@ -26,9 +26,11 @@ import android.content.ContentValues;
import android.content.ContentProvider.PipeDataWriter;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
+import android.database.MatrixCursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import android.provider.OpenableColumns;
import android.util.Log;
/**
@@ -45,8 +47,43 @@ public class FileProvider extends ContentProvider
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
- // Don't support queries.
- return null;
+
+ // content providers that support open and openAssetFile should support queries for all
+ // android.provider.OpenableColumns.
+
+ int displayNameIndex = -1;
+ int sizeIndex = -1;
+
+ // If projection is null, return all columns.
+ if (projection == null) {
+ projection = new String[] {
+ OpenableColumns.DISPLAY_NAME,
+ OpenableColumns.SIZE};
+ }
+
+ for (int i = 0; i < projection.length; i++) {
+ if (OpenableColumns.DISPLAY_NAME.equals(projection[i])) {
+ displayNameIndex = i;
+ }
+ if (OpenableColumns.SIZE.equals(projection[i])) {
+ sizeIndex = i;
+ }
+ }
+
+ MatrixCursor cursor = new MatrixCursor(projection);
+ Object[] result = new Object[projection.length];
+
+ for (int i = 0; i < result.length; i++) {
+ if (i == displayNameIndex) {
+ result[i] = uri.getPath();
+ }
+ if (i == sizeIndex) {
+ result[i] = null; // Size is unknown, so null, if it was known, it would go here.
+ }
+ }
+
+ cursor.addRow(result);
+ return cursor;
}
@Override
diff --git a/samples/ApiDemos/src/com/example/android/apis/content/TextUndoActivity.java b/samples/ApiDemos/src/com/example/android/apis/content/TextUndoActivity.java
new file mode 100644
index 000000000..6b9dc8e62
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/content/TextUndoActivity.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.content;
+
+import android.app.Activity;
+//import android.content.UndoManager;
+import android.os.Parcelable;
+import android.view.View;
+import android.widget.Button;
+import com.example.android.apis.R;
+
+import android.os.Bundle;
+import android.widget.TextView;
+
+/**
+ * Simple example of using an UndoManager for editing text in a TextView.
+ */
+public class TextUndoActivity extends Activity {
+ //UndoManager mUndoManager;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ /*
+ mUndoManager = new UndoManager();
+ if (savedInstanceState != null) {
+ Parcelable p = savedInstanceState.getParcelable("undo");
+ if (p != null) {
+ mUndoManager.restoreInstanceState(p);
+ }
+ }
+ */
+
+ setContentView(R.layout.text_undo);
+
+ /*
+ ((TextView)findViewById(R.id.text)).setUndoManager(mUndoManager, "text");
+ ((Button)findViewById(R.id.undo)).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mUndoManager.undo(null, 1);
+ }
+ });
+ ((Button)findViewById(R.id.redo)).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mUndoManager.redo(null, 1);
+ }
+ });
+ */
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ //outState.putParcelable("undo", mUndoManager.saveInstanceState());
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/CubeRenderer.java b/samples/ApiDemos/src/com/example/android/apis/graphics/CubeRenderer.java
index ac0ae27e3..b30808c53 100644
--- a/samples/ApiDemos/src/com/example/android/apis/graphics/CubeRenderer.java
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/CubeRenderer.java
@@ -25,7 +25,7 @@ import android.opengl.GLSurfaceView;
* Render a pair of tumbling cubes.
*/
-class CubeRenderer implements GLSurfaceView.Renderer {
+public class CubeRenderer implements GLSurfaceView.Renderer {
public CubeRenderer(boolean useTranslucentBackground) {
mTranslucentBackground = useTranslucentBackground;
mCube = new Cube();
diff --git a/samples/ApiDemos/src/com/example/android/apis/hardware/ConsumerIr.java b/samples/ApiDemos/src/com/example/android/apis/hardware/ConsumerIr.java
new file mode 100644
index 000000000..c0ae960ea
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/hardware/ConsumerIr.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 20013The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.hardware;
+
+// Need the following import to get access to the app resources, since this
+// class is in a sub-package.
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.hardware.ConsumerIrManager;
+import android.view.View;
+import android.widget.TextView;
+import android.util.Log;
+
+import com.example.android.apis.R;
+
+/**
+ * App that transmit an IR code
+ *
+ * <p>This demonstrates the {@link android.hardware.ConsumerIrManager android.hardware.ConsumerIrManager} class.
+ *
+ * <h4>Demo</h4>
+ * Hardware / Consumer IR
+ *
+ * <h4>Source files</h4>
+ * <table class="LinkTable">
+ * <tr>
+ * <td>src/com.example.android.apis/hardware/ConsumerIr.java</td>
+ * <td>Consumer IR demo</td>
+ * </tr>
+ * <tr>
+ * <td>res/any/layout/consumer_ir.xml</td>
+ * <td>Defines contents of the screen</td>
+ * </tr>
+ * </table>
+ */
+public class ConsumerIr extends Activity {
+ private static final String TAG = "ConsumerIrTest";
+ TextView mFreqsText;
+ ConsumerIrManager mCIR;
+
+ /**
+ * Initialization of the Activity after it is first created. Must at least
+ * call {@link android.app.Activity#setContentView setContentView()} to
+ * describe what is to be displayed in the screen.
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // Be sure to call the super class.
+ super.onCreate(savedInstanceState);
+
+ // Get a reference to the ConsumerIrManager
+ mCIR = (ConsumerIrManager)getSystemService(Context.CONSUMER_IR_SERVICE);
+
+ // See assets/res/any/layout/consumer_ir.xml for this
+ // view layout definition, which is being set here as
+ // the content of our screen.
+ setContentView(R.layout.consumer_ir);
+
+ // Set the OnClickListener for the button so we see when it's pressed.
+ findViewById(R.id.send_button).setOnClickListener(mSendClickListener);
+ findViewById(R.id.get_freqs_button).setOnClickListener(mGetFreqsClickListener);
+ mFreqsText = (TextView) findViewById(R.id.freqs_text);
+ }
+
+ View.OnClickListener mSendClickListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ if (!mCIR.hasIrEmitter()) {
+ Log.e(TAG, "No IR Emitter found\n");
+ return;
+ }
+
+ // A pattern of alternating series of carrier on and off periods measured in
+ // microseconds.
+ int[] pattern = {1901, 4453, 625, 1614, 625, 1588, 625, 1614, 625, 442, 625, 442, 625,
+ 468, 625, 442, 625, 494, 572, 1614, 625, 1588, 625, 1614, 625, 494, 572, 442, 651,
+ 442, 625, 442, 625, 442, 625, 1614, 625, 1588, 651, 1588, 625, 442, 625, 494, 598,
+ 442, 625, 442, 625, 520, 572, 442, 625, 442, 625, 442, 651, 1588, 625, 1614, 625,
+ 1588, 625, 1614, 625, 1588, 625, 48958};
+
+ // transmit the pattern at 38.4KHz
+ mCIR.transmit(38400, pattern);
+ }
+ };
+
+ View.OnClickListener mGetFreqsClickListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ StringBuilder b = new StringBuilder();
+
+ if (!mCIR.hasIrEmitter()) {
+ mFreqsText.setText("No IR Emitter found!");
+ Log.e(TAG, "No IR Emitter found!\n");
+ return;
+ }
+
+ // Get the available carrier frequency ranges
+ ConsumerIrManager.CarrierFrequencyRange[] freqs = mCIR.getCarrierFrequencies();
+ b.append("IR Carrier Frequencies:\n");
+ for (ConsumerIrManager.CarrierFrequencyRange range : freqs) {
+ b.append(String.format(" %d - %d\n", range.getMinFrequency(),
+ range.getMaxFrequency()));
+ }
+ mFreqsText.setText(b.toString());
+ }
+ };
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/media/VideoViewDemo.java b/samples/ApiDemos/src/com/example/android/apis/media/VideoViewDemo.java
index e72c077a6..dacff56f2 100644
--- a/samples/ApiDemos/src/com/example/android/apis/media/VideoViewDemo.java
+++ b/samples/ApiDemos/src/com/example/android/apis/media/VideoViewDemo.java
@@ -18,6 +18,7 @@ package com.example.android.apis.media;
import com.example.android.apis.R;
import android.app.Activity;
+import android.net.Uri;
import android.os.Bundle;
import android.widget.MediaController;
import android.widget.Toast;
@@ -29,7 +30,6 @@ public class VideoViewDemo extends Activity {
* TODO: Set the path variable to a streaming video URL or a local media
* file path.
*/
- private String path = "";
private VideoView mVideoView;
@Override
@@ -38,24 +38,13 @@ public class VideoViewDemo extends Activity {
setContentView(R.layout.videoview);
mVideoView = (VideoView) findViewById(R.id.surface_view);
- if (path == "") {
- // Tell the user to provide a media file URL/path.
- Toast.makeText(
- VideoViewDemo.this,
- "Please edit VideoViewDemo Activity, and set path"
- + " variable to your media file URL/path",
- Toast.LENGTH_LONG).show();
- } else {
-
- /*
- * Alternatively,for streaming media you can use
- * mVideoView.setVideoURI(Uri.parse(URLstring));
- */
- mVideoView.setVideoPath(path);
- mVideoView.setMediaController(new MediaController(this));
- mVideoView.requestFocus();
-
- }
+ /*
+ * Alternatively, you can use mVideoView.setVideoPath(<path>);
+ */
+ mVideoView.setVideoURI(Uri.parse("android.resource://" + getPackageName() +
+ "/" + R.raw.videoviewdemo));
+ mVideoView.setMediaController(new MediaController(this));
+ mVideoView.requestFocus();
}
}
diff --git a/samples/ApiDemos/src/com/example/android/apis/os/TriggerSensors.java b/samples/ApiDemos/src/com/example/android/apis/os/TriggerSensors.java
new file mode 100644
index 000000000..7d4b4168c
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/os/TriggerSensors.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.os;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.View;
+import android.hardware.Sensor;
+import android.hardware.TriggerEvent;
+import android.hardware.TriggerEventListener;
+import android.hardware.SensorManager;
+import android.os.Bundle;
+import android.widget.TextView;
+import com.example.android.apis.R;
+
+/**
+ * <h3>Application showing the Trigger Sensor API for the Significant Motion sensor. </h3>
+
+<p>This demonstrates the {@link android.hardware.SensorManager android.hardware.SensorManager
+ android.hardware.TriggerEventListener} class.
+
+<h4>Demo</h4>
+OS / TriggerSensors
+
+<h4>Source files</h4>
+ * <table class="LinkTable">
+ * <tr>
+ * <td >src/com.example.android.apis/os/TriggerSensors.java</td>
+ * <td >TriggerSensors</td>
+ * </tr>
+ * </table>
+ */
+
+
+class TriggerListener extends TriggerEventListener {
+ private Context mContext;
+ private TextView mTextView;
+
+ TriggerListener(Context context, TextView textView) {
+ mContext = context;
+ mTextView = textView;
+ }
+
+ @Override
+ public void onTrigger(TriggerEvent event) {
+ if (event.values[0] == 1) {
+ mTextView.append(mContext.getString(R.string.sig_motion) + "\n");
+ mTextView.append(mContext.getString(R.string.sig_motion_auto_disabled) + "\n");
+ }
+ // Sensor is auto disabled.
+ }
+}
+
+public class TriggerSensors extends Activity {
+ private SensorManager mSensorManager;
+ private Sensor mSigMotion;
+ private TriggerListener mListener;
+ private TextView mTextView;
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (mSigMotion != null && mSensorManager.requestTriggerSensor(mListener, mSigMotion))
+ mTextView.append(getString(R.string.sig_motion_enabled) + "\n");
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ // Call disable only if needed for cleanup.
+ // The sensor is auto disabled when triggered.
+ if (mSigMotion != null) mSensorManager.cancelTriggerSensor(mListener, mSigMotion);
+ }
+
+
+ /**
+ * Initialization of the Activity after it is first created. Must at least
+ * call {@link android.app.Activity#setContentView setContentView()} to
+ * describe what is to be displayed in the screen.
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.trigger_sensors);
+ mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
+ mSigMotion = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
+ mTextView = (TextView)findViewById(R.id.text);
+ mListener = new TriggerListener(this, mTextView);
+ if (mSigMotion == null) {
+ mTextView.append(getString(R.string.no_sig_motion) + "\n");
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ if (mSigMotion != null) mSensorManager.cancelTriggerSensor(mListener, mSigMotion);
+ super.onStop();
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/security/KeyStoreUsage.java b/samples/ApiDemos/src/com/example/android/apis/security/KeyStoreUsage.java
new file mode 100644
index 000000000..901806ac5
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/security/KeyStoreUsage.java
@@ -0,0 +1,526 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.example.android.apis.security;
+
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.content.Context;
+import android.database.DataSetObserver;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.security.KeyPairGeneratorSpec;
+import android.util.Base64;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnFocusChangeListener;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStore.PrivateKeyEntry;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.UnrecoverableEntryException;
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.security.auth.x500.X500Principal;
+
+public class KeyStoreUsage extends Activity {
+ private static final String TAG = "AndroidKeyStoreUsage";
+
+ /**
+ * An instance of {@link java.security.KeyStore} through which this app
+ * talks to the {@code AndroidKeyStore}.
+ */
+ KeyStore mKeyStore;
+
+ /**
+ * Used by the {@code ListView} in our layout to list the keys available in
+ * our {@code KeyStore} by their alias names.
+ */
+ AliasAdapter mAdapter;
+
+ /**
+ * Button in the UI that causes a new keypair to be generated in the
+ * {@code KeyStore}.
+ */
+ Button mGenerateButton;
+
+ /**
+ * Button in the UI that causes data to be signed by a key we selected from
+ * the list available in the {@code KeyStore}.
+ */
+ Button mSignButton;
+
+ /**
+ * Button in the UI that causes data to be signed by a key we selected from
+ * the list available in the {@code KeyStore}.
+ */
+ Button mVerifyButton;
+
+ /**
+ * Button in the UI that causes a key entry to be deleted from the
+ * {@code KeyStore}.
+ */
+ Button mDeleteButton;
+
+ /**
+ * Text field in the UI that holds plaintext.
+ */
+ EditText mPlainText;
+
+ /**
+ * Text field in the UI that holds the signature.
+ */
+ EditText mCipherText;
+
+ /**
+ * The alias of the selected entry in the KeyStore.
+ */
+ private String mSelectedAlias;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.keystore_usage);
+
+ /*
+ * Set up our {@code ListView} with an adapter that allows
+ * us to choose from the available entry aliases.
+ */
+ ListView lv = (ListView) findViewById(R.id.entries_list);
+ mAdapter = new AliasAdapter(getApplicationContext());
+ lv.setAdapter(mAdapter);
+ lv.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ mSelectedAlias = mAdapter.getItem(position);
+ setKeyActionButtonsEnabled(true);
+ }
+ });
+
+ // This is alias the user wants for a generated key.
+ final EditText aliasInput = (EditText) findViewById(R.id.entry_name);
+ mGenerateButton = (Button) findViewById(R.id.generate_button);
+ mGenerateButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ /*
+ * When the user presses the "Generate" button, we'll
+ * check the alias isn't blank here.
+ */
+ final String alias = aliasInput.getText().toString();
+ if (alias == null || alias.length() == 0) {
+ aliasInput.setError(getResources().getText(R.string.keystore_no_alias_error));
+ } else {
+ /*
+ * It's not blank, so disable the generate button while
+ * the generation of the key is happening. It will be
+ * enabled by the {@code AsyncTask} later after its
+ * work is done.
+ */
+ aliasInput.setError(null);
+ mGenerateButton.setEnabled(false);
+ new GenerateTask().execute(alias);
+ }
+ }
+ });
+
+ mSignButton = (Button) findViewById(R.id.sign_button);
+ mSignButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final String alias = mSelectedAlias;
+ final String data = mPlainText.getText().toString();
+ if (alias != null) {
+ setKeyActionButtonsEnabled(false);
+ new SignTask().execute(alias, data);
+ }
+ }
+ });
+
+ mVerifyButton = (Button) findViewById(R.id.verify_button);
+ mVerifyButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final String alias = mSelectedAlias;
+ final String data = mPlainText.getText().toString();
+ final String signature = mCipherText.getText().toString();
+ if (alias != null) {
+ setKeyActionButtonsEnabled(false);
+ new VerifyTask().execute(alias, data, signature);
+ }
+ }
+ });
+
+ mDeleteButton = (Button) findViewById(R.id.delete_button);
+ mDeleteButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final String alias = mSelectedAlias;
+ if (alias != null) {
+ setKeyActionButtonsEnabled(false);
+ new DeleteTask().execute(alias);
+ }
+ }
+ });
+
+ mPlainText = (EditText) findViewById(R.id.plaintext);
+ mPlainText.setOnFocusChangeListener(new OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mPlainText.setTextColor(getResources().getColor(android.R.color.primary_text_dark));
+ }
+ });
+
+ mCipherText = (EditText) findViewById(R.id.ciphertext);
+ mCipherText.setOnFocusChangeListener(new OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mCipherText
+ .setTextColor(getResources().getColor(android.R.color.primary_text_dark));
+ }
+ });
+
+ updateKeyList();
+ }
+
+ private class AliasAdapter extends ArrayAdapter<String> {
+ public AliasAdapter(Context context) {
+ // We want users to choose a key, so use the appropriate layout.
+ super(context, android.R.layout.simple_list_item_single_choice);
+ }
+
+ /**
+ * This clears out all previous aliases and replaces it with the
+ * current entries.
+ */
+ public void setAliases(List<String> items) {
+ clear();
+ addAll(items);
+ notifyDataSetChanged();
+ }
+ }
+
+ private void updateKeyList() {
+ setKeyActionButtonsEnabled(false);
+ new UpdateKeyListTask().execute();
+ }
+
+ /**
+ * Sets all the buttons related to actions that act on an existing key to
+ * enabled or disabled.
+ */
+ private void setKeyActionButtonsEnabled(boolean enabled) {
+ mSignButton.setEnabled(enabled);
+ mVerifyButton.setEnabled(enabled);
+ mDeleteButton.setEnabled(enabled);
+ }
+
+ private class UpdateKeyListTask extends AsyncTask<Void, Void, Enumeration<String>> {
+ @Override
+ protected Enumeration<String> doInBackground(Void... params) {
+ try {
+// BEGIN_INCLUDE(list)
+ /*
+ * Load the Android KeyStore instance using the the
+ * "AndroidKeyStore" provider to list out what entries are
+ * currently stored.
+ */
+ KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
+ ks.load(null);
+ Enumeration<String> aliases = ks.aliases();
+// END_INCLUDE(list)
+ return aliases;
+ } catch (KeyStoreException e) {
+ Log.w(TAG, "Could not list keys", e);
+ return null;
+ } catch (NoSuchAlgorithmException e) {
+ Log.w(TAG, "Could not list keys", e);
+ return null;
+ } catch (CertificateException e) {
+ Log.w(TAG, "Could not list keys", e);
+ return null;
+ } catch (IOException e) {
+ Log.w(TAG, "Could not list keys", e);
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Enumeration<String> result) {
+ List<String> aliases = new ArrayList<String>();
+ while (result.hasMoreElements()) {
+ aliases.add(result.nextElement());
+ }
+ mAdapter.setAliases(aliases);
+ }
+ }
+
+ private class GenerateTask extends AsyncTask<String, Void, Boolean> {
+ @Override
+ protected Boolean doInBackground(String... params) {
+ final String alias = params[0];
+ try {
+// BEGIN_INCLUDE(generate)
+ /*
+ * Generate a new entry in the KeyStore by using the
+ * KeyPairGenerator API. We have to specify the attributes for a
+ * self-signed X.509 certificate here so the KeyStore can attach
+ * the public key part to it. It can be replaced later with a
+ * certificate signed by a Certificate Authority (CA) if needed.
+ */
+ Calendar cal = Calendar.getInstance();
+ Date now = cal.getTime();
+ cal.add(Calendar.YEAR, 1);
+ Date end = cal.getTime();
+
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
+ kpg.initialize(new KeyPairGeneratorSpec.Builder(getApplicationContext())
+ .setAlias(alias)
+ .setStartDate(now)
+ .setEndDate(end)
+ .setSerialNumber(BigInteger.valueOf(1))
+ .setSubject(new X500Principal("CN=test1"))
+ .build());
+
+ KeyPair kp = kpg.generateKeyPair();
+// END_INCLUDE(generate)
+ return true;
+ } catch (NoSuchAlgorithmException e) {
+ Log.w(TAG, "Could not generate key", e);
+ return false;
+ } catch (InvalidAlgorithmParameterException e) {
+ Log.w(TAG, "Could not generate key", e);
+ return false;
+ } catch (NoSuchProviderException e) {
+ Log.w(TAG, "Could not generate key", e);
+ return false;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ updateKeyList();
+ mGenerateButton.setEnabled(true);
+ }
+
+ @Override
+ protected void onCancelled() {
+ mGenerateButton.setEnabled(true);
+ }
+ }
+
+ private class SignTask extends AsyncTask<String, Void, String> {
+ @Override
+ protected String doInBackground(String... params) {
+ final String alias = params[0];
+ final String dataString = params[1];
+ try {
+ byte[] data = dataString.getBytes();
+// BEGIN_INCLUDE(sign)
+ /*
+ * Use a PrivateKey in the KeyStore to create a signature over
+ * some data.
+ */
+ KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
+ ks.load(null);
+ KeyStore.Entry entry = ks.getEntry(alias, null);
+ if (!(entry instanceof PrivateKeyEntry)) {
+ Log.w(TAG, "Not an instance of a PrivateKeyEntry");
+ return null;
+ }
+ Signature s = Signature.getInstance("SHA256withRSA");
+ s.initSign(((PrivateKeyEntry) entry).getPrivateKey());
+ s.update(data);
+ byte[] signature = s.sign();
+// END_INCLUDE(sign)
+ return Base64.encodeToString(signature, Base64.DEFAULT);
+ } catch (NoSuchAlgorithmException e) {
+ Log.w(TAG, "Could not generate key", e);
+ return null;
+ } catch (KeyStoreException e) {
+ Log.w(TAG, "Could not generate key", e);
+ return null;
+ } catch (CertificateException e) {
+ Log.w(TAG, "Could not generate key", e);
+ return null;
+ } catch (IOException e) {
+ Log.w(TAG, "Could not generate key", e);
+ return null;
+ } catch (UnrecoverableEntryException e) {
+ Log.w(TAG, "Could not generate key", e);
+ return null;
+ } catch (InvalidKeyException e) {
+ Log.w(TAG, "Could not generate key", e);
+ return null;
+ } catch (SignatureException e) {
+ Log.w(TAG, "Could not generate key", e);
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(String result) {
+ mCipherText.setText(result);
+ setKeyActionButtonsEnabled(true);
+ }
+
+ @Override
+ protected void onCancelled() {
+ mCipherText.setText("error!");
+ setKeyActionButtonsEnabled(true);
+ }
+ }
+
+ private class VerifyTask extends AsyncTask<String, Void, Boolean> {
+ @Override
+ protected Boolean doInBackground(String... params) {
+ final String alias = params[0];
+ final String dataString = params[1];
+ final String signatureString = params[2];
+ try {
+ byte[] data = dataString.getBytes();
+ byte[] signature;
+ try {
+ signature = Base64.decode(signatureString, Base64.DEFAULT);
+ } catch (IllegalArgumentException e) {
+ signature = new byte[0];
+ }
+// BEGIN_INCLUDE(verify)
+ /*
+ * Verify a signature previously made by a PrivateKey in our
+ * KeyStore. This uses the X.509 certificate attached to our
+ * private key in the KeyStore to validate a previously
+ * generated signature.
+ */
+ KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
+ ks.load(null);
+ KeyStore.Entry entry = ks.getEntry(alias, null);
+ if (!(entry instanceof PrivateKeyEntry)) {
+ Log.w(TAG, "Not an instance of a PrivateKeyEntry");
+ return false;
+ }
+ Signature s = Signature.getInstance("SHA256withRSA");
+ s.initVerify(((PrivateKeyEntry) entry).getCertificate());
+ s.update(data);
+ boolean valid = s.verify(signature);
+// END_INCLUDE(verify)
+ return valid;
+ } catch (NoSuchAlgorithmException e) {
+ Log.w(TAG, "Could not generate key", e);
+ return false;
+ } catch (KeyStoreException e) {
+ Log.w(TAG, "Could not generate key", e);
+ return false;
+ } catch (CertificateException e) {
+ Log.w(TAG, "Could not generate key", e);
+ return false;
+ } catch (IOException e) {
+ Log.w(TAG, "Could not generate key", e);
+ return false;
+ } catch (UnrecoverableEntryException e) {
+ Log.w(TAG, "Could not generate key", e);
+ return false;
+ } catch (InvalidKeyException e) {
+ Log.w(TAG, "Could not generate key", e);
+ return false;
+ } catch (SignatureException e) {
+ Log.w(TAG, "Could not generate key", e);
+ return false;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ if (result) {
+ mCipherText.setTextColor(getResources().getColor(R.color.solid_green));
+ } else {
+ mCipherText.setTextColor(getResources().getColor(R.color.solid_red));
+ }
+ setKeyActionButtonsEnabled(true);
+ }
+
+ @Override
+ protected void onCancelled() {
+ mCipherText.setText("error!");
+ setKeyActionButtonsEnabled(true);
+ mCipherText.setTextColor(getResources().getColor(android.R.color.primary_text_dark));
+ }
+ }
+
+ private class DeleteTask extends AsyncTask<String, Void, Void> {
+ @Override
+ protected Void doInBackground(String... params) {
+ final String alias = params[0];
+ try {
+// BEGIN_INCLUDE(delete)
+ /*
+ * Deletes a previously generated or stored entry in the
+ * KeyStore.
+ */
+ KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
+ ks.load(null);
+ ks.deleteEntry(alias);
+// END_INCLUDE(delete)
+ } catch (NoSuchAlgorithmException e) {
+ Log.w(TAG, "Could not generate key", e);
+ } catch (KeyStoreException e) {
+ Log.w(TAG, "Could not generate key", e);
+ } catch (CertificateException e) {
+ Log.w(TAG, "Could not generate key", e);
+ } catch (IOException e) {
+ Log.w(TAG, "Could not generate key", e);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ updateKeyList();
+ }
+
+ @Override
+ protected void onCancelled() {
+ updateKeyList();
+ }
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/ContentBrowserNavActivity.java b/samples/ApiDemos/src/com/example/android/apis/view/ContentBrowserNavActivity.java
new file mode 100644
index 000000000..2c5f7bf20
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/view/ContentBrowserNavActivity.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.view;
+
+import android.app.ActionBar;
+import android.app.ActionBar.Tab;
+import android.app.Activity;
+import android.app.FragmentTransaction;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ScrollView;
+import android.widget.SearchView;
+import android.widget.SeekBar;
+import android.widget.ShareActionProvider;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.SearchView.OnQueryTextListener;
+
+import com.example.android.apis.R;
+
+/**
+ * This activity demonstrates how to use system UI flags to implement
+ * a content browser style of UI (such as a book reader) that hides the
+ * nav bar as well as the status bar.
+ */
+public class ContentBrowserNavActivity extends Activity
+ implements OnQueryTextListener, ActionBar.TabListener {
+
+ /**
+ * Implementation of a view for displaying immersive content, using system UI
+ * flags to transition in and out of modes where the user is focused on that
+ * content.
+ */
+//BEGIN_INCLUDE(content)
+ public static class Content extends ScrollView
+ implements View.OnSystemUiVisibilityChangeListener, View.OnClickListener {
+ TextView mText;
+ TextView mTitleView;
+ SeekBar mSeekView;
+ boolean mNavVisible;
+ int mBaseSystemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | SYSTEM_UI_FLAG_LAYOUT_STABLE | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+ int mLastSystemUiVis;
+
+ Runnable mNavHider = new Runnable() {
+ @Override public void run() {
+ setNavVisibility(false);
+ }
+ };
+
+ public Content(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mText = new TextView(context);
+ mText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
+ mText.setText(context.getString(R.string.alert_dialog_two_buttons2ultra_msg));
+ mText.setClickable(false);
+ mText.setOnClickListener(this);
+ mText.setTextIsSelectable(true);
+ addView(mText, new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+
+ setOnSystemUiVisibilityChangeListener(this);
+ }
+
+ public void init(TextView title, SeekBar seek) {
+ // This called by the containing activity to supply the surrounding
+ // state of the content browser that it will interact with.
+ mTitleView = title;
+ mSeekView = seek;
+ setNavVisibility(true);
+ }
+
+ @Override public void onSystemUiVisibilityChange(int visibility) {
+ // Detect when we go out of low-profile mode, to also go out
+ // of full screen. We only do this when the low profile mode
+ // is changing from its last state, and turning off.
+ int diff = mLastSystemUiVis ^ visibility;
+ mLastSystemUiVis = visibility;
+ if ((diff&SYSTEM_UI_FLAG_LOW_PROFILE) != 0
+ && (visibility&SYSTEM_UI_FLAG_LOW_PROFILE) == 0) {
+ setNavVisibility(true);
+ }
+ }
+
+ @Override protected void onWindowVisibilityChanged(int visibility) {
+ super.onWindowVisibilityChanged(visibility);
+
+ // When we become visible, we show our navigation elements briefly
+ // before hiding them.
+ setNavVisibility(true);
+ getHandler().postDelayed(mNavHider, 2000);
+ }
+
+ @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+ super.onScrollChanged(l, t, oldl, oldt);
+
+ // When the user scrolls, we hide navigation elements.
+ setNavVisibility(false);
+ }
+
+ @Override public void onClick(View v) {
+ // When the user clicks, we toggle the visibility of navigation elements.
+ int curVis = getSystemUiVisibility();
+ setNavVisibility((curVis&SYSTEM_UI_FLAG_LOW_PROFILE) != 0);
+ }
+
+ void setBaseSystemUiVisibility(int visibility) {
+ mBaseSystemUiVisibility = visibility;
+ }
+
+ void setNavVisibility(boolean visible) {
+ int newVis = mBaseSystemUiVisibility;
+ if (!visible) {
+ newVis |= SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_FULLSCREEN
+ | SYSTEM_UI_FLAG_HIDE_NAVIGATION | SYSTEM_UI_FLAG_IMMERSIVE;
+ }
+ final boolean changed = newVis == getSystemUiVisibility();
+
+ // Unschedule any pending event to hide navigation if we are
+ // changing the visibility, or making the UI visible.
+ if (changed || visible) {
+ Handler h = getHandler();
+ if (h != null) {
+ h.removeCallbacks(mNavHider);
+ }
+ }
+
+ // Set the new desired visibility.
+ setSystemUiVisibility(newVis);
+ mTitleView.setVisibility(visible ? VISIBLE : INVISIBLE);
+ mSeekView.setVisibility(visible ? VISIBLE : INVISIBLE);
+ }
+ }
+//END_INCLUDE(content)
+
+ Content mContent;
+
+ public ContentBrowserNavActivity() {
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
+
+ setContentView(R.layout.content_browser_nav);
+ mContent = (Content)findViewById(R.id.content);
+ mContent.init((TextView)findViewById(R.id.title),
+ (SeekBar)findViewById(R.id.seekbar));
+
+ ActionBar bar = getActionBar();
+ bar.addTab(bar.newTab().setText("Tab 1").setTabListener(this));
+ bar.addTab(bar.newTab().setText("Tab 2").setTabListener(this));
+ bar.addTab(bar.newTab().setText("Tab 3").setTabListener(this));
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.content_actions, menu);
+ SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
+ searchView.setOnQueryTextListener(this);
+
+ // Set file with share history to the provider and set the share intent.
+ MenuItem actionItem = menu.findItem(R.id.menu_item_share_action_provider_action_bar);
+ ShareActionProvider actionProvider = (ShareActionProvider) actionItem.getActionProvider();
+ actionProvider.setShareHistoryFileName(ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME);
+ // Note that you can set/change the intent any time,
+ // say when the user has selected an image.
+ Intent shareIntent = new Intent(Intent.ACTION_SEND);
+ shareIntent.setType("image/*");
+ Uri uri = Uri.fromFile(getFileStreamPath("shared.png"));
+ shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
+ actionProvider.setShareIntent(shareIntent);
+ return true;
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ /**
+ * This method is declared in the menu.
+ */
+ public void onSort(MenuItem item) {
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.show_tabs:
+ getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+ item.setChecked(true);
+ return true;
+ case R.id.hide_tabs:
+ getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
+ item.setChecked(true);
+ return true;
+ case R.id.stable_layout:
+ item.setChecked(!item.isChecked());
+ mContent.setBaseSystemUiVisibility(item.isChecked()
+ ? View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ : View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ Toast.makeText(this, "Searching for: " + query + "...", Toast.LENGTH_SHORT).show();
+ return true;
+ }
+
+ @Override
+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
+ }
+
+ @Override
+ public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+ }
+
+ @Override
+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/CustomLayout.java b/samples/ApiDemos/src/com/example/android/apis/view/CustomLayout.java
new file mode 100644
index 000000000..dfd886a60
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/view/CustomLayout.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.view;
+
+// Need the following import to get access to the app resources, since this
+// class is in a sub-package.
+import android.graphics.Rect;
+import com.example.android.apis.R;
+
+//BEGIN_INCLUDE(Complete)
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RemoteViews;
+
+/**
+ * Example of writing a custom layout manager. This is a fairly full-featured
+ * layout manager that is relatively general, handling all layout cases. You
+ * can simplify it for more specific cases.
+ */
+@RemoteViews.RemoteView
+public class CustomLayout extends ViewGroup {
+ /** The amount of space used by children in the left gutter. */
+ private int mLeftWidth;
+
+ /** The amount of space used by children in the right gutter. */
+ private int mRightWidth;
+
+ /** These are used for computing child frames based on their gravity. */
+ private final Rect mTmpContainerRect = new Rect();
+ private final Rect mTmpChildRect = new Rect();
+
+ public CustomLayout(Context context) {
+ super(context);
+ }
+
+ public CustomLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CustomLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * Any layout manager that doesn't scroll will want this.
+ */
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return false;
+ }
+
+ /**
+ * Ask all children to measure themselves and compute the measurement of this
+ * layout based on the children.
+ */
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int count = getChildCount();
+
+ // These keep track of the space we are using on the left and right for
+ // views positioned there; we need member variables so we can also use
+ // these for layout later.
+ mLeftWidth = 0;
+ mRightWidth = 0;
+
+ // Measurement will ultimately be computing these values.
+ int maxHeight = 0;
+ int maxWidth = 0;
+ int childState = 0;
+
+ // Iterate through all children, measuring them and computing our dimensions
+ // from their size.
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ // Measure the child.
+ measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
+
+ // Update our size information based on the layout params. Children
+ // that asked to be positioned on the left or right go in those gutters.
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp.position == LayoutParams.POSITION_LEFT) {
+ mLeftWidth += Math.max(maxWidth,
+ child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
+ } else if (lp.position == LayoutParams.POSITION_RIGHT) {
+ mRightWidth += Math.max(maxWidth,
+ child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
+ } else {
+ maxWidth = Math.max(maxWidth,
+ child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
+ }
+ maxHeight = Math.max(maxHeight,
+ child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
+ childState = combineMeasuredStates(childState, child.getMeasuredState());
+ }
+ }
+
+ // Total width is the maximum width of all inner children plus the gutters.
+ maxWidth += mLeftWidth + mRightWidth;
+
+ // Check against our minimum height and width
+ maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+ maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+ // Report our final dimensions.
+ setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
+ resolveSizeAndState(maxHeight, heightMeasureSpec,
+ childState << MEASURED_HEIGHT_STATE_SHIFT));
+ }
+
+ /**
+ * Position all children within this layout.
+ */
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ final int count = getChildCount();
+
+ // These are the far left and right edges in which we are performing layout.
+ int leftPos = getPaddingLeft();
+ int rightPos = right - left - getPaddingRight();
+
+ // This is the middle region inside of the gutter.
+ final int middleLeft = leftPos + mLeftWidth;
+ final int middleRight = rightPos - mRightWidth;
+
+ // These are the top and bottom edges in which we are performing layout.
+ final int parentTop = getPaddingTop();
+ final int parentBottom = bottom - top - getPaddingBottom();
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ final int width = child.getMeasuredWidth();
+ final int height = child.getMeasuredHeight();
+
+ // Compute the frame in which we are placing this child.
+ if (lp.position == LayoutParams.POSITION_LEFT) {
+ mTmpContainerRect.left = leftPos + lp.leftMargin;
+ mTmpContainerRect.right = leftPos + width + lp.rightMargin;
+ leftPos = mTmpContainerRect.right;
+ } else if (lp.position == LayoutParams.POSITION_RIGHT) {
+ mTmpContainerRect.right = rightPos - lp.rightMargin;
+ mTmpContainerRect.left = rightPos - width - lp.leftMargin;
+ rightPos = mTmpContainerRect.left;
+ } else {
+ mTmpContainerRect.left = middleLeft + lp.leftMargin;
+ mTmpContainerRect.right = middleRight - lp.rightMargin;
+ }
+ mTmpContainerRect.top = parentTop + lp.topMargin;
+ mTmpContainerRect.bottom = parentBottom - lp.bottomMargin;
+
+ // Use the child's gravity and size to determine its final
+ // frame within its container.
+ Gravity.apply(lp.gravity, width, height, mTmpContainerRect, mTmpChildRect);
+
+ // Place the child.
+ child.layout(mTmpChildRect.left, mTmpChildRect.top,
+ mTmpChildRect.right, mTmpChildRect.bottom);
+ }
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ // The rest of the implementation is for custom per-child layout parameters.
+ // If you do not need these (for example you are writing a layout manager
+ // that does fixed positioning of its children), you can drop all of this.
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new CustomLayout.LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected LayoutParams generateDefaultLayoutParams() {
+ return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ return new LayoutParams(p);
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams;
+ }
+
+ /**
+ * Custom per-child layout information.
+ */
+ public static class LayoutParams extends MarginLayoutParams {
+ /**
+ * The gravity to apply with the View to which these layout parameters
+ * are associated.
+ */
+ public int gravity = Gravity.TOP | Gravity.START;
+
+ public static int POSITION_MIDDLE = 0;
+ public static int POSITION_LEFT = 1;
+ public static int POSITION_RIGHT = 2;
+
+ public int position = POSITION_MIDDLE;
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+
+ // Pull the layout param values from the layout XML during
+ // inflation. This is not needed if you don't care about
+ // changing the layout behavior in XML.
+ TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.CustomLayoutLP);
+ gravity = a.getInt(R.styleable.CustomLayoutLP_android_layout_gravity, gravity);
+ position = a.getInt(R.styleable.CustomLayoutLP_layout_position, position);
+ a.recycle();
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(ViewGroup.LayoutParams source) {
+ super(source);
+ }
+ }
+}
+//END_INCLUDE(Complete)
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/CustomLayoutActivity.java b/samples/ApiDemos/src/com/example/android/apis/view/CustomLayoutActivity.java
new file mode 100644
index 000000000..171656933
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/view/CustomLayoutActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.view;
+
+// Need the following import to get access to the app resources, since this
+// class is in a sub-package.
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class CustomLayoutActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.custom_layout);
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/DateWidgets1.java b/samples/ApiDemos/src/com/example/android/apis/view/DateWidgets1.java
index f0b1d222e..537bda896 100644
--- a/samples/ApiDemos/src/com/example/android/apis/view/DateWidgets1.java
+++ b/samples/ApiDemos/src/com/example/android/apis/view/DateWidgets1.java
@@ -51,8 +51,9 @@ public class DateWidgets1 extends Activity {
private int mHour;
private int mMinute;
- static final int TIME_DIALOG_ID = 0;
- static final int DATE_DIALOG_ID = 1;
+ static final int TIME_12_DIALOG_ID = 0;
+ static final int TIME_24_DIALOG_ID = 1;
+ static final int DATE_DIALOG_ID = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -62,21 +63,9 @@ public class DateWidgets1 extends Activity {
mDateDisplay = (TextView) findViewById(R.id.dateDisplay);
- Button pickDate = (Button) findViewById(R.id.pickDate);
- pickDate.setOnClickListener(new View.OnClickListener() {
-
- public void onClick(View v) {
- showDialog(DATE_DIALOG_ID);
- }
- });
-
- Button pickTime = (Button) findViewById(R.id.pickTime);
- pickTime.setOnClickListener(new View.OnClickListener() {
-
- public void onClick(View v) {
- showDialog(TIME_DIALOG_ID);
- }
- });
+ setDialogOnClickListener(R.id.pickDate, DATE_DIALOG_ID);
+ setDialogOnClickListener(R.id.pickTime12, TIME_12_DIALOG_ID);
+ setDialogOnClickListener(R.id.pickTime24, TIME_24_DIALOG_ID);
final Calendar c = Calendar.getInstance();
mYear = c.get(Calendar.YEAR);
@@ -88,12 +77,22 @@ public class DateWidgets1 extends Activity {
updateDisplay();
}
+ private void setDialogOnClickListener(int buttonId, final int dialogId) {
+ Button b = (Button) findViewById(buttonId);
+ b.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ showDialog(dialogId);
+ }
+ });
+ }
+
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
- case TIME_DIALOG_ID:
+ case TIME_12_DIALOG_ID:
+ case TIME_24_DIALOG_ID:
return new TimePickerDialog(this,
- mTimeSetListener, mHour, mMinute, false);
+ mTimeSetListener, mHour, mMinute, id == TIME_24_DIALOG_ID);
case DATE_DIALOG_ID:
return new DatePickerDialog(this,
mDateSetListener,
@@ -105,7 +104,8 @@ public class DateWidgets1 extends Activity {
@Override
protected void onPrepareDialog(int id, Dialog dialog) {
switch (id) {
- case TIME_DIALOG_ID:
+ case TIME_12_DIALOG_ID:
+ case TIME_24_DIALOG_ID:
((TimePickerDialog) dialog).updateTime(mHour, mMinute);
break;
case DATE_DIALOG_ID:
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/ExpandableList1.java b/samples/ApiDemos/src/com/example/android/apis/view/ExpandableList1.java
index bda366beb..ef8c03c66 100644
--- a/samples/ApiDemos/src/com/example/android/apis/view/ExpandableList1.java
+++ b/samples/ApiDemos/src/com/example/android/apis/view/ExpandableList1.java
@@ -118,7 +118,9 @@ public class ExpandableList1 extends ExpandableListActivity {
// Center the text vertically
textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
// Set the text starting position
- textView.setPadding(36, 0, 0, 0);
+ textView.setPaddingRelative(36, 0, 0, 0);
+ // Set the text alignment
+ textView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
return textView;
}
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/GameControllerInput.java b/samples/ApiDemos/src/com/example/android/apis/view/GameControllerInput.java
index ce7c749df..10b2f9a72 100644
--- a/samples/ApiDemos/src/com/example/android/apis/view/GameControllerInput.java
+++ b/samples/ApiDemos/src/com/example/android/apis/view/GameControllerInput.java
@@ -142,7 +142,7 @@ public class GameControllerInput extends Activity
public boolean dispatchGenericMotionEvent(MotionEvent event) {
// Check that the event came from a joystick since a generic motion event
// could be almost anything.
- if (isJoystick(event.getSource())
+ if (event.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK)
&& event.getAction() == MotionEvent.ACTION_MOVE) {
// Update device state for visualization and logging.
InputDeviceState state = getInputDeviceState(event.getDeviceId());
@@ -195,10 +195,6 @@ public class GameControllerInput extends Activity
}
}
- private static boolean isJoystick(int source) {
- return (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0;
- }
-
/**
* Tracks the state of joystick axes and game controller buttons for a particular
* input device for diagnostic purposes.
@@ -215,7 +211,7 @@ public class GameControllerInput extends Activity
int numAxes = 0;
final List<MotionRange> ranges = device.getMotionRanges();
for (MotionRange range : ranges) {
- if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+ if (range.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK)) {
numAxes += 1;
}
}
@@ -224,7 +220,7 @@ public class GameControllerInput extends Activity
mAxisValues = new float[numAxes];
int i = 0;
for (MotionRange range : ranges) {
- if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+ if (range.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK)) {
mAxes[i++] = range.getAxis();
}
}
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/GameView.java b/samples/ApiDemos/src/com/example/android/apis/view/GameView.java
index 2904147ef..d7f039bdd 100644
--- a/samples/ApiDemos/src/com/example/android/apis/view/GameView.java
+++ b/samples/ApiDemos/src/com/example/android/apis/view/GameView.java
@@ -209,7 +209,7 @@ public class GameView extends View {
// Check that the event came from a joystick since a generic motion event
// could be almost anything.
- if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0
+ if (event.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK)
&& event.getAction() == MotionEvent.ACTION_MOVE) {
// Cache the most recently obtained device information.
// The device information may change over time but it can be
@@ -771,4 +771,4 @@ public class GameView extends View {
return 0.25f;
}
}
-} \ No newline at end of file
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/GridLayout1.java b/samples/ApiDemos/src/com/example/android/apis/view/GridLayout1.java
index f4318e454..3221fa0d7 100644
--- a/samples/ApiDemos/src/com/example/android/apis/view/GridLayout1.java
+++ b/samples/ApiDemos/src/com/example/android/apis/view/GridLayout1.java
@@ -16,13 +16,13 @@
package com.example.android.apis.view;
-import com.example.android.apis.R;
-
import android.app.Activity;
import android.os.Bundle;
+import com.example.android.apis.R;
/**
- * A simple form, showing use of the GridLayout API from XML.
+ * Demonstrates using GridLayout to build the same "Simple Form" as in the
+ * LinearLayout and RelativeLayout demos.
*/
public class GridLayout1 extends Activity {
protected void onCreate(Bundle savedInstanceState) {
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/GridLayout2.java b/samples/ApiDemos/src/com/example/android/apis/view/GridLayout2.java
new file mode 100644
index 000000000..2ad09f497
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/view/GridLayout2.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.view;
+
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * A form, showing use of the GridLayout API from XML.
+ */
+public class GridLayout2 extends Activity {
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.grid_layout_2);
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/GridLayout0.java b/samples/ApiDemos/src/com/example/android/apis/view/GridLayout3.java
index 70de1ac39..48a55467a 100644
--- a/samples/ApiDemos/src/com/example/android/apis/view/GridLayout0.java
+++ b/samples/ApiDemos/src/com/example/android/apis/view/GridLayout3.java
@@ -18,6 +18,7 @@ package com.example.android.apis.view;
import android.app.Activity;
import android.content.Context;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.view.View;
@@ -25,81 +26,84 @@ import android.widget.*;
import static android.text.InputType.*;
import static android.widget.GridLayout.*;
+import static android.widget.GridLayout.LayoutParams;
/**
- * A simple form, showing use of the GridLayout API.
+ * A form, showing use of the GridLayout API. Here we demonstrate use of the row/column order
+ * preserved property which allows rows and or columns to pass over each other when needed.
+ * The two buttons in the bottom right corner need to be separated from the other UI elements.
+ * This can either be done by separating rows or separating columns - but we don't need
+ * to do both and may only have enough space to do one or the other.
*/
-public class GridLayout0 extends Activity {
-
+public class GridLayout3 extends Activity {
public static View create(Context context) {
GridLayout p = new GridLayout(context);
p.setUseDefaultMargins(true);
p.setAlignmentMode(ALIGN_BOUNDS);
- p.setRowOrderPreserved(false);
+ Configuration configuration = context.getResources().getConfiguration();
+ if ((configuration.orientation == Configuration.ORIENTATION_PORTRAIT)) {
+ p.setColumnOrderPreserved(false);
+ } else {
+ p.setRowOrderPreserved(false);
+ }
- Spec row1 = spec(0);
- Spec row2 = spec(1);
- Spec row3 = spec(2, BASELINE);
- Spec row4 = spec(3, BASELINE);
- Spec row5 = spec(2, 3, FILL); // allow the last two rows to overlap the middle two
- Spec row6 = spec(5);
- Spec row7 = spec(6);
+ Spec titleRow = spec(0);
+ Spec introRow = spec(1);
+ Spec emailRow = spec(2, BASELINE);
+ Spec passwordRow = spec(3, BASELINE);
+ Spec button1Row = spec(5);
+ Spec button2Row = spec(6);
- Spec col1a = spec(0, 4, CENTER);
- Spec col1b = spec(0, 4, LEFT);
- Spec col1c = spec(0, RIGHT);
- Spec col2 = spec(1, LEFT);
- Spec col3 = spec(2, FILL);
- Spec col4a = spec(3);
- Spec col4b = spec(3, FILL);
+ Spec centerInAllColumns = spec(0, 4, CENTER);
+ Spec leftAlignInAllColumns = spec(0, 4, LEFT);
+ Spec labelColumn = spec(0, RIGHT);
+ Spec fieldColumn = spec(1, LEFT);
+ Spec defineLastColumn = spec(3);
+ Spec fillLastColumn = spec(3, FILL);
{
TextView c = new TextView(context);
c.setTextSize(32);
c.setText("Email setup");
- p.addView(c, new LayoutParams(row1, col1a));
+ p.addView(c, new LayoutParams(titleRow, centerInAllColumns));
}
{
TextView c = new TextView(context);
c.setTextSize(16);
- c.setText("You can configure email in just a few steps:");
- p.addView(c, new LayoutParams(row2, col1b));
+ c.setText("You can configure email in a few simple steps:");
+ p.addView(c, new LayoutParams(introRow, leftAlignInAllColumns));
}
{
TextView c = new TextView(context);
c.setText("Email address:");
- p.addView(c, new LayoutParams(row3, col1c));
+ p.addView(c, new LayoutParams(emailRow, labelColumn));
}
{
EditText c = new EditText(context);
c.setEms(10);
c.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
- p.addView(c, new LayoutParams(row3, col2));
+ p.addView(c, new LayoutParams(emailRow, fieldColumn));
}
{
TextView c = new TextView(context);
c.setText("Password:");
- p.addView(c, new LayoutParams(row4, col1c));
+ p.addView(c, new LayoutParams(passwordRow, labelColumn));
}
{
TextView c = new EditText(context);
c.setEms(8);
c.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD);
- p.addView(c, new LayoutParams(row4, col2));
- }
- {
- Space c = new Space(context);
- p.addView(c, new LayoutParams(row5, col3));
+ p.addView(c, new LayoutParams(passwordRow, fieldColumn));
}
{
Button c = new Button(context);
c.setText("Manual setup");
- p.addView(c, new LayoutParams(row6, col4a));
+ p.addView(c, new LayoutParams(button1Row, defineLastColumn));
}
{
Button c = new Button(context);
c.setText("Next");
- p.addView(c, new LayoutParams(row7, col4b));
+ p.addView(c, new LayoutParams(button2Row, fillLastColumn));
}
return p;
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/OverscanActivity.java b/samples/ApiDemos/src/com/example/android/apis/view/SystemUIModes.java
index 309799270..8197e05da 100644
--- a/samples/ApiDemos/src/com/example/android/apis/view/OverscanActivity.java
+++ b/samples/ApiDemos/src/com/example/android/apis/view/SystemUIModes.java
@@ -27,12 +27,10 @@ import android.os.Bundle;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.ActionMode;
-import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.view.View.OnClickListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.CheckBox;
@@ -51,10 +49,10 @@ import com.example.android.apis.R;
* the system decor, in order to better focus the user's attention or use available screen real
* estate on the task at hand.
*/
-public class OverscanActivity extends Activity
+public class SystemUIModes extends Activity
implements OnQueryTextListener, ActionBar.TabListener {
public static class IV extends ImageView implements View.OnSystemUiVisibilityChangeListener {
- private OverscanActivity mActivity;
+ private SystemUIModes mActivity;
private ActionMode mActionMode;
public IV(Context context) {
super(context);
@@ -62,7 +60,7 @@ public class OverscanActivity extends Activity
public IV(Context context, AttributeSet attrs) {
super(context, attrs);
}
- public void setActivity(OverscanActivity act) {
+ public void setActivity(SystemUIModes act) {
setOnSystemUiVisibilityChangeListener(this);
mActivity = act;
}
@@ -126,6 +124,42 @@ public class OverscanActivity extends Activity
win.setAttributes(winParams);
}
+ private void setOverscan(boolean on) {
+ Window win = getWindow();
+ WindowManager.LayoutParams winParams = win.getAttributes();
+ final int bits = WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN;
+ if (on) {
+ winParams.flags |= bits;
+ } else {
+ winParams.flags &= ~bits;
+ }
+ win.setAttributes(winParams);
+ }
+
+ private void setTranslucentStatus(boolean on) {
+ Window win = getWindow();
+ WindowManager.LayoutParams winParams = win.getAttributes();
+ final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
+ if (on) {
+ winParams.flags |= bits;
+ } else {
+ winParams.flags &= ~bits;
+ }
+ win.setAttributes(winParams);
+ }
+
+ private void setTranslucentNavigation(boolean on) {
+ Window win = getWindow();
+ WindowManager.LayoutParams winParams = win.getAttributes();
+ final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
+ if (on) {
+ winParams.flags |= bits;
+ } else {
+ winParams.flags &= ~bits;
+ }
+ win.setAttributes(winParams);
+ }
+
private String getDisplaySize() {
DisplayMetrics dm = getResources().getDisplayMetrics();
return String.format("DisplayMetrics = (%d x %d)", dm.widthPixels, dm.heightPixels);
@@ -141,24 +175,23 @@ public class OverscanActivity extends Activity
static int TOAST_LENGTH = 500;
IV mImage;
- CheckBox[] mCheckControls = new CheckBox[6];
+ CheckBox[] mCheckControls = new CheckBox[8];
int[] mCheckFlags = new int[] { View.SYSTEM_UI_FLAG_LOW_PROFILE,
View.SYSTEM_UI_FLAG_FULLSCREEN, View.SYSTEM_UI_FLAG_HIDE_NAVIGATION,
+ View.SYSTEM_UI_FLAG_IMMERSIVE, View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY,
View.SYSTEM_UI_FLAG_LAYOUT_STABLE, View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
};
TextView mMetricsText;
- public OverscanActivity() {
+ public SystemUIModes() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
-
- setContentView(R.layout.overscan);
+ setContentView(R.layout.system_ui_modes);
mImage = (IV) findViewById(R.id.image);
mImage.setActivity(this);
@@ -171,9 +204,11 @@ public class OverscanActivity extends Activity
mCheckControls[0] = (CheckBox) findViewById(R.id.modeLowProfile);
mCheckControls[1] = (CheckBox) findViewById(R.id.modeFullscreen);
mCheckControls[2] = (CheckBox) findViewById(R.id.modeHideNavigation);
- mCheckControls[3] = (CheckBox) findViewById(R.id.layoutStable);
- mCheckControls[4] = (CheckBox) findViewById(R.id.layoutFullscreen);
- mCheckControls[5] = (CheckBox) findViewById(R.id.layoutHideNavigation);
+ mCheckControls[3] = (CheckBox) findViewById(R.id.modeImmersive);
+ mCheckControls[4] = (CheckBox) findViewById(R.id.modeImmersiveSticky);
+ mCheckControls[5] = (CheckBox) findViewById(R.id.layoutStable);
+ mCheckControls[6] = (CheckBox) findViewById(R.id.layoutFullscreen);
+ mCheckControls[7] = (CheckBox) findViewById(R.id.layoutHideNavigation);
for (int i=0; i<mCheckControls.length; i++) {
mCheckControls[i].setOnCheckedChangeListener(checkChangeListener);
}
@@ -185,6 +220,30 @@ public class OverscanActivity extends Activity
}
}
);
+ ((CheckBox) findViewById(R.id.windowOverscan)).setOnCheckedChangeListener(
+ new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ setOverscan(isChecked);
+ }
+ }
+ );
+ ((CheckBox) findViewById(R.id.windowTranslucentStatus)).setOnCheckedChangeListener(
+ new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ setTranslucentStatus(isChecked);
+ }
+ }
+ );
+ ((CheckBox) findViewById(R.id.windowTranslucentNav)).setOnCheckedChangeListener(
+ new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ setTranslucentNavigation(isChecked);
+ }
+ }
+ );
((CheckBox) findViewById(R.id.windowHideActionBar)).setOnCheckedChangeListener(
new CompoundButton.OnCheckedChangeListener() {
@Override
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/SystemUIModesOverlay.java b/samples/ApiDemos/src/com/example/android/apis/view/SystemUIModesOverlay.java
new file mode 100644
index 000000000..73eadb1dd
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/view/SystemUIModesOverlay.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.view;
+
+import android.os.Bundle;
+import android.view.Window;
+
+/**
+ * Version of demo that uses the action bar in overlay mode.
+ */
+public class SystemUIModesOverlay extends SystemUIModes {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
+ super.onCreate(savedInstanceState);
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/TextClockDemo.java b/samples/ApiDemos/src/com/example/android/apis/view/TextClockDemo.java
new file mode 100644
index 000000000..f2b85cedc
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/view/TextClockDemo.java
@@ -0,0 +1,38 @@
+/*
+ * 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.apis.view;
+
+// Need the following import to get access to the app resources, since this
+// class is in a sub-package.
+import android.text.Html;
+import android.widget.TextClock;
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Variants of {@link TextClock}.
+ */
+public class TextClockDemo extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.textclock);
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/TranslucentBarsActivity.java b/samples/ApiDemos/src/com/example/android/apis/view/TranslucentBarsActivity.java
new file mode 100644
index 000000000..98c11d1a6
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/view/TranslucentBarsActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.view;
+
+import android.app.Activity;
+import android.os.Bundle;
+import com.example.android.apis.R;
+
+/**
+ * This activity demonstrates a simple implementation of displaying
+ * content with translucent system UI bars.
+ */
+public class TranslucentBarsActivity extends Activity {
+ public TranslucentBarsActivity() {
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.translucent_bars);
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/VideoPlayerActivity.java b/samples/ApiDemos/src/com/example/android/apis/view/VideoPlayerActivity.java
index e5333d705..3bee59460 100644
--- a/samples/ApiDemos/src/com/example/android/apis/view/VideoPlayerActivity.java
+++ b/samples/ApiDemos/src/com/example/android/apis/view/VideoPlayerActivity.java
@@ -167,7 +167,7 @@ public class VideoPlayerActivity extends Activity
h.removeCallbacks(mNavHider);
if (!mMenusOpen && !mPaused) {
// If the menus are open or play is paused, we will not auto-hide.
- h.postDelayed(mNavHider, 1500);
+ h.postDelayed(mNavHider, 3000);
}
}
}
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/WindowFocusObserver.java b/samples/ApiDemos/src/com/example/android/apis/view/WindowFocusObserver.java
new file mode 100644
index 000000000..83c47288a
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/view/WindowFocusObserver.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.view;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.WindowId;
+import android.view.WindowId.FocusObserver;
+import android.widget.SearchView;
+import android.widget.ShareActionProvider;
+import android.widget.TextView;
+import android.widget.Toast;
+import com.example.android.apis.R;
+
+public class WindowFocusObserver extends Activity implements SearchView.OnQueryTextListener {
+ TextView mState;
+
+ final FocusObserver mObserver = new WindowId.FocusObserver() {
+
+ @Override
+ public void onFocusGained(WindowId token) {
+ mState.setText("Gained focus");
+ }
+
+ @Override
+ public void onFocusLost(WindowId token) {
+ mState.setText("Lost focus");
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.window_focus_observer);
+ mState = (TextView)findViewById(R.id.focus_state);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.content_actions, menu);
+ SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
+ searchView.setOnQueryTextListener(this);
+
+ // Set file with share history to the provider and set the share intent.
+ MenuItem actionItem = menu.findItem(R.id.menu_item_share_action_provider_action_bar);
+ ShareActionProvider actionProvider = (ShareActionProvider) actionItem.getActionProvider();
+ actionProvider.setShareHistoryFileName(ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME);
+ // Note that you can set/change the intent any time,
+ // say when the user has selected an image.
+ Intent shareIntent = new Intent(Intent.ACTION_SEND);
+ shareIntent.setType("image/*");
+ Uri uri = Uri.fromFile(getFileStreamPath("shared.png"));
+ shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
+ actionProvider.setShareIntent(shareIntent);
+ return true;
+ }
+
+ public void onSort(MenuItem item) {
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ Toast.makeText(this, "Searching for: " + query + "...", Toast.LENGTH_SHORT).show();
+ return true;
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ WindowId token = getWindow().getDecorView().getWindowId();
+ token.registerFocusObserver(mObserver);
+ mState.setText(token.isFocused() ? "Focused" : "Not focused");
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ getWindow().getDecorView().getWindowId().unregisterFocusObserver(mObserver);
+ }
+}