development
リビジョン | 42d3a4ae1431fb3f02ac212000e8cdfb6f21e226 (tree) |
---|---|
日時 | 2010-09-14 09:00:10 |
作者 | Megha Joshi <mjoshi@goog...> |
コミッター | Megha Joshi |
Adding CrossCompatibility sample app.
Change-Id: I565584ea53212f61a9fbbb44e50da59f4ba06fff
@@ -109,6 +109,7 @@ development/samples/CubeLiveWallpaper samples/${PLATFORM_NAME}/CubeLiveWa | ||
109 | 109 | development/samples/VoiceRecognitionService samples/${PLATFORM_NAME}/VoiceRecognitionService |
110 | 110 | development/samples/TicTacToeLib samples/${PLATFORM_NAME}/TicTacToeLib |
111 | 111 | development/samples/TicTacToeMain samples/${PLATFORM_NAME}/TicTacToeMain |
112 | +development/samples/CrossCompatibility samples/${PLATFORM_NAME}/CrossCompatibility | |
112 | 113 | |
113 | 114 | # dx |
114 | 115 | bin/dx platforms/${PLATFORM_NAME}/tools/dx |
@@ -0,0 +1,17 @@ | ||
1 | +<?xml version="1.0" encoding="utf-8"?> | |
2 | +<manifest xmlns:android="http://schemas.android.com/apk/res/android" | |
3 | + package="com.example.android.touchexample" | |
4 | + android:versionCode="1" | |
5 | + android:versionName="1.0"> | |
6 | + <application android:icon="@drawable/icon" android:label="@string/app_name"> | |
7 | + <activity android:name=".TouchExampleActivity" | |
8 | + android:label="@string/app_name"> | |
9 | + <intent-filter> | |
10 | + <action android:name="android.intent.action.MAIN" /> | |
11 | + <category android:name="android.intent.category.LAUNCHER" /> | |
12 | + </intent-filter> | |
13 | + </activity> | |
14 | + | |
15 | + </application> | |
16 | + <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="8" /> | |
17 | +</manifest> |
@@ -0,0 +1,20 @@ | ||
1 | +<p>This sample demonstrates how to design an application that is compatible across different Android versions. Applications | |
2 | +should degrade gracefully on older platform versions, dropping features or providing | |
3 | +when the platform support needed by features or functionality isn't available.</p> | |
4 | + | |
5 | +<p>In this case, the CrossCompatibility application shows how to use APIs that are not available in all Android versions and | |
6 | +still create a single <code>.apk</code> that runs on all Android versions.</p> | |
7 | + | |
8 | +<ul> | |
9 | +<li>The main application's <a | |
10 | +href="AndroidManifest.html">AndroidManifest.xml</a> file declares that it is backwards compatible with API level 3 devices with attribute <code>minSdkVersion</code> in the <code>uses-sdk</code> tag. | |
11 | + </li> | |
12 | +<li> | |
13 | +<a | |
14 | +href="src/com/example/android/touchexample/VersionedGestureDetector.html">VersionedGestureDetector.java</a> | |
15 | +is a version independent abstract class which factors out multitouch APIs that differ between platform versions. </li> | |
16 | + | |
17 | +</ul> | |
18 | + | |
19 | +<p> For more information on how to make your applications cross-compatible, please check out the original | |
20 | +blogpost <a href="http://android-developers.blogspot.com/2010/07/how-to-have-your-cupcake-and-eat-it-too.html">here</a>.</p> |
@@ -0,0 +1,67 @@ | ||
1 | +<?xml version="1.0" encoding="UTF-8"?> | |
2 | +<project name="TouchExampleActivity" default="help"> | |
3 | + | |
4 | + <!-- The local.properties file is created and updated by the 'android' tool. | |
5 | + It contains the path to the SDK. It should *NOT* be checked in in Version | |
6 | + Control Systems. --> | |
7 | + <property file="local.properties" /> | |
8 | + | |
9 | + <!-- The build.properties file can be created by you and is never touched | |
10 | + by the 'android' tool. This is the place to change some of the default property values | |
11 | + used by the Ant rules. | |
12 | + Here are some properties you may want to change/update: | |
13 | + | |
14 | + application.package | |
15 | + the name of your application package as defined in the manifest. Used by the | |
16 | + 'uninstall' rule. | |
17 | + source.dir | |
18 | + the name of the source directory. Default is 'src'. | |
19 | + out.dir | |
20 | + the name of the output directory. Default is 'bin'. | |
21 | + | |
22 | + Properties related to the SDK location or the project target should be updated | |
23 | + using the 'android' tool with the 'update' action. | |
24 | + | |
25 | + This file is an integral part of the build system for your application and | |
26 | + should be checked in in Version Control Systems. | |
27 | + | |
28 | + --> | |
29 | + <property file="build.properties" /> | |
30 | + | |
31 | + <!-- The default.properties file is created and updated by the 'android' tool, as well | |
32 | + as ADT. | |
33 | + This file is an integral part of the build system for your application and | |
34 | + should be checked in in Version Control Systems. --> | |
35 | + <property file="default.properties" /> | |
36 | + | |
37 | + <!-- Custom Android task to deal with the project target, and import the proper rules. | |
38 | + This requires ant 1.6.0 or above. --> | |
39 | + <path id="android.antlibs"> | |
40 | + <pathelement path="${sdk.dir}/tools/lib/anttasks.jar" /> | |
41 | + <pathelement path="${sdk.dir}/tools/lib/sdklib.jar" /> | |
42 | + <pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" /> | |
43 | + <pathelement path="${sdk.dir}/tools/lib/apkbuilder.jar" /> | |
44 | + <pathelement path="${sdk.dir}/tools/lib/jarutils.jar" /> | |
45 | + </path> | |
46 | + | |
47 | + <taskdef name="setup" | |
48 | + classname="com.android.ant.SetupTask" | |
49 | + classpathref="android.antlibs" /> | |
50 | + | |
51 | + <!-- Execute the Android Setup task that will setup some properties specific to the target, | |
52 | + and import the build rules files. | |
53 | + | |
54 | + The rules file is imported from | |
55 | + <SDK>/platforms/<target_platform>/templates/android_rules.xml | |
56 | + | |
57 | + To customize some build steps for your project: | |
58 | + - copy the content of the main node <project> from android_rules.xml | |
59 | + - paste it in this build.xml below the <setup /> task. | |
60 | + - disable the import by changing the setup task below to <setup import="false" /> | |
61 | + | |
62 | + This will ensure that the properties are setup correctly but that your customized | |
63 | + build steps are used. | |
64 | + --> | |
65 | + <setup /> | |
66 | + | |
67 | +</project> |
@@ -0,0 +1,4 @@ | ||
1 | +<?xml version="1.0" encoding="utf-8"?> | |
2 | +<resources> | |
3 | + <string name="app_name">TouchExample</string> | |
4 | +</resources> |
@@ -0,0 +1,34 @@ | ||
1 | +/* | |
2 | + * Copyright (C) 2010 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package com.example.android.touchexample; | |
17 | + | |
18 | +import android.app.Activity; | |
19 | +import android.os.Bundle; | |
20 | +import android.view.ViewGroup; | |
21 | + | |
22 | +public class TouchExampleActivity extends Activity { | |
23 | + /** Called when the activity is first created. */ | |
24 | + @Override | |
25 | + public void onCreate(Bundle savedInstanceState) { | |
26 | + super.onCreate(savedInstanceState); | |
27 | + | |
28 | + TouchExampleView view = new TouchExampleView(this); | |
29 | + view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, | |
30 | + ViewGroup.LayoutParams.MATCH_PARENT)); | |
31 | + | |
32 | + setContentView(view); | |
33 | + } | |
34 | +} |
@@ -0,0 +1,82 @@ | ||
1 | +/* | |
2 | + * Copyright (C) 2010 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package com.example.android.touchexample; | |
17 | + | |
18 | +import android.content.Context; | |
19 | +import android.graphics.Canvas; | |
20 | +import android.graphics.drawable.Drawable; | |
21 | +import android.util.AttributeSet; | |
22 | +import android.view.MotionEvent; | |
23 | +import android.view.View; | |
24 | + | |
25 | +public class TouchExampleView extends View { | |
26 | + private Drawable mIcon; | |
27 | + private float mPosX; | |
28 | + private float mPosY; | |
29 | + | |
30 | + private VersionedGestureDetector mDetector; | |
31 | + private float mScaleFactor = 1.f; | |
32 | + | |
33 | + public TouchExampleView(Context context) { | |
34 | + this(context, null, 0); | |
35 | + } | |
36 | + | |
37 | + public TouchExampleView(Context context, AttributeSet attrs) { | |
38 | + this(context, attrs, 0); | |
39 | + } | |
40 | + | |
41 | + public TouchExampleView(Context context, AttributeSet attrs, int defStyle) { | |
42 | + super(context, attrs, defStyle); | |
43 | + mIcon = context.getResources().getDrawable(R.drawable.icon); | |
44 | + mIcon.setBounds(0, 0, mIcon.getIntrinsicWidth(), mIcon.getIntrinsicHeight()); | |
45 | + | |
46 | + mDetector = VersionedGestureDetector.newInstance(context, new GestureCallback()); | |
47 | + } | |
48 | + | |
49 | + @Override | |
50 | + public boolean onTouchEvent(MotionEvent ev) { | |
51 | + mDetector.onTouchEvent(ev); | |
52 | + return true; | |
53 | + } | |
54 | + | |
55 | + @Override | |
56 | + public void onDraw(Canvas canvas) { | |
57 | + super.onDraw(canvas); | |
58 | + | |
59 | + canvas.save(); | |
60 | + canvas.translate(mPosX, mPosY); | |
61 | + canvas.scale(mScaleFactor, mScaleFactor); | |
62 | + mIcon.draw(canvas); | |
63 | + canvas.restore(); | |
64 | + } | |
65 | + | |
66 | + private class GestureCallback implements VersionedGestureDetector.OnGestureListener { | |
67 | + public void onDrag(float dx, float dy) { | |
68 | + mPosX += dx; | |
69 | + mPosY += dy; | |
70 | + invalidate(); | |
71 | + } | |
72 | + | |
73 | + public void onScale(float scaleFactor) { | |
74 | + mScaleFactor *= scaleFactor; | |
75 | + | |
76 | + // Don't let the object get too small or too large. | |
77 | + mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f)); | |
78 | + | |
79 | + invalidate(); | |
80 | + } | |
81 | + } | |
82 | +} |
@@ -0,0 +1,165 @@ | ||
1 | +/* | |
2 | + * Copyright (C) 2010 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package com.example.android.touchexample; | |
17 | + | |
18 | +import android.content.Context; | |
19 | +import android.os.Build; | |
20 | +import android.util.Log; | |
21 | +import android.view.MotionEvent; | |
22 | +import android.view.ScaleGestureDetector; | |
23 | + | |
24 | +public abstract class VersionedGestureDetector { | |
25 | + private static final String TAG = "VersionedGestureDetector"; | |
26 | + | |
27 | + OnGestureListener mListener; | |
28 | + | |
29 | + public static VersionedGestureDetector newInstance(Context context, | |
30 | + OnGestureListener listener) { | |
31 | + final int sdkVersion = Integer.parseInt(Build.VERSION.SDK); | |
32 | + VersionedGestureDetector detector = null; | |
33 | + if (sdkVersion < Build.VERSION_CODES.ECLAIR) { | |
34 | + detector = new CupcakeDetector(); | |
35 | + } else if (sdkVersion < Build.VERSION_CODES.FROYO) { | |
36 | + detector = new EclairDetector(); | |
37 | + } else { | |
38 | + detector = new FroyoDetector(context); | |
39 | + } | |
40 | + | |
41 | + Log.d(TAG, "Created new " + detector.getClass()); | |
42 | + detector.mListener = listener; | |
43 | + | |
44 | + return detector; | |
45 | + } | |
46 | + | |
47 | + public abstract boolean onTouchEvent(MotionEvent ev); | |
48 | + | |
49 | + public interface OnGestureListener { | |
50 | + public void onDrag(float dx, float dy); | |
51 | + public void onScale(float scaleFactor); | |
52 | + } | |
53 | + | |
54 | + private static class CupcakeDetector extends VersionedGestureDetector { | |
55 | + float mLastTouchX; | |
56 | + float mLastTouchY; | |
57 | + | |
58 | + float getActiveX(MotionEvent ev) { | |
59 | + return ev.getX(); | |
60 | + } | |
61 | + | |
62 | + float getActiveY(MotionEvent ev) { | |
63 | + return ev.getY(); | |
64 | + } | |
65 | + | |
66 | + boolean shouldDrag() { | |
67 | + return true; | |
68 | + } | |
69 | + | |
70 | + @Override | |
71 | + public boolean onTouchEvent(MotionEvent ev) { | |
72 | + switch (ev.getAction()) { | |
73 | + case MotionEvent.ACTION_DOWN: { | |
74 | + mLastTouchX = getActiveX(ev); | |
75 | + mLastTouchY = getActiveY(ev); | |
76 | + break; | |
77 | + } | |
78 | + case MotionEvent.ACTION_MOVE: { | |
79 | + final float x = getActiveX(ev); | |
80 | + final float y = getActiveY(ev); | |
81 | + | |
82 | + if (shouldDrag()) { | |
83 | + mListener.onDrag(x - mLastTouchX, y - mLastTouchY); | |
84 | + } | |
85 | + | |
86 | + mLastTouchX = x; | |
87 | + mLastTouchY = y; | |
88 | + break; | |
89 | + } | |
90 | + } | |
91 | + return true; | |
92 | + } | |
93 | + } | |
94 | + | |
95 | + private static class EclairDetector extends CupcakeDetector { | |
96 | + private static final int INVALID_POINTER_ID = -1; | |
97 | + private int mActivePointerId = INVALID_POINTER_ID; | |
98 | + private int mActivePointerIndex = 0; | |
99 | + | |
100 | + @Override | |
101 | + float getActiveX(MotionEvent ev) { | |
102 | + return ev.getX(mActivePointerIndex); | |
103 | + } | |
104 | + | |
105 | + @Override | |
106 | + float getActiveY(MotionEvent ev) { | |
107 | + return ev.getY(mActivePointerIndex); | |
108 | + } | |
109 | + | |
110 | + @Override | |
111 | + public boolean onTouchEvent(MotionEvent ev) { | |
112 | + final int action = ev.getAction(); | |
113 | + switch (action & MotionEvent.ACTION_MASK) { | |
114 | + case MotionEvent.ACTION_DOWN: | |
115 | + mActivePointerId = ev.getPointerId(0); | |
116 | + break; | |
117 | + case MotionEvent.ACTION_CANCEL: | |
118 | + case MotionEvent.ACTION_UP: | |
119 | + mActivePointerId = INVALID_POINTER_ID; | |
120 | + break; | |
121 | + case MotionEvent.ACTION_POINTER_UP: | |
122 | + final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) | |
123 | + >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; | |
124 | + final int pointerId = ev.getPointerId(pointerIndex); | |
125 | + if (pointerId == mActivePointerId) { | |
126 | + // This was our active pointer going up. Choose a new | |
127 | + // active pointer and adjust accordingly. | |
128 | + final int newPointerIndex = pointerIndex == 0 ? 1 : 0; | |
129 | + mActivePointerId = ev.getPointerId(newPointerIndex); | |
130 | + mLastTouchX = ev.getX(newPointerIndex); | |
131 | + mLastTouchY = ev.getY(newPointerIndex); | |
132 | + } | |
133 | + break; | |
134 | + } | |
135 | + | |
136 | + mActivePointerIndex = ev.findPointerIndex(mActivePointerId); | |
137 | + return super.onTouchEvent(ev); | |
138 | + } | |
139 | + } | |
140 | + | |
141 | + private static class FroyoDetector extends EclairDetector { | |
142 | + private ScaleGestureDetector mDetector; | |
143 | + | |
144 | + public FroyoDetector(Context context) { | |
145 | + mDetector = new ScaleGestureDetector(context, | |
146 | + new ScaleGestureDetector.SimpleOnScaleGestureListener() { | |
147 | + @Override public boolean onScale(ScaleGestureDetector detector) { | |
148 | + mListener.onScale(detector.getScaleFactor()); | |
149 | + return true; | |
150 | + } | |
151 | + }); | |
152 | + } | |
153 | + | |
154 | + @Override | |
155 | + boolean shouldDrag() { | |
156 | + return !mDetector.isInProgress(); | |
157 | + } | |
158 | + | |
159 | + @Override | |
160 | + public boolean onTouchEvent(MotionEvent ev) { | |
161 | + mDetector.onTouchEvent(ev); | |
162 | + return super.onTouchEvent(ev); | |
163 | + } | |
164 | + } | |
165 | +} |