development/samples/CrossCompatibility samples/${PLATFORM_NAME}/CrossCompatibility
development/samples/CubeLiveWallpaper samples/${PLATFORM_NAME}/CubeLiveWallpaper
development/samples/Home samples/${PLATFORM_NAME}/Home
+development/samples/Honeycomb-Gallery samples/${PLATFORM_NAME}/Honeycomb-Gallery
development/samples/JetBoy samples/${PLATFORM_NAME}/JetBoy
development/samples/LunarLander samples/${PLATFORM_NAME}/LunarLander
development/samples/MultiResolution samples/${PLATFORM_NAME}/MultiResolution
--- /dev/null
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := Honeycomb-Gallery
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.hcgallery" android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-sdk android:minSdkVersion="Honeycomb" />
+
+ <uses-permission android:name="android.permission.CAMERA" />
+
+ <application android:label="@string/app_name"
+ android:icon="@drawable/icon"
+ android:logo="@drawable/logo"
+ android:theme="@android:style/Theme.Holo.Light"
+ android:hardwareAccelerated="true"
+ android:debuggable="true">
+
+ <activity android:name=".MainActivity" android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <!-- CameraSample -->
+ <activity android:name=".CameraSample"
+ android:label="@string/camera_sample"
+ android:screenOrientation="landscape" />
+
+ <!-- StackView widget sample code -->
+ <receiver android:name=".widget.WidgetProvider"
+ android:label="@string/app_widget_name">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider"
+ android:resource="@xml/widget_info" />
+ </receiver>
+ <service android:name=".widget.WidgetService"
+ android:exported="true"
+ android:permission="android.permission.BIND_REMOTEVIEWS" />
+
+ </application>
+</manifest>
--- /dev/null
+<p>This is a demo application highlighting how to use some of the new APIs in
+Honeycomb, including Fragments, the Action Bar, drag'n drop, transition
+animations, and a stack widget. The image gallery shows how all these pieces
+can work together in one application.</p>
+
+<p>The application includes the following classes:<p>
+<ul>
+ <li><a href="src/com/example/android/hcgallery/ContentFragment.html">ContentFragment</a>
+ A fragment responsible for containing the "content" of the application.
+ Displays images, receives drag/drop events from other fragments.</li>
+ <li><a href="src/com/example/android/hcgallery/TitlesFragment.html">TitlesFragment</a>
+ Shows a ListView of photos to display in the ContentFragment. Photos can
+ be chosen either by tapping on the listview, or dragging them from the
+ list to the content area. The list of photos displayed depends on the
+ category selected in the ActionBar.</li>
+ <li><a href="src/com/example/android/hcgallery/MainActivity.html">MainActivity</a>
+ This is the main entry point of the application. MainActivity is
+ responsible for initialization of the ActionBar, TitlesFragment, and
+ ContentFragment. MainActivity is also responsible for keeping track of
+ the currently selected theme and currently selected photo when the
+ activity is recreated, such as when the screen is rotated or an intent to
+ a seperate activity is fired (such as the included Camera sample).
+ MainActivity also contains code demonstrating how to animate
+ showing/hiding fragments (in this case, the TitlesFragment) and the
+ ActionBar, demonstrating how to smoothly transition between states
+ in your application.</li>
+ <li><a href="src/com/example/android/hcgallery/widget/WidgetProvider.html">WidgetProvider</a>
+ The WidgetProvider class is an implementation of the AppWidgetProvider
+ class, which is a view that can be embedded in other applications.
+ This class is responsible for handling when the instance of this widget
+ is enabled, disabled, created, deleted, or updated.</li>
+ <li><a href="src/com/example/android/hcgallery/widget/WidgetService.html">WidgetService</a>
+ The WidgetService is an implementation of the RemoteViewsService class,
+ which is used to populate the collection view (in this case, a stack
+ widget)</li> </ul>
+
+<img alt="Screenshot" src="../images/hcgallery.png" />
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <gradient android:startColor="#ff666666" android:endColor="#ff333333" android:angle="270" />
+</shape>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/frags">
+
+ <fragment class="com.example.android.hcgallery.TitlesFragment"
+ android:id="@+id/frag_title"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/titles_size"/>
+
+ <fragment class="com.example.android.hcgallery.ContentFragment"
+ android:id="@+id/frag_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+</LinearLayout>
--- /dev/null
+<!--
+ Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">Hello Action Bar</TextView>
+</LinearLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <fragment android:name="com.example.android.hcgallery.CameraFragment"
+ android:id="@+id/camera_frag"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</FrameLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="12dp">
+ <ImageView android:id="@+id/image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="fitCenter"
+ android:layout_gravity="center" />
+</FrameLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/frags">
+
+ <fragment class="com.example.android.hcgallery.TitlesFragment"
+ android:id="@+id/frag_title"
+ android:visibility="gone"
+ android:layout_width="@dimen/titles_size"
+ android:layout_height="match_parent" />
+
+ <fragment class="com.example.android.hcgallery.ContentFragment"
+ android:id="@+id/frag_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+</LinearLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="10dp"
+ android:gravity="bottom"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:background="?android:attr/activatedBackgroundIndicator" >
+</TextView>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/widget_item"
+ android:layout_width="120dp"
+ android:layout_height="120dp"
+ android:gravity="center"
+ android:background="@drawable/widget_item_background"
+ android:textColor="#ffffff"
+ android:textStyle="bold"
+ android:textSize="44sp" />
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <StackView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/stack_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:loopViews="true" />
+ <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/empty_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:background="@drawable/widget_item_background"
+ android:textColor="#ffffff"
+ android:textStyle="bold"
+ android:text="@string/widget_empty_view_text"
+ android:textSize="20sp" />
+</FrameLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/switch_cam" android:title="Switch Camera" />
+</menu>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/camera"
+ android:title="Camera"
+ android:icon="@drawable/ic_menu_camera"
+ android:showAsAction="ifRoom" />
+ <item android:id="@+id/toggleTitles"
+ android:icon="@drawable/ic_menu_toggle"
+ android:title="Toggle Titles"
+ android:showAsAction="ifRoom|withText" />
+ <item android:id="@+id/toggleTheme"
+ android:title="Day/Night"
+ android:showAsAction="ifRoom|withText" />
+</menu>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+ <dimen name="titles_size">200dp</dimen>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+ <dimen name="titles_size">300dp</dimen>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<resources>
+ <string name="app_name">Honeycomb Example</string>
+ <string name="camera_sample">Camera Example</string>
+ <string name="clip_label">Clip Label</string>
+
+ <string name="app_widget_name">Honeycomb Example Widget</string>
+ <string name="widget_empty_view_text">Touch to show data</string>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+ android:minWidth="150dp"
+ android:minHeight="150dp"
+ android:updatePeriodMillis="3600000"
+ android:initialLayout="@layout/widget_layout"
+ android:previewImage="@drawable/widget_preview"
+ android:autoAdvanceViewId="@id/stack_view" />
--- /dev/null
+/*
+ * 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.hcgallery;
+
+import java.io.IOException;
+import java.util.List;
+
+import android.app.Fragment;
+import android.app.Activity;
+import android.app.ActionBar;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.Camera;
+import android.hardware.Camera.CameraInfo;
+import android.hardware.Camera.Size;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class CameraFragment extends Fragment {
+
+ private Preview mPreview;
+ Camera mCamera;
+ int mNumberOfCameras;
+ int cameraCurrentlyLocked;
+
+ // The first rear facing camera
+ int defaultCameraId;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Add an up arrow to the "home" button, indicating that the button will go "up"
+ // one activity in the app's Activity heirarchy.
+ Activity activity = this.getActivity();
+ ActionBar actionBar = activity.getActionBar();
+ actionBar.setDisplayHomeAsUpEnabled(true);
+
+ // Create a RelativeLayout container that will hold a SurfaceView,
+ // and set it as the content of our activity.
+ mPreview = new Preview(this.getActivity());
+
+ // Find the total number of cameras available
+ mNumberOfCameras = Camera.getNumberOfCameras();
+
+ // Find the ID of the default camera
+ CameraInfo cameraInfo = new CameraInfo();
+ for (int i = 0; i < mNumberOfCameras; i++) {
+ Camera.getCameraInfo(i, cameraInfo);
+ if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
+ defaultCameraId = i;
+ }
+ }
+ setHasOptionsMenu(mNumberOfCameras > 1);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return mPreview;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ // Open the default i.e. the first rear facing camera.
+ mCamera = Camera.open(defaultCameraId);
+ cameraCurrentlyLocked = defaultCameraId;
+ mPreview.setCamera(mCamera);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ // Because the Camera object is a shared resource, it's very
+ // important to release it when the activity is paused.
+ if (mCamera != null) {
+ mPreview.setCamera(null);
+ mCamera.release();
+ mCamera = null;
+ }
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if (mNumberOfCameras > 1) {
+ // Inflate our menu which can gather user input for switching camera
+ inflater.inflate(R.menu.camera_menu, menu);
+ } else {
+ super.onCreateOptionsMenu(menu, inflater);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle item selection
+ switch (item.getItemId()) {
+ case R.id.switch_cam:
+ // Release this camera -> cameraCurrentlyLocked
+ if (mCamera != null) {
+ mCamera.stopPreview();
+ mPreview.setCamera(null);
+ mCamera.release();
+ mCamera = null;
+ }
+
+ // Acquire the next camera and request Preview to reconfigure
+ // parameters.
+ mCamera = Camera
+ .open((cameraCurrentlyLocked + 1) % mNumberOfCameras);
+ cameraCurrentlyLocked = (cameraCurrentlyLocked + 1)
+ % mNumberOfCameras;
+ mPreview.switchCamera(mCamera);
+
+ // Start the preview
+ mCamera.startPreview();
+ return true;
+ case android.R.id.home:
+ Intent intent = new Intent(this.getActivity(), MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ startActivity(intent);
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+
+/**
+ * A simple wrapper around a Camera and a SurfaceView that renders a centered
+ * preview of the Camera to the surface. We need to center the SurfaceView
+ * because not all devices have cameras that support preview sizes at the same
+ * aspect ratio as the device's display.
+ */
+class Preview extends ViewGroup implements SurfaceHolder.Callback {
+ private final String TAG = "Preview";
+
+ SurfaceView mSurfaceView;
+ SurfaceHolder mHolder;
+ Size mPreviewSize;
+ List<Size> mSupportedPreviewSizes;
+ Camera mCamera;
+
+ Preview(Context context) {
+ super(context);
+
+ mSurfaceView = new SurfaceView(context);
+ addView(mSurfaceView);
+
+ // Install a SurfaceHolder.Callback so we get notified when the
+ // underlying surface is created and destroyed.
+ mHolder = mSurfaceView.getHolder();
+ mHolder.addCallback(this);
+ mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+ }
+
+ public void setCamera(Camera camera) {
+ mCamera = camera;
+ if (mCamera != null) {
+ mSupportedPreviewSizes = mCamera.getParameters()
+ .getSupportedPreviewSizes();
+ requestLayout();
+ }
+ }
+
+ public void switchCamera(Camera camera) {
+ setCamera(camera);
+ try {
+ camera.setPreviewDisplay(mHolder);
+ } catch (IOException exception) {
+ Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
+ }
+ Camera.Parameters parameters = camera.getParameters();
+ parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
+ requestLayout();
+
+ camera.setParameters(parameters);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // We purposely disregard child measurements because act as a
+ // wrapper to a SurfaceView that centers the camera preview instead
+ // of stretching it.
+ final int width = resolveSize(getSuggestedMinimumWidth(),
+ widthMeasureSpec);
+ final int height = resolveSize(getSuggestedMinimumHeight(),
+ heightMeasureSpec);
+ setMeasuredDimension(width, height);
+
+ if (mSupportedPreviewSizes != null) {
+ mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width,
+ height);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ if (changed && getChildCount() > 0) {
+ final View child = getChildAt(0);
+
+ final int width = r - l;
+ final int height = b - t;
+
+ int previewWidth = width;
+ int previewHeight = height;
+ if (mPreviewSize != null) {
+ previewWidth = mPreviewSize.width;
+ previewHeight = mPreviewSize.height;
+ }
+
+ // Center the child SurfaceView within the parent.
+ if (width * previewHeight > height * previewWidth) {
+ final int scaledChildWidth = previewWidth * height
+ / previewHeight;
+ child.layout((width - scaledChildWidth) / 2, 0,
+ (width + scaledChildWidth) / 2, height);
+ } else {
+ final int scaledChildHeight = previewHeight * width
+ / previewWidth;
+ child.layout(0, (height - scaledChildHeight) / 2, width,
+ (height + scaledChildHeight) / 2);
+ }
+ }
+ }
+
+ public void surfaceCreated(SurfaceHolder holder) {
+ // The Surface has been created, acquire the camera and tell it where
+ // to draw.
+ try {
+ if (mCamera != null) {
+ mCamera.setPreviewDisplay(holder);
+ }
+ } catch (IOException exception) {
+ Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
+ }
+ }
+
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ // Surface will be destroyed when we return, so stop the preview.
+ if (mCamera != null) {
+ mCamera.stopPreview();
+ }
+ }
+
+ private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
+ final double ASPECT_TOLERANCE = 0.1;
+ double targetRatio = (double) w / h;
+ if (sizes == null)
+ return null;
+
+ Size optimalSize = null;
+ double minDiff = Double.MAX_VALUE;
+
+ int targetHeight = h;
+
+ // Try to find an size match aspect ratio and size
+ for (Size size : sizes) {
+ double ratio = (double) size.width / size.height;
+ if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
+ continue;
+ if (Math.abs(size.height - targetHeight) < minDiff) {
+ optimalSize = size;
+ minDiff = Math.abs(size.height - targetHeight);
+ }
+ }
+
+ // Cannot find the one match the aspect ratio, ignore the requirement
+ if (optimalSize == null) {
+ minDiff = Double.MAX_VALUE;
+ for (Size size : sizes) {
+ if (Math.abs(size.height - targetHeight) < minDiff) {
+ optimalSize = size;
+ minDiff = Math.abs(size.height - targetHeight);
+ }
+ }
+ }
+ return optimalSize;
+ }
+
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ // Now that the size is known, set up the camera parameters and begin
+ // the preview.
+ Camera.Parameters parameters = mCamera.getParameters();
+ parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
+ requestLayout();
+
+ mCamera.setParameters(parameters);
+ mCamera.startPreview();
+ }
+
+}
--- /dev/null
+/*
+ * 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.hcgallery;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class CameraSample extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ int themeId = this.getIntent().getExtras().getInt("theme");
+ this.setTheme(themeId);
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.camera_sample);
+ }
+
+}
--- /dev/null
+/*
+ * 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.hcgallery;
+
+import java.util.StringTokenizer;
+
+import android.app.ActionBar;
+import android.app.Fragment;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.ClipData.Item;
+import android.os.Bundle;
+import android.view.DragEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import android.widget.ImageView;
+
+public class ContentFragment extends Fragment {
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ final View view = inflater.inflate(R.layout.content_welcome, null);
+ final ImageView imageView = (ImageView) view.findViewById(R.id.image);
+ view.setDrawingCacheEnabled(false);
+
+ view.setOnDragListener(new View.OnDragListener() {
+ public boolean onDrag(View v, DragEvent event) {
+ switch (event.getAction()) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ return processDragStarted(event);
+ case DragEvent.ACTION_DROP:
+ return processDrop(event, imageView);
+ }
+ return false;
+ }
+ });
+
+ view.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ ActionBar bar = ContentFragment.this.getActivity()
+ .getActionBar();
+ if (bar != null) {
+ if (bar.isShowing()) {
+ bar.hide();
+ } else {
+ bar.show();
+ }
+ }
+ }
+ });
+ return view;
+ }
+
+ boolean processDragStarted(DragEvent event) {
+ // Determine whether to continue processing drag and drop based on the
+ // plain text mime type.
+ ClipDescription clipDesc = event.getClipDescription();
+ if (clipDesc != null) {
+ return clipDesc.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN);
+ }
+ return false;
+ }
+
+ boolean processDrop(DragEvent event, ImageView imageView) {
+ // Attempt to parse clip data with expected format: category||entry_id.
+ // Ignore event if data does not conform to this format.
+ ClipData data = event.getClipData();
+ if (data != null) {
+ if (data.getItemCount() > 0) {
+ Item item = data.getItemAt(0);
+ String textData = (String) item.getText();
+ if (textData != null) {
+ StringTokenizer tokenizer = new StringTokenizer(textData, "||");
+ if (tokenizer.countTokens() != 2) {
+ return false;
+ }
+ int category = -1;
+ int entryId = -1;
+ try {
+ category = Integer.parseInt(tokenizer.nextToken());
+ entryId = Integer.parseInt(tokenizer.nextToken());
+ } catch (NumberFormatException exception) {
+ return false;
+ }
+ imageView.setImageBitmap(
+ Directory.getCategory(category)
+ .getEntry(entryId)
+ .getBitmap(getResources()));
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
--- /dev/null
+/*
+ * 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.hcgallery;
+
+public class Directory {
+ private static DirectoryCategory[] categories;
+
+ public static void initializeDirectory() {
+ categories = new DirectoryCategory[] {
+ new DirectoryCategory("Balloons", new DirectoryEntry[] {
+ new DirectoryEntry("Green Balloon", R.drawable.green_balloon),
+ new DirectoryEntry("Red Balloon", R.drawable.red_balloon),
+ new DirectoryEntry("Blue Balloon", R.drawable.blue_balloon)}),
+ new DirectoryCategory("Bikes", new DirectoryEntry[] {
+ new DirectoryEntry("Old school huffy", R.drawable.blue_bike),
+ new DirectoryEntry("New Bikes", R.drawable.rainbow_bike),
+ new DirectoryEntry("Chrome Fast", R.drawable.chrome_wheel)}),
+ new DirectoryCategory("Androids", new DirectoryEntry[] {
+ new DirectoryEntry("Steampunk Android", R.drawable.punk_droid),
+ new DirectoryEntry("Stargazing Android", R.drawable.stargazer_droid),
+ new DirectoryEntry("Big Android", R.drawable.big_droid) }),
+ new DirectoryCategory("Pastries", new DirectoryEntry[] {
+ new DirectoryEntry("Cupcake", R.drawable.cupcake),
+ new DirectoryEntry("Donut", R.drawable.donut),
+ new DirectoryEntry("Eclair", R.drawable.eclair),
+ new DirectoryEntry("Froyo", R.drawable.froyo), }), };
+
+ }
+
+ public static int getCategoryCount() {
+ return categories.length;
+ }
+
+ public static DirectoryCategory getCategory(int i) {
+ return categories[i];
+ }
+}
--- /dev/null
+/*
+ * 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.hcgallery;
+
+public class DirectoryCategory {
+ private String name;
+ private DirectoryEntry[] entries;
+
+ public DirectoryCategory(String name, DirectoryEntry[] entries) {
+ this.name = name;
+ this.entries = entries;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getEntryCount() {
+ return entries.length;
+ }
+
+ public DirectoryEntry getEntry(int i) {
+ return entries[i];
+ }
+}
--- /dev/null
+/*
+ * 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.hcgallery;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
+
+public class DirectoryEntry {
+ private String name;
+ private int resID;
+
+ public DirectoryEntry(String name, int resID) {
+ this.name = name;
+ this.resID = resID;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Drawable getDrawable(Resources res) {
+ return res.getDrawable(resID);
+ }
+
+ public Bitmap getBitmap(Resources res) {
+ return BitmapFactory.decodeResource(res, resID);
+ }
+}
--- /dev/null
+/*
+ * 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.hcgallery;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class MainActivity extends Activity implements ActionBar.TabListener {
+
+ private View mActionBarView;
+ private Animator mCurrentTitlesAnimator;
+ private String[] mToggleLabels = {"Show Titles", "Hide Titles"};
+ private int mLabelIndex = 1;
+ private int mThemeId = -1;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if(savedInstanceState != null && savedInstanceState.getInt("theme", -1) != -1) {
+ mThemeId = savedInstanceState.getInt("theme");
+ this.setTheme(mThemeId);
+ }
+ setContentView(R.layout.main);
+
+ Directory.initializeDirectory();
+
+ ActionBar bar = getActionBar();
+
+ int i;
+ for (i = 0; i < Directory.getCategoryCount(); i++)
+ bar.addTab(bar.newTab().setText(Directory.getCategory(i).getName())
+ .setTabListener(this));
+
+ mActionBarView = getLayoutInflater().inflate(
+ R.layout.action_bar_custom, null);
+
+ bar.setCustomView(mActionBarView);
+ bar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_USE_LOGO);
+ bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+ bar.setDisplayShowHomeEnabled(true);
+
+ // If category is not saved to the savedInstanceState,
+ // 0 is returned by default.
+ if(savedInstanceState != null) {
+ int category = savedInstanceState.getInt("category");
+ bar.selectTab(bar.getTabAt(category));
+ }
+ }
+
+ public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
+ TitlesFragment titleFrag = (TitlesFragment) getFragmentManager()
+ .findFragmentById(R.id.frag_title);
+ titleFrag.populateTitles(tab.getPosition());
+
+ titleFrag.selectPosition(0);
+ }
+
+ public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
+ }
+
+ public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.main_menu, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.camera:
+ Intent intent = new Intent(this, CameraSample.class);
+ intent.putExtra("theme", mThemeId);
+ startActivity(intent);
+ return true;
+ case R.id.toggleTitles:
+ toggleVisibleTitles();
+ return true;
+ case R.id.toggleTheme:
+ if (mThemeId == android.R.style.Theme_Holo) {
+ mThemeId = android.R.style.Theme_Holo_Light;
+ } else {
+ mThemeId = android.R.style.Theme_Holo;
+ }
+ this.recreate();
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ public void toggleVisibleTitles() {
+ // Use these for custom animations.
+ final FragmentManager fm = getFragmentManager();
+ final TitlesFragment f = (TitlesFragment) fm
+ .findFragmentById(R.id.frag_title);
+ final View titlesView = f.getView();
+ mLabelIndex = 1 - mLabelIndex;
+
+ // Determine if we're in portrait, and whether we're showing or hiding the titles
+ // with this toggle.
+ final boolean isPortrait = getResources().getConfiguration().orientation ==
+ Configuration.ORIENTATION_PORTRAIT;
+
+ final boolean shouldShow = f.isHidden() || mCurrentTitlesAnimator != null;
+
+ // Cancel the current titles animation if there is one.
+ if (mCurrentTitlesAnimator != null)
+ mCurrentTitlesAnimator.cancel();
+
+ // Begin setting up the object animator. We'll animate the bottom or right edge of the
+ // titles view, as well as its alpha for a fade effect.
+ ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(
+ titlesView,
+ PropertyValuesHolder.ofInt(
+ isPortrait ? "bottom" : "right",
+ shouldShow ? getResources().getDimensionPixelSize(R.dimen.titles_size)
+ : 0),
+ PropertyValuesHolder.ofFloat("alpha", shouldShow ? 1 : 0)
+ );
+
+ // At each step of the animation, we'll perform layout by calling setLayoutParams.
+ final ViewGroup.LayoutParams lp = titlesView.getLayoutParams();
+ objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ // *** WARNING ***: triggering layout at each animation frame highly impacts
+ // performance so you should only do this for simple layouts. More complicated
+ // layouts can be better served with individual animations on child views to
+ // avoid the performance penalty of layout.
+ if (isPortrait) {
+ lp.height = (Integer) valueAnimator.getAnimatedValue();
+ } else {
+ lp.width = (Integer) valueAnimator.getAnimatedValue();
+ }
+ titlesView.setLayoutParams(lp);
+ }
+ });
+
+ if (shouldShow) {
+ fm.beginTransaction().show(f).commit();
+ objectAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ mCurrentTitlesAnimator = null;
+ }
+ });
+
+ } else {
+ objectAnimator.addListener(new AnimatorListenerAdapter() {
+ boolean canceled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ canceled = true;
+ super.onAnimationCancel(animation);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ if (canceled)
+ return;
+ mCurrentTitlesAnimator = null;
+ fm.beginTransaction().hide(f).commit();
+ }
+ });
+ }
+
+ // Start the animation.
+ objectAnimator.start();
+ mCurrentTitlesAnimator = objectAnimator;
+
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ menu.getItem(1).setTitle(mToggleLabels[mLabelIndex]);
+ return true;
+ }
+
+ @Override
+ public void onSaveInstanceState (Bundle outState) {
+ super.onSaveInstanceState(outState);
+ ActionBar bar = getActionBar();
+ int category = bar.getSelectedTab().getPosition();
+ outState.putInt("category", category);
+ outState.putInt("theme", mThemeId);
+ }
+}
--- /dev/null
+/*
+ * 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.hcgallery;
+
+import android.app.ListFragment;
+import android.content.ClipData;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.AdapterView.OnItemLongClickListener;
+
+public class TitlesFragment extends ListFragment {
+ private int mCategory = 0;
+ private int mCurPosition = 0;
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ //Current position should survive screen rotations.
+ if (savedInstanceState != null) {
+ mCategory = savedInstanceState.getInt("category");
+ mCurPosition = savedInstanceState.getInt("listPosition");
+ }
+
+ populateTitles(mCategory);
+ ListView lv = getListView();
+ lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+ selectPosition(mCurPosition);
+ lv.setCacheColorHint(Color.WHITE);
+
+ lv.setOnItemLongClickListener(new OnItemLongClickListener() {
+
+ @Override
+ public boolean onItemLongClick(AdapterView<?> av, View v, int pos,
+ long id) {
+ final String title = (String) ((TextView) v).getText();
+
+ // Set up clip data with the category||entry_id format.
+ final String textData = String.format("%d||%d", mCategory, pos);
+ ClipData data = ClipData.newPlainText(title, textData);
+ v.startDrag(data, new MyDragShadowBuilder(v), null, 0);
+ return true;
+ }
+ });
+ }
+
+ private static class MyDragShadowBuilder extends View.DragShadowBuilder {
+ private static Drawable shadow;
+
+ public MyDragShadowBuilder(View v) {
+ super(v);
+ shadow = new ColorDrawable(Color.BLUE);
+ shadow.setBounds(0, 0, v.getWidth(), v.getHeight());
+ }
+
+ @Override
+ public void onDrawShadow(Canvas canvas) {
+ shadow.draw(canvas);
+ }
+ }
+
+ public void populateTitles(int category) {
+ DirectoryCategory cat = Directory.getCategory(category);
+ String[] items = new String[cat.getEntryCount()];
+ for (int i = 0; i < cat.getEntryCount(); i++)
+ items[i] = cat.getEntry(i).getName();
+ setListAdapter(new ArrayAdapter<String>(getActivity(),
+ R.layout.title_list_item, items));
+ mCategory = category;
+ }
+
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ updateImage(position);
+ }
+
+ private void updateImage(int position) {
+ ImageView iv = (ImageView) getFragmentManager().findFragmentById(
+ R.id.frag_content).getView().findViewById(R.id.image);
+ iv.setImageDrawable(Directory.getCategory(mCategory).getEntry(position)
+ .getDrawable(getResources()));
+ mCurPosition = position;
+ }
+
+ public void selectPosition(int position) {
+ ListView lv = getListView();
+ lv.setItemChecked(position, true);
+ updateImage(position);
+
+ }
+
+ @Override
+ public void onSaveInstanceState (Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt("listPosition", mCurPosition);
+ outState.putInt("category", mCategory);
+ }
+}
--- /dev/null
+/*
+ * 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.hcgallery.widget;
+
+public class WidgetItem {
+ public String text;
+
+ public WidgetItem(String text) {
+ this.text = text;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.hcgallery.widget;
+
+import com.example.android.hcgallery.R;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.widget.RemoteViews;
+import android.widget.Toast;
+
+public class WidgetProvider extends AppWidgetProvider {
+ public static String TOAST_ACTION = "com.example.android.widget.action.TOAST";
+
+ @Override
+ public void onDeleted(Context context, int[] appWidgetIds) {
+ super.onDeleted(context, appWidgetIds);
+ }
+
+ @Override
+ public void onDisabled(Context context) {
+ super.onDisabled(context);
+ }
+
+ @Override
+ public void onEnabled(Context context) {
+ super.onEnabled(context);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ AppWidgetManager mgr = AppWidgetManager.getInstance(context);
+ if (intent.getAction().equals(TOAST_ACTION)) {
+ int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+ int viewIndex = intent.getIntExtra("numberToToast", 0);
+ Toast.makeText(context, "Touched view " + viewIndex, Toast.LENGTH_SHORT).show();
+ }
+ super.onReceive(context, intent);
+ }
+
+ @Override
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+ // update each of the widgets with the remote adapter
+ for (int i = 0; i < appWidgetIds.length; ++i) {
+
+ // Here we setup the intent which points to the StackViewService which will
+ // provide the views for this collection.
+ Intent intent = new Intent(context, WidgetService.class);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
+ // When intents are compared, the extras are ignored, so we need to embed the extras
+ // into the data so that the extras will not be ignored.
+ intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
+ RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
+ rv.setRemoteAdapter(appWidgetIds[i], R.id.stack_view, intent);
+
+ // The empty view is displayed when the collection has no items. It should be a sibling
+ // of the collection view.
+ rv.setEmptyView(R.id.stack_view, R.id.empty_view);
+
+ // Here we setup the a pending intent template. Individuals items of a collection
+ // cannot setup their own pending intents, instead, the collection as a whole can
+ // setup a pending intent template, and the individual items can set a fillInIntent
+ // to create unique before on an item to item basis.
+ Intent toastIntent = new Intent(context, WidgetProvider.class);
+ toastIntent.setAction(WidgetProvider.TOAST_ACTION);
+ toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
+ toastIntent.setData(Uri.parse("widgetid" + appWidgetIds[i]));
+ PendingIntent toastPendingIntent = PendingIntent.getBroadcast(context, 0, toastIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ rv.setPendingIntentTemplate(R.id.stack_view, toastPendingIntent);
+
+ appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
+ }
+ super.onUpdate(context, appWidgetManager, appWidgetIds);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.hcgallery.widget;
+
+import com.example.android.hcgallery.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.RemoteViews;
+import android.widget.RemoteViewsService;
+
+public class WidgetService extends RemoteViewsService {
+
+ private class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
+ private static final int mCount = 10;
+ private List<WidgetItem> mWidgetItems = new ArrayList<WidgetItem>();
+ private Context mContext;
+ private int mAppWidgetId;
+
+ public StackRemoteViewsFactory(Context context, Intent intent) {
+ mContext = context;
+ mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+ }
+
+ public void onCreate() {
+ // In onCreate() you setup any connections / cursors to your data source. Heavy lifting,
+ // for example downloading or creating content etc, should be deferred to getViewAt() or
+ // onDataSetChanged(). Taking more than 20 seconds in this call will result in an ANR.
+ for (int i = 0; i < mCount; i++) {
+ mWidgetItems.add(new WidgetItem(i + "!"));
+ }
+
+ // We sleep for 3 seconds here to show how the empty view appears in the interim.
+ // The empty view is set in the WidgetProvider and should be a sibling of the
+ // collection view.
+ try {
+ Thread.sleep(3000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void onDestroy() {
+ // In onDestroy() you should tear down anything that was setup for your data source,
+ // eg. cursors, connections, etc.
+ mWidgetItems.clear();
+ }
+
+ public int getCount() {
+ return mCount;
+ }
+
+ public RemoteViews getViewAt(int position) {
+ // position will always range from 0 to getCount() - 1.
+
+ // We construct a remote views item based on our widget item xml file, and set the
+ // text based on the position.
+ RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item);
+ rv.setTextViewText(R.id.widget_item, mWidgetItems.get(position).text);
+
+ // Next, we set an intent so that clicking on this view will result in a toast message
+ Bundle extras = new Bundle();
+ extras.putInt(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
+ extras.putInt("numberToToast", position);
+ Intent fillInIntent = new Intent();
+ fillInIntent.putExtras(extras);
+ rv.setOnClickFillInIntent(R.id.widget_item, fillInIntent);
+
+ // You can do heaving lifting in here, synchronously. For example, if you need to
+ // process an image, fetch something from the network, etc., it is ok to do it here,
+ // synchronously. A loading view will show up in lieu of the actual contents in the
+ // interim.
+ try {
+ Log.d("WidgetService/getViewAt", "Loading view " + position);
+ // Simulating a time-consuming operation. NO NEED to include this call in your app!
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ // Return our remote views object.
+ return rv;
+ }
+
+ public RemoteViews getLoadingView() {
+ // You can create a custom loading view (for instance when getViewAt() is slow. If you
+ // return null here, you will get the default loading view.
+ return null;
+ }
+
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public void onDataSetChanged() {
+ // This is triggered when you call AppWidgetManager notifyAppWidgetViewDataChanged
+ // on the collection view corresponding to this factory. You can do heaving lifting in
+ // here, synchronously. For example, if you need to process an image, fetch something
+ // from the network, etc., it is ok to do it here, synchronously. The widget will remain
+ // in its current state while work is being done here, so you don't need to worry about
+ // locking up the widget.
+ }
+ }
+
+ public RemoteViewsFactory onGetViewFactory(Intent intent) {
+ return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
+ }
+}