diff --git a/build/sdk.atree b/build/sdk.atree index af9376504..6459d020a 100644 --- a/build/sdk.atree +++ b/build/sdk.atree @@ -160,6 +160,7 @@ development/samples/ContactManager samples/${PLATFORM_NAME}/ContactMan 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 diff --git a/samples/Honeycomb-Gallery/Android.mk b/samples/Honeycomb-Gallery/Android.mk new file mode 100644 index 000000000..01788afb0 --- /dev/null +++ b/samples/Honeycomb-Gallery/Android.mk @@ -0,0 +1,16 @@ +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)) diff --git a/samples/Honeycomb-Gallery/AndroidManifest.xml b/samples/Honeycomb-Gallery/AndroidManifest.xml new file mode 100644 index 000000000..4776020aa --- /dev/null +++ b/samples/Honeycomb-Gallery/AndroidManifest.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/Honeycomb-Gallery/_index.html b/samples/Honeycomb-Gallery/_index.html new file mode 100644 index 000000000..51605277c --- /dev/null +++ b/samples/Honeycomb-Gallery/_index.html @@ -0,0 +1,37 @@ +

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.

+ +

The application includes the following classes:

+

+ +Screenshot diff --git a/samples/Honeycomb-Gallery/res/drawable-hdpi/android_logo.png b/samples/Honeycomb-Gallery/res/drawable-hdpi/android_logo.png new file mode 100644 index 000000000..f8be345f6 Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-hdpi/android_logo.png differ diff --git a/samples/Honeycomb-Gallery/res/drawable-hdpi/ic_menu_camera.png b/samples/Honeycomb-Gallery/res/drawable-hdpi/ic_menu_camera.png new file mode 100644 index 000000000..d03cbf9e6 Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-hdpi/ic_menu_camera.png differ diff --git a/samples/Honeycomb-Gallery/res/drawable-hdpi/ic_menu_toggle.png b/samples/Honeycomb-Gallery/res/drawable-hdpi/ic_menu_toggle.png new file mode 100644 index 000000000..a4b50d9db Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-hdpi/ic_menu_toggle.png differ diff --git a/samples/Honeycomb-Gallery/res/drawable-hdpi/icon.png b/samples/Honeycomb-Gallery/res/drawable-hdpi/icon.png new file mode 100644 index 000000000..404a2a440 Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-hdpi/icon.png differ diff --git a/samples/Honeycomb-Gallery/res/drawable-hdpi/logo.png b/samples/Honeycomb-Gallery/res/drawable-hdpi/logo.png new file mode 100644 index 000000000..5af2f7547 Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-hdpi/logo.png differ diff --git a/samples/Honeycomb-Gallery/res/drawable-ldpi/icon.png b/samples/Honeycomb-Gallery/res/drawable-ldpi/icon.png new file mode 100644 index 000000000..add216314 Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-ldpi/icon.png differ diff --git a/samples/Honeycomb-Gallery/res/drawable-mdpi/ic_menu_camera.png b/samples/Honeycomb-Gallery/res/drawable-mdpi/ic_menu_camera.png new file mode 100644 index 000000000..a8f2f4165 Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-mdpi/ic_menu_camera.png differ diff --git a/samples/Honeycomb-Gallery/res/drawable-mdpi/ic_menu_toggle.png b/samples/Honeycomb-Gallery/res/drawable-mdpi/ic_menu_toggle.png new file mode 100644 index 000000000..9a77dc757 Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-mdpi/ic_menu_toggle.png differ diff --git a/samples/Honeycomb-Gallery/res/drawable-mdpi/icon.png b/samples/Honeycomb-Gallery/res/drawable-mdpi/icon.png new file mode 100644 index 000000000..0e62f31cf Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-mdpi/icon.png differ diff --git a/samples/Honeycomb-Gallery/res/drawable-mdpi/logo.png b/samples/Honeycomb-Gallery/res/drawable-mdpi/logo.png new file mode 100644 index 000000000..43642eba0 Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-mdpi/logo.png differ diff --git a/samples/Honeycomb-Gallery/res/drawable-nodpi/big_droid.JPG b/samples/Honeycomb-Gallery/res/drawable-nodpi/big_droid.JPG new file mode 100644 index 000000000..2bb450c22 Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-nodpi/big_droid.JPG differ diff --git a/samples/Honeycomb-Gallery/res/drawable-nodpi/blue_balloon.JPG b/samples/Honeycomb-Gallery/res/drawable-nodpi/blue_balloon.JPG new file mode 100644 index 000000000..f11657462 Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-nodpi/blue_balloon.JPG differ diff --git a/samples/Honeycomb-Gallery/res/drawable-nodpi/blue_bike.JPG b/samples/Honeycomb-Gallery/res/drawable-nodpi/blue_bike.JPG new file mode 100644 index 000000000..f424815dc Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-nodpi/blue_bike.JPG differ diff --git a/samples/Honeycomb-Gallery/res/drawable-nodpi/chrome_wheel.JPG b/samples/Honeycomb-Gallery/res/drawable-nodpi/chrome_wheel.JPG new file mode 100644 index 000000000..a748593f2 Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-nodpi/chrome_wheel.JPG differ diff --git a/samples/Honeycomb-Gallery/res/drawable-nodpi/cupcake.JPG b/samples/Honeycomb-Gallery/res/drawable-nodpi/cupcake.JPG new file mode 100644 index 000000000..ba902c692 Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-nodpi/cupcake.JPG differ diff --git a/samples/Honeycomb-Gallery/res/drawable-nodpi/donut.JPG b/samples/Honeycomb-Gallery/res/drawable-nodpi/donut.JPG new file mode 100644 index 000000000..5f5ee46c5 Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-nodpi/donut.JPG differ diff --git a/samples/Honeycomb-Gallery/res/drawable-nodpi/eclair.JPG b/samples/Honeycomb-Gallery/res/drawable-nodpi/eclair.JPG new file mode 100644 index 000000000..656e6cb8f Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-nodpi/eclair.JPG differ diff --git a/samples/Honeycomb-Gallery/res/drawable-nodpi/froyo.JPG b/samples/Honeycomb-Gallery/res/drawable-nodpi/froyo.JPG new file mode 100644 index 000000000..f6c5f069f Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-nodpi/froyo.JPG differ diff --git a/samples/Honeycomb-Gallery/res/drawable-nodpi/green_balloon.JPG b/samples/Honeycomb-Gallery/res/drawable-nodpi/green_balloon.JPG new file mode 100644 index 000000000..171e3a3c0 Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-nodpi/green_balloon.JPG differ diff --git a/samples/Honeycomb-Gallery/res/drawable-nodpi/punk_droid.JPG b/samples/Honeycomb-Gallery/res/drawable-nodpi/punk_droid.JPG new file mode 100644 index 000000000..090779ba9 Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-nodpi/punk_droid.JPG differ diff --git a/samples/Honeycomb-Gallery/res/drawable-nodpi/rainbow_bike.JPG b/samples/Honeycomb-Gallery/res/drawable-nodpi/rainbow_bike.JPG new file mode 100644 index 000000000..87be337ed Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-nodpi/rainbow_bike.JPG differ diff --git a/samples/Honeycomb-Gallery/res/drawable-nodpi/red_balloon.JPG b/samples/Honeycomb-Gallery/res/drawable-nodpi/red_balloon.JPG new file mode 100644 index 000000000..3e5e95c18 Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-nodpi/red_balloon.JPG differ diff --git a/samples/Honeycomb-Gallery/res/drawable-nodpi/stargazer_droid.JPG b/samples/Honeycomb-Gallery/res/drawable-nodpi/stargazer_droid.JPG new file mode 100644 index 000000000..e336b324d Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-nodpi/stargazer_droid.JPG differ diff --git a/samples/Honeycomb-Gallery/res/drawable-nodpi/widget_preview.png b/samples/Honeycomb-Gallery/res/drawable-nodpi/widget_preview.png new file mode 100644 index 000000000..b2a134278 Binary files /dev/null and b/samples/Honeycomb-Gallery/res/drawable-nodpi/widget_preview.png differ diff --git a/samples/Honeycomb-Gallery/res/drawable/widget_item_background.xml b/samples/Honeycomb-Gallery/res/drawable/widget_item_background.xml new file mode 100644 index 000000000..aad4da35e --- /dev/null +++ b/samples/Honeycomb-Gallery/res/drawable/widget_item_background.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/samples/Honeycomb-Gallery/res/layout-port/main.xml b/samples/Honeycomb-Gallery/res/layout-port/main.xml new file mode 100644 index 000000000..ea63a67e9 --- /dev/null +++ b/samples/Honeycomb-Gallery/res/layout-port/main.xml @@ -0,0 +1,34 @@ + + + + + + + + + diff --git a/samples/Honeycomb-Gallery/res/layout/action_bar_custom.xml b/samples/Honeycomb-Gallery/res/layout/action_bar_custom.xml new file mode 100644 index 000000000..5f111d9e8 --- /dev/null +++ b/samples/Honeycomb-Gallery/res/layout/action_bar_custom.xml @@ -0,0 +1,24 @@ + + + + Hello Action Bar + diff --git a/samples/Honeycomb-Gallery/res/layout/camera_sample.xml b/samples/Honeycomb-Gallery/res/layout/camera_sample.xml new file mode 100644 index 000000000..dcb88679c --- /dev/null +++ b/samples/Honeycomb-Gallery/res/layout/camera_sample.xml @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/samples/Honeycomb-Gallery/res/layout/content_welcome.xml b/samples/Honeycomb-Gallery/res/layout/content_welcome.xml new file mode 100644 index 000000000..0b92508c2 --- /dev/null +++ b/samples/Honeycomb-Gallery/res/layout/content_welcome.xml @@ -0,0 +1,27 @@ + + + + + + diff --git a/samples/Honeycomb-Gallery/res/layout/main.xml b/samples/Honeycomb-Gallery/res/layout/main.xml new file mode 100644 index 000000000..f96382d1d --- /dev/null +++ b/samples/Honeycomb-Gallery/res/layout/main.xml @@ -0,0 +1,34 @@ + + + + + + + + + diff --git a/samples/Honeycomb-Gallery/res/layout/title_list_item.xml b/samples/Honeycomb-Gallery/res/layout/title_list_item.xml new file mode 100644 index 000000000..cead7090c --- /dev/null +++ b/samples/Honeycomb-Gallery/res/layout/title_list_item.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/samples/Honeycomb-Gallery/res/layout/widget_item.xml b/samples/Honeycomb-Gallery/res/layout/widget_item.xml new file mode 100644 index 000000000..a1cbe1f83 --- /dev/null +++ b/samples/Honeycomb-Gallery/res/layout/widget_item.xml @@ -0,0 +1,25 @@ + + + diff --git a/samples/Honeycomb-Gallery/res/layout/widget_layout.xml b/samples/Honeycomb-Gallery/res/layout/widget_layout.xml new file mode 100644 index 000000000..987465086 --- /dev/null +++ b/samples/Honeycomb-Gallery/res/layout/widget_layout.xml @@ -0,0 +1,36 @@ + + + + + + diff --git a/samples/Honeycomb-Gallery/res/menu/camera_menu.xml b/samples/Honeycomb-Gallery/res/menu/camera_menu.xml new file mode 100644 index 000000000..55184dd8a --- /dev/null +++ b/samples/Honeycomb-Gallery/res/menu/camera_menu.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/samples/Honeycomb-Gallery/res/menu/main_menu.xml b/samples/Honeycomb-Gallery/res/menu/main_menu.xml new file mode 100644 index 000000000..2a4267911 --- /dev/null +++ b/samples/Honeycomb-Gallery/res/menu/main_menu.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/samples/Honeycomb-Gallery/res/values-port/dimens.xml b/samples/Honeycomb-Gallery/res/values-port/dimens.xml new file mode 100644 index 000000000..fc41ac967 --- /dev/null +++ b/samples/Honeycomb-Gallery/res/values-port/dimens.xml @@ -0,0 +1,19 @@ + + + + 200dp + diff --git a/samples/Honeycomb-Gallery/res/values/dimens.xml b/samples/Honeycomb-Gallery/res/values/dimens.xml new file mode 100644 index 000000000..1f49654e3 --- /dev/null +++ b/samples/Honeycomb-Gallery/res/values/dimens.xml @@ -0,0 +1,19 @@ + + + + 300dp + diff --git a/samples/Honeycomb-Gallery/res/values/strings.xml b/samples/Honeycomb-Gallery/res/values/strings.xml new file mode 100644 index 000000000..a6c3b5720 --- /dev/null +++ b/samples/Honeycomb-Gallery/res/values/strings.xml @@ -0,0 +1,24 @@ + + + + Honeycomb Example + Camera Example + Clip Label + + Honeycomb Example Widget + Touch to show data + diff --git a/samples/Honeycomb-Gallery/res/xml/widget_info.xml b/samples/Honeycomb-Gallery/res/xml/widget_info.xml new file mode 100644 index 000000000..db95f9c31 --- /dev/null +++ b/samples/Honeycomb-Gallery/res/xml/widget_info.xml @@ -0,0 +1,23 @@ + + + diff --git a/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/CameraFragment.java b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/CameraFragment.java new file mode 100644 index 000000000..6eef985b3 --- /dev/null +++ b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/CameraFragment.java @@ -0,0 +1,318 @@ +/* + * 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 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 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(); + } + +} diff --git a/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/CameraSample.java b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/CameraSample.java new file mode 100644 index 000000000..22438585d --- /dev/null +++ b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/CameraSample.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.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); + } + +} diff --git a/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/ContentFragment.java b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/ContentFragment.java new file mode 100644 index 000000000..c4b34bfcd --- /dev/null +++ b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/ContentFragment.java @@ -0,0 +1,119 @@ +/* + * 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; + } +} diff --git a/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/Directory.java b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/Directory.java new file mode 100644 index 000000000..570c78858 --- /dev/null +++ b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/Directory.java @@ -0,0 +1,51 @@ +/* + * 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]; + } +} diff --git a/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/DirectoryCategory.java b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/DirectoryCategory.java new file mode 100644 index 000000000..926f6e5cf --- /dev/null +++ b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/DirectoryCategory.java @@ -0,0 +1,39 @@ +/* + * 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]; + } +} diff --git a/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/DirectoryEntry.java b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/DirectoryEntry.java new file mode 100644 index 000000000..ecfd4c385 --- /dev/null +++ b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/DirectoryEntry.java @@ -0,0 +1,44 @@ +/* + * 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); + } +} diff --git a/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/MainActivity.java b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/MainActivity.java new file mode 100644 index 000000000..ec8ea49a4 --- /dev/null +++ b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/MainActivity.java @@ -0,0 +1,220 @@ +/* + * 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); + } +} diff --git a/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/TitlesFragment.java b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/TitlesFragment.java new file mode 100644 index 000000000..226768119 --- /dev/null +++ b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/TitlesFragment.java @@ -0,0 +1,122 @@ +/* + * 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(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); + } +} diff --git a/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/widget/WidgetItem.java b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/widget/WidgetItem.java new file mode 100644 index 000000000..4c4b230d1 --- /dev/null +++ b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/widget/WidgetItem.java @@ -0,0 +1,25 @@ +/* + * 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; + } +} diff --git a/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/widget/WidgetProvider.java b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/widget/WidgetProvider.java new file mode 100644 index 000000000..07a984d14 --- /dev/null +++ b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/widget/WidgetProvider.java @@ -0,0 +1,96 @@ +/* + * 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); + } +} diff --git a/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/widget/WidgetService.java b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/widget/WidgetService.java new file mode 100644 index 000000000..e7aaa6b37 --- /dev/null +++ b/samples/Honeycomb-Gallery/src/com/example/android/hcgallery/widget/WidgetService.java @@ -0,0 +1,136 @@ +/* + * 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 mWidgetItems = new ArrayList(); + 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); + } +}