OSDN Git Service

Sample code to illustrate how to use the accelerometer
authorMathias Agopian <mathias@google.com>
Sat, 3 Jul 2010 01:12:04 +0000 (18:12 -0700)
committerMathias Agopian <mathias@google.com>
Wed, 14 Jul 2010 05:51:23 +0000 (22:51 -0700)
this is a small particle + physics system that shows how
to integrate accleration data into positions accurately.

Change-Id: I3bbdc433f52d98464269ac8cb996c4cf037ae8d6

12 files changed:
samples/AccelerometerPlay/Android.mk [new file with mode: 0644]
samples/AccelerometerPlay/AndroidManifest.xml [new file with mode: 0644]
samples/AccelerometerPlay/MODULE_LICENSE_APACHE2 [new file with mode: 0644]
samples/AccelerometerPlay/NOTICE [new file with mode: 0644]
samples/AccelerometerPlay/res/drawable-hdpi/ball.png [new file with mode: 0644]
samples/AccelerometerPlay/res/drawable-hdpi/icon.png [new file with mode: 0644]
samples/AccelerometerPlay/res/drawable-hdpi/wood.jpg [new file with mode: 0644]
samples/AccelerometerPlay/res/drawable-ldpi/icon.png [new file with mode: 0644]
samples/AccelerometerPlay/res/drawable-mdpi/icon.png [new file with mode: 0644]
samples/AccelerometerPlay/res/layout/main.xml [new file with mode: 0644]
samples/AccelerometerPlay/res/values/strings.xml [new file with mode: 0644]
samples/AccelerometerPlay/src/com/example/android/accelerometerplay/AccelerometerPlayActivity.java [new file with mode: 0644]

diff --git a/samples/AccelerometerPlay/Android.mk b/samples/AccelerometerPlay/Android.mk
new file mode 100644 (file)
index 0000000..4767271
--- /dev/null
@@ -0,0 +1,31 @@
+# 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.
+#
+
+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 := AccelerometerPlay
+
+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/AccelerometerPlay/AndroidManifest.xml b/samples/AccelerometerPlay/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..5def875
--- /dev/null
@@ -0,0 +1,38 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      android:versionCode="1"
+      android:versionName="1.0" package="com.example.android.accelerometerplay">
+    <application android:icon="@drawable/icon" android:label="@string/app_name">
+        <activity android:name=".AccelerometerPlayActivity"
+                  android:label="@string/app_name"
+                  android:screenOrientation="portrait" android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+    </application>
+
+
+<uses-sdk android:minSdkVersion="5"></uses-sdk>
+<uses-permission android:name="android.permission.VIBRATE"></uses-permission>
+
+<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>
+
+</manifest> 
\ No newline at end of file
diff --git a/samples/AccelerometerPlay/MODULE_LICENSE_APACHE2 b/samples/AccelerometerPlay/MODULE_LICENSE_APACHE2
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/samples/AccelerometerPlay/NOTICE b/samples/AccelerometerPlay/NOTICE
new file mode 100644 (file)
index 0000000..c5b1efa
--- /dev/null
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/samples/AccelerometerPlay/res/drawable-hdpi/ball.png b/samples/AccelerometerPlay/res/drawable-hdpi/ball.png
new file mode 100644 (file)
index 0000000..e79e4d6
Binary files /dev/null and b/samples/AccelerometerPlay/res/drawable-hdpi/ball.png differ
diff --git a/samples/AccelerometerPlay/res/drawable-hdpi/icon.png b/samples/AccelerometerPlay/res/drawable-hdpi/icon.png
new file mode 100644 (file)
index 0000000..8074c4c
Binary files /dev/null and b/samples/AccelerometerPlay/res/drawable-hdpi/icon.png differ
diff --git a/samples/AccelerometerPlay/res/drawable-hdpi/wood.jpg b/samples/AccelerometerPlay/res/drawable-hdpi/wood.jpg
new file mode 100644 (file)
index 0000000..883f491
Binary files /dev/null and b/samples/AccelerometerPlay/res/drawable-hdpi/wood.jpg differ
diff --git a/samples/AccelerometerPlay/res/drawable-ldpi/icon.png b/samples/AccelerometerPlay/res/drawable-ldpi/icon.png
new file mode 100644 (file)
index 0000000..1095584
Binary files /dev/null and b/samples/AccelerometerPlay/res/drawable-ldpi/icon.png differ
diff --git a/samples/AccelerometerPlay/res/drawable-mdpi/icon.png b/samples/AccelerometerPlay/res/drawable-mdpi/icon.png
new file mode 100644 (file)
index 0000000..a07c69f
Binary files /dev/null and b/samples/AccelerometerPlay/res/drawable-mdpi/icon.png differ
diff --git a/samples/AccelerometerPlay/res/layout/main.xml b/samples/AccelerometerPlay/res/layout/main.xml
new file mode 100644 (file)
index 0000000..8166ca6
--- /dev/null
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+</LinearLayout>
diff --git a/samples/AccelerometerPlay/res/values/strings.xml b/samples/AccelerometerPlay/res/values/strings.xml
new file mode 100644 (file)
index 0000000..6e3785e
--- /dev/null
@@ -0,0 +1,19 @@
+<?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">AccelerometerPlay</string>
+</resources>
diff --git a/samples/AccelerometerPlay/src/com/example/android/accelerometerplay/AccelerometerPlayActivity.java b/samples/AccelerometerPlay/src/com/example/android/accelerometerplay/AccelerometerPlayActivity.java
new file mode 100644 (file)
index 0000000..ed0605c
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+ * 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.
+ */
+
+package com.example.android.accelerometerplay;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.BitmapFactory.Options;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.util.DisplayMetrics;
+import android.view.View;
+
+/**
+ * This is an example of using the accelerometer to integrate the device's
+ * acceleration to a position using the Verlet method. This is illustrated with
+ * a very simple particle system comprised of a few iron balls freely moving on
+ * an inclined wooden table. The inclination of the virtual table is controlled
+ * by the device's accelerometer.
+ * 
+ * @see SensorManager
+ * @see SensorEvent
+ * @see Sensor
+ */
+
+public class AccelerometerPlayActivity extends Activity {
+
+       private SimulationView mSimulationView;
+       private SensorManager mSensorManager;
+       private PowerManager mPowerManager;
+       private WakeLock mWakeLock;
+
+       /** Called when the activity is first created. */
+       @Override
+       public void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+
+               // Get an instance of the SensorManager
+               mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
+
+               // Get an instance of the PowerManager
+               mPowerManager = (PowerManager) getSystemService(POWER_SERVICE);
+
+               // Create a bright wake lock
+               mWakeLock = mPowerManager.newWakeLock(
+                               PowerManager.SCREEN_BRIGHT_WAKE_LOCK, getClass().getName());
+
+               // instantiate our simulation view and set it as the activity's content
+               mSimulationView = new SimulationView(this);
+               setContentView(mSimulationView);
+       }
+
+       @Override
+       protected void onResume() {
+               super.onResume();
+               /*
+                * when the activity is resumed, we acquire a wake-lock so that the
+                * screen stays on, since the user will likely not be fiddling with the
+                * screen or buttons.
+                */
+               mWakeLock.acquire();
+
+               // Start the simulation
+               mSimulationView.startSimulation();
+       }
+
+       @Override
+       protected void onPause() {
+               super.onPause();
+               /*
+                * When the activity is paused, we make sure to stop the simulation,
+                * release our sensor resources and wake locks
+                */
+
+               // Stop the simulation
+               mSimulationView.stopSimulation();
+
+               // and release our wake-lock
+               mWakeLock.release();
+       }
+
+       class SimulationView extends View implements SensorEventListener {
+               // diameter of the balls in meters
+               private static final float sBallDiameter = 0.004f;
+               private static final float sBallDiameter2 = sBallDiameter
+                               * sBallDiameter;
+
+               // friction of the virtual table and air
+               private static final float sFriction = 0.1f;
+
+               private Sensor mAccelerometer;
+               private long mLastT;
+               private float mLastDeltaT;
+
+               private float mXDpi;
+               private float mYDpi;
+               private float mMetersToPixelsX;
+               private float mMetersToPixelsY;
+               private Bitmap mBitmap;
+               private Bitmap mWood;
+               private float mXOrigin;
+               private float mYOrigin;
+               private float mSensorX;
+               private float mSensorY;
+               private long mSensorTimeStamp;
+               private long mCpuTimeStamp;
+               private float mHorizontalBound;
+               private float mVerticalBound;
+               private final ParticleSystem mParticleSystem = new ParticleSystem();
+
+               /*
+                * Each of our particle holds its previous and current position, its
+                * acceleration. for added realism each particle has its own friction
+                * coefficient.
+                */
+               class Particle {
+                       private float mPosX;
+                       private float mPosY;
+                       private float mAccelX;
+                       private float mAccelY;
+                       private float mLastPosX;
+                       private float mLastPosY;
+                       private float mOneMinusFriction;
+
+                       Particle() {
+                               // make each particle a bit different by randomizing its
+                               // coefficient of friction
+                               final float r = ((float) Math.random() - 0.5f) * 0.2f;
+                               mOneMinusFriction = 1.0f - sFriction + r;
+                       }
+
+                       public void computePhysics(float sx, float sy, float dT, float dTC) {
+                               // Force of gravity applied to our virtual object
+                               final float m = 1000.0f; // mass of our virtual object
+                               final float gx = -sx * m;
+                               final float gy = -sy * m;
+
+                               /*
+                                * ·F = mA <=> A = ·F / m
+                                * 
+                                * We could simplify the code by completely eliminating "m" (the
+                                * mass) from all the equations, but it would hide the concepts
+                                * from this sample code.
+                                */
+                               final float invm = 1.0f / m;
+                               final float ax = gx * invm;
+                               final float ay = gy * invm;
+
+                               /*
+                                * Time-corrected Verlet integration
+                                * 
+                                * The position Verlet integrator is defined as
+                                * 
+                                * x(t+Æt) = x(t) + x(t) - x(t-Æt) + a(t)Ætö2
+                                * 
+                                * However, the above equation doesn't handle variable Æt very
+                                * well, a time-corrected version is needed:
+                                * 
+                                * x(t+Æt) = x(t) + (x(t) - x(t-Æt)) * (Æt/Æt_prev) + a(t)Ætö2
+                                * 
+                                * 
+                                * We also add a simple friction term (f) to the equation:
+                                * 
+                                * x(t+Æt) = x(t) + (1-f) * (x(t) - x(t-Æt)) * (Æt/Æt_prev) +
+                                * a(t)Ætö2
+                                */
+                               final float dTdT = dT * dT;
+                               final float x = mPosX + mOneMinusFriction * dTC
+                                               * (mPosX - mLastPosX) + mAccelX * dTdT;
+                               final float y = mPosY + mOneMinusFriction * dTC
+                                               * (mPosY - mLastPosY) + mAccelY * dTdT;
+                               mLastPosX = mPosX;
+                               mLastPosY = mPosY;
+                               mPosX = x;
+                               mPosY = y;
+                               mAccelX = ax;
+                               mAccelY = ay;
+                       }
+
+                       /*
+                        * Resolving constraints and collisions with the Verlet integrator
+                        * can be very simple, we simply need to move a colliding or
+                        * constrained particle in such way that the constraint is
+                        * satisfied.
+                        */
+                       public void resolveCollisionWithBounds() {
+                               final float xmax = mHorizontalBound;
+                               final float ymax = mVerticalBound;
+                               final float x = mPosX;
+                               final float y = mPosY;
+                               if (x > xmax) {
+                                       mPosX = xmax;
+                               } else if (x < -xmax) {
+                                       mPosX = -xmax;
+                               }
+                               if (y > ymax) {
+                                       mPosY = ymax;
+                               } else if (y < -ymax) {
+                                       mPosY = -ymax;
+                               }
+                       }
+               }
+
+               /*
+                * A particle system is just a collection of particles
+                */
+               class ParticleSystem {
+                       static final int NUM_PARTICLES = 15;
+                       private Particle mBalls[] = new Particle[NUM_PARTICLES];
+
+                       ParticleSystem() {
+                               /*
+                                * Initially our particles have no speed or acceleration
+                                */
+                               for (int i = 0; i < mBalls.length; i++) {
+                                       mBalls[i] = new Particle();
+                               }
+                       }
+
+                       /*
+                        * Update the position of each particle in the system using the
+                        * Verlet integrator.
+                        */
+                       private void updatePositions(float sx, float sy, long timestamp) {
+                               final long t = timestamp;
+                               if (mLastT != 0) {
+                                       final float dT = (float) (t - mLastT)
+                                                       * (1.0f / 1000000000.0f);
+                                       if (mLastDeltaT != 0) {
+                                               final float dTC = dT / mLastDeltaT;
+                                               final int count = mBalls.length;
+                                               for (int i = 0; i < count; i++) {
+                                                       Particle ball = mBalls[i];
+                                                       ball.computePhysics(sx, sy, dT, dTC);
+                                               }
+                                       }
+                                       mLastDeltaT = dT;
+                               }
+                               mLastT = t;
+                       }
+
+                       /*
+                        * Performs one iteration of the simulation. First updating the
+                        * position of all the particles and resolving the constraints and
+                        * collisions.
+                        */
+                       public void update(float sx, float sy, long now) {
+                               // update the system's positions
+                               updatePositions(sx, sy, now);
+
+                               // We do no more than a limited number of iterations
+                               final int NUM_MAX_ITERATIONS = 10;
+
+                               /*
+                                * Resolve collisions, each particle is tested against every
+                                * other particle for collision. If a collision is detected the
+                                * particle is moved away using a virtual spring of infinite
+                                * stiffness.
+                                */
+                               boolean more = true;
+                               final int count = mBalls.length;
+                               for (int k = 0; k < NUM_MAX_ITERATIONS && more; k++) {
+                                       more = false;
+                                       for (int i = 0; i < count; i++) {
+                                               Particle curr = mBalls[i];
+                                               for (int j = i + 1; j < count; j++) {
+                                                       Particle ball = mBalls[j];
+                                                       float dx = ball.mPosX - curr.mPosX;
+                                                       float dy = ball.mPosY - curr.mPosY;
+                                                       float dd = dx * dx + dy * dy;
+                                                       // Check for collisions
+                                                       if (dd <= sBallDiameter2) {
+                                                               /*
+                                                                * add a little bit of entropy, after nothing is
+                                                                * perfect in the universe.
+                                                                */
+                                                               dx += ((float) Math.random() - 0.5f) * 0.0001f;
+                                                               dy += ((float) Math.random() - 0.5f) * 0.0001f;
+                                                               dd = dx * dx + dy * dy;
+                                                               // simulate the spring
+                                                               final float d = (float) Math.sqrt(dd);
+                                                               final float c = (0.5f * (sBallDiameter - d))
+                                                                               / d;
+                                                               curr.mPosX -= dx * c;
+                                                               curr.mPosY -= dy * c;
+                                                               ball.mPosX += dx * c;
+                                                               ball.mPosY += dy * c;
+                                                               more = true;
+                                                       }
+                                               }
+                                               /*
+                                                * Finally make sure the particle doesn't intersects
+                                                * with the walls.
+                                                */
+                                               curr.resolveCollisionWithBounds();
+                                       }
+                               }
+                       }
+
+                       public int getParticleCount() {
+                               return mBalls.length;
+                       }
+
+                       public float getPosX(int i) {
+                               return mBalls[i].mPosX;
+                       }
+
+                       public float getPosY(int i) {
+                               return mBalls[i].mPosY;
+                       }
+               }
+
+               public void startSimulation() {
+                       /*
+                        * It is not necessary to get accelerometer events at a very high
+                        * rate, by using a slower rate (SENSOR_DELAY_UI), we get an
+                        * automatic low-pass filter, which "extracts" the gravity component
+                        * of the acceleration. As an added benefit, we use less power and
+                        * CPU resources.
+                        */
+                       mSensorManager.registerListener(this, mAccelerometer,
+                                       SensorManager.SENSOR_DELAY_UI);
+               }
+
+               public void stopSimulation() {
+                       mSensorManager.unregisterListener(this);
+               }
+
+               public SimulationView(Context context) {
+                       super(context);
+                       mAccelerometer = mSensorManager
+                                       .getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+
+                       DisplayMetrics metrics = new DisplayMetrics();
+                       getWindowManager().getDefaultDisplay().getMetrics(metrics);
+                       mXDpi = metrics.xdpi;
+                       mYDpi = metrics.ydpi;
+                       mMetersToPixelsX = mXDpi / 0.0254f;
+                       mMetersToPixelsY = mYDpi / 0.0254f;
+
+                       // rescale the ball so it's about 0.5 cm on screen
+                       Bitmap ball = BitmapFactory.decodeResource(getResources(),
+                                       R.drawable.ball);
+                       final int dstWidth = (int) (sBallDiameter * mMetersToPixelsX + 0.5f);
+                       final int dstHeight = (int) (sBallDiameter * mMetersToPixelsY + 0.5f);
+                       mBitmap = Bitmap
+                                       .createScaledBitmap(ball, dstWidth, dstHeight, true);
+
+                       Options opts = new Options();
+                       opts.inDither = true;
+                       opts.inPreferredConfig = Bitmap.Config.RGB_565;
+                       mWood = BitmapFactory.decodeResource(getResources(),
+                                       R.drawable.wood, opts);
+               }
+
+               @Override
+               protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+                       // compute the origin of the screen relative to the origin of
+                       // the bitmap
+                       mXOrigin = (w - mBitmap.getWidth()) * 0.5f;
+                       mYOrigin = (h - mBitmap.getHeight()) * 0.5f;
+                       mHorizontalBound = ((w / mMetersToPixelsX - sBallDiameter) * 0.5f);
+                       mVerticalBound = ((h / mMetersToPixelsY - sBallDiameter) * 0.5f);
+               }
+
+               @Override
+               public void onSensorChanged(SensorEvent event) {
+                       if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
+                               return;
+                       /*
+                        * record the accelerometer data, the event's timestamp as well as
+                        * the current time. The latter is needed so we can calculate the
+                        * "present" time during rendering.
+                        */
+                       mSensorX = event.values[0];
+                       mSensorY = event.values[1];
+                       mSensorTimeStamp = event.timestamp;
+                       mCpuTimeStamp = System.nanoTime();
+               }
+
+               @Override
+               protected void onDraw(Canvas canvas) {
+
+                       /*
+                        * draw the background
+                        */
+
+                       canvas.drawBitmap(mWood, 0, 0, null);
+
+                       /*
+                        * compute the new position of our object, based on accelerometer
+                        * data and present time.
+                        */
+
+                       final ParticleSystem particleSystem = mParticleSystem;
+                       final long now = mSensorTimeStamp
+                                       + (System.nanoTime() - mCpuTimeStamp);
+                       final float sx = mSensorX;
+                       final float sy = mSensorY;
+
+                       particleSystem.update(sx, sy, now);
+
+                       final float xc = mXOrigin;
+                       final float yc = mYOrigin;
+                       final float xs = mMetersToPixelsX;
+                       final float ys = mMetersToPixelsY;
+                       final Bitmap bitmap = mBitmap;
+                       final int count = particleSystem.getParticleCount();
+                       for (int i = 0; i < count; i++) {
+                               /*
+                                * We transform the canvas so that the coordinate system matches
+                                * the sensors coordinate system with the origin in the center
+                                * of the screen and the unit is the meter.
+                                */
+
+                               final float x = xc + particleSystem.getPosX(i) * xs;
+                               final float y = yc - particleSystem.getPosY(i) * ys;
+                               canvas.drawBitmap(bitmap, x, y, null);
+                       }
+
+                       // and make sure to redraw asap
+                       invalidate();
+               }
+
+               @Override
+               public void onAccuracyChanged(Sensor sensor, int accuracy) {
+               }
+       }
+}