デュアルタッチを有効にするには?
私はAndroidアプリケーションには詳しくない。
とりあえず、手を入れられそうな箇所を2つ調べてみた。
- まず、先のページタッチパネルにある、/dev/input からc言語ネイティブプログラムで直接読み取る。2点目はDOWN、UPの通知なく、いきなりサイズが通知されるようだ。
- Dev Tools にある Pointer Location で デュアルタッチした場合、getSize() で2点間の距離が得られる事が分かった。こちらも同様?
前者を修正するには kernel を変更するか、ラップするかすれば良いのだろう。ただし、最終的にFramework(API)に伝わるまでどのような道筋があるのか追いかけたくない。Androidアプリケーションが直接 API としてアクセスする Framework を考える。こちらは、アプリ側から直接利用されるJavaクラスだ。アプリに取って都合の良いイベントに加工しやすいかもしれない。
テストに使った Pointer Location がタッチ場所の座業取得に利用しているのが MotionEvent クラスだった。全体的に Android はこのクラスでイベントを受けるらしい…。このクラス内で、getSize() でサイズが得られるようなものが取得できた際に、2点に拡張してあげればよい。そうすれば、デュアルタッチになるのでは無かろうか?…と言う事で、修正を加えてみた。物理層~kernel~API直下まで一気に無視した。
結局、単純な話で size 通知が来たら、2点間の距離と中点が分かってるので手っ取り早くX座標に対し展開、Y座標は2点のうち最新の座標を両点のY座標としている。2点間の距離は float で 0~1 として得られるらしいので、X軸の解像度を乗じて2で割った数減加して X1、X2 とした。(π/4に傾けて実装するのも良いかもしれないが…。)
final float MAX_SIZE = 960;
と言う箇所が最大値。
あとは、1点目の後、最初に2点押された場合に
ACTION_POINTER_2_DOWN
1点に戻った場合に
ACTION_POINTER_2_UP
を通知している。
ただし、2点押されている状態から同時に離された場合は考慮していない。
タイミングによって、そのような状態になるのか Pointer Location が異常終了した事があった。
raw logを取って対策を講じれば良いのだろうが面倒なので今回は行っていない。
また、pointerIndex⇔pointerId の相互ひも付けも行っていない。
たとえば、1DOWN , 2DOWN , 1UP とした場合でも、1DOWN , 2DOWN , 2UP になると思う。
1点目と2点目が瞬間入れ替わりするような現象も出るが、、、、お絵かきとかで無ければ問題ないと思う。
以上が概要で、以下がその実装。
無線LANの時と同様 LOG 出力でぐちゃぐちゃになっているが、コメントアウトして diff を取っているので、そこに修正を入れる必要はない。ファイル最下にある、readFromParcel メソッドに少々加工を施し、クラス変数を定義した。
より良いコードがありましたら、コメントに記入頂ければ幸いです。
今回、注目したソースコード。
./frameworks/base/core/java/android/view/MotionEvent.java ←これに修正を入れている。
./apps/Development/src/com/android/development/PointerLocation.java
./frameworks/base/core/java/com/android/internal/widget/PointerLocationView.java
以下、修正差分。
機能的に、必要部分は
@@ -217,6 +217,9 @@
部分と
@@ -1289,6 +1347,84 @@
以降のみ。
$ diff -ubB MotionEvent.java.orig MotionEvent.java--- MotionEvent.java.orig 2011-01-03 14:22:55.620059406 +0900
+++ MotionEvent.java 2011-01-05 22:48:33.150516732 +0900
@@ -27,7 +27,7 @@
* it is being used for.
*/
public final class MotionEvent implements Parcelable {
- static final boolean DEBUG_POINTERS = false;
+ static final boolean DEBUG_POINTERS = true;
/**
* Bit mask of the parts of the action code that are the action itself.
@@ -217,6 +217,9 @@
static private Object gRecyclerLock = new Object();
static private int gRecyclerUsed = 0;
static private MotionEvent gRecyclerTop = null;
+ static private int lastAction = ACTION_MASK;
+ static private boolean isDual = false;
+
private long mDownTime;
private long mEventTimeNano;
@@ -643,6 +646,7 @@
* and pointer index.
*/
public final int getAction() {
+ //Log.v("MotionEvent","getAction");
return mAction;
}
@@ -655,6 +659,7 @@
* pointer actions.
*/
public final int getActionMasked() {
+ //Log.v("MotionEvent","getActionMasked");
return mAction & ACTION_MASK;
}
@@ -667,6 +672,7 @@
* gone down or up.
*/
public final int getActionIndex() {
+ //Log.v("MotionEvent","getActionIndex");
return (mAction & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT;
}
@@ -675,6 +681,7 @@
* a stream of position events.
*/
public final long getDownTime() {
+ //Log.v("MotionEvent","getDownTime");
return mDownTime;
}
@@ -682,6 +689,7 @@
* Returns the time (in ms) when this specific event was generated.
*/
public final long getEventTime() {
+ //Log.v("MotionEvent","getEventTime");
return mTimeSamples[0];
}
@@ -692,6 +700,7 @@
* @hide
*/
public final long getEventTimeNano() {
+ //Log.v("MotionEvent","getEventTimeNano");
return mEventTimeNano;
}
@@ -700,6 +709,7 @@
* arbitrary pointer identifier).
*/
public final float getX() {
+ //Log.v("MotionEvent","grtX");
return mDataSamples[SAMPLE_X];
}
@@ -708,6 +718,7 @@
* arbitrary pointer identifier).
*/
public final float getY() {
+ //Log.v("MotionEvent","getY");
return mDataSamples[SAMPLE_Y];
}
@@ -716,6 +727,7 @@
* arbitrary pointer identifier).
*/
public final float getPressure() {
+ //Log.v("MotionEvent","getPressure");
return mDataSamples[SAMPLE_PRESSURE];
}
@@ -724,7 +736,8 @@
* arbitrary pointer identifier).
*/
public final float getSize() {
- return mDataSamples[SAMPLE_SIZE];
+ //Log.v("MotionEvent","getSize");
+ return mDataSamples[SAMPLE_SIZE]; // SAMPLE_SIZE=3
}
/**
@@ -732,6 +745,7 @@
* >= 1.
*/
public final int getPointerCount() {
+ //Log.v("MotionEvent","getPointerCount");
return mNumPointers;
}
@@ -744,6 +758,7 @@
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*/
public final int getPointerId(int pointerIndex) {
+ //Log.v("MotionEvent:getHistoricalX","getPointerId("+pointerIndex+")= "+mPointerIdentifiers[pointerIndex] );
return mPointerIdentifiers[pointerIndex];
}
@@ -756,6 +771,7 @@
* that pointer identifier.
*/
public final int findPointerIndex(int pointerId) {
+ //Log.v("MotionEvent","findPointerIndex");
int i = mNumPointers;
while (i > 0) {
i--;
@@ -776,6 +792,7 @@
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*/
public final float getX(int pointerIndex) {
+ //Log.v("MotionEvent","getX(in)");
return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_X];
}
@@ -789,6 +806,7 @@
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*/
public final float getY(int pointerIndex) {
+ //Log.v("MotionEvent","getY(in)");
return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_Y];
}
@@ -804,6 +822,7 @@
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*/
public final float getPressure(int pointerIndex) {
+ //Log.v("MotionEvent","getPressure(in)");
return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_PRESSURE];
}
@@ -820,6 +839,7 @@
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*/
public final float getSize(int pointerIndex) {
+ //Log.v("MotionEvent","getSize(in)");
return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_SIZE];
}
@@ -834,6 +854,7 @@
* @see KeyEvent#getMetaState()
*/
public final int getMetaState() {
+ //Log.v("MotionEvent","getMetaState");
return mMetaState;
}
@@ -844,6 +865,7 @@
* and views.
*/
public final float getRawX() {
+ //Log.v("MotionEvent","getRawX");
return mRawX;
}
@@ -854,6 +876,7 @@
* and views.
*/
public final float getRawY() {
+ //Log.v("MotionEvent","getRawY");
return mRawY;
}
@@ -864,6 +887,7 @@
* @return Returns the precision of X coordinates being reported.
*/
public final float getXPrecision() {
+ //Log.v("MotionEvent","getXPrecision");
return mXPrecision;
}
@@ -874,6 +898,7 @@
* @return Returns the precision of Y coordinates being reported.
*/
public final float getYPrecision() {
+ //Log.v("MotionEvent","getYPrecision");
return mYPrecision;
}
@@ -886,6 +911,7 @@
* @return Returns the number of historical points in the event.
*/
public final int getHistorySize() {
+ //Log.v("MotionEvent","getHistorySize");
return mNumSamples - 1;
}
@@ -900,6 +926,7 @@
* @see #getEventTime
*/
public final long getHistoricalEventTime(int pos) {
+ //Log.v("MotionEvent","getHistoricalEventTime(pos)");
return mTimeSamples[pos + 1];
}
@@ -908,6 +935,7 @@
* arbitrary pointer identifier).
*/
public final float getHistoricalX(int pos) {
+ //Log.v("MotionEvent","getHistoricalX(pos)");
return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + SAMPLE_X];
}
@@ -916,6 +944,7 @@
* arbitrary pointer identifier).
*/
public final float getHistoricalY(int pos) {
+ //Log.v("MotionEvent","getHistoricalY(pos)");
return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + SAMPLE_Y];
}
@@ -924,6 +953,7 @@
* arbitrary pointer identifier).
*/
public final float getHistoricalPressure(int pos) {
+ //Log.v("MotionEvent","getHistoricalPressure(pos)");
return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + SAMPLE_PRESSURE];
}
@@ -932,6 +962,7 @@
* arbitrary pointer identifier).
*/
public final float getHistoricalSize(int pos) {
+ //Log.v("MotionEvent","getHistoricalSize(pos)");
return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + SAMPLE_SIZE];
}
@@ -949,6 +980,8 @@
* @see #getX
*/
public final float getHistoricalX(int pointerIndex, int pos) {
+ //Log.v("MotionEvent:getHistoricalX","pointerIndex="+pointerIndex+" pos="+pos+" mDataSamples["+
+ //((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_X+"]" );
return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers)
+ (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_X];
}
@@ -967,6 +1000,8 @@
* @see #getY
*/
public final float getHistoricalY(int pointerIndex, int pos) {
+ //Log.v("MotionEvent:getHistoricalY","pointerIndex="+pointerIndex+" pos="+pos+" mDataSamples["+
+ //((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_Y+"]" );
return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers)
+ (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_Y];
}
@@ -985,6 +1020,7 @@
* @see #getPressure
*/
public final float getHistoricalPressure(int pointerIndex, int pos) {
+ //Log.v("MotionEvent","getHistoricalPressure(in,pos)");
return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers)
+ (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_PRESSURE];
}
@@ -1003,6 +1039,7 @@
* @see #getSize
*/
public final float getHistoricalSize(int pointerIndex, int pos) {
+ //Log.v("MotionEvent","getHistoricalSize(in,pos)");
return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers)
+ (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_SIZE];
}
@@ -1013,6 +1050,7 @@
* numbers are arbitrary and you shouldn't depend on the values.
*/
public final int getDeviceId() {
+ //Log.v("MotionEvent","getDeviceId");
return mDeviceId;
}
@@ -1027,6 +1065,7 @@
* @see #EDGE_BOTTOM
*/
public final int getEdgeFlags() {
+ //Log.v("MotionEvent","getEdgeFlahs");
return mEdgeFlags;
}
@@ -1038,6 +1077,7 @@
* @see #getEdgeFlags()
*/
public final void setEdgeFlags(int flags) {
+ //Log.v("MotionEvent","setEdgeFlags");
mEdgeFlags = flags;
}
@@ -1045,6 +1085,7 @@
* Sets this event's action.
*/
public final void setAction(int action) {
+ //Log.v("MotionEvent","setAction");
mAction = action;
}
@@ -1054,6 +1095,7 @@
* @param deltaY Amount to add to the current Y coordinate of the event.
*/
public final void offsetLocation(float deltaX, float deltaY) {
+ //Log.v("MotionEvent","offsetLocation");
final int N = mNumPointers*mNumSamples*4;
final float[] pos = mDataSamples;
for (int i=0; i<N; i+=NUM_SAMPLE_DATA) {
@@ -1070,6 +1112,7 @@
* @param y New absolute Y location.
*/
public final void setLocation(float x, float y) {
+ //Log.v("MotionEvent","setLocation");
float deltaX = x-mDataSamples[SAMPLE_X];
float deltaY = y-mDataSamples[SAMPLE_Y];
if (deltaX != 0 || deltaY != 0) {
@@ -1090,8 +1133,9 @@
* @param size The new size.
* @param metaState Meta key state.
*/
- public final void addBatch(long eventTime, float x, float y,
- float pressure, float size, int metaState) {
+ public final void addBatch(long eventTime, float x, float y, float pressure, float size, int metaState) {
+ //Log.v("MotionEvent","addBatch");
+
float[] data = mDataSamples;
long[] times = mTimeSamples;
@@ -1143,6 +1187,7 @@
* @hide
*/
public final void addBatch(long eventTime, float[] inData, int metaState) {
+ //Log.v("MotionEvent","addBatch");
float[] data = mDataSamples;
long[] times = mTimeSamples;
@@ -1216,6 +1261,7 @@
}
public void writeToParcel(Parcel out, int flags) {
+ //Log.v("MotionEvent","writeToParcel");
out.writeLong(mDownTime);
out.writeLong(mEventTimeNano);
out.writeInt(mAction);
@@ -1250,6 +1296,10 @@
}
private void readFromParcel(Parcel in) {
+ String text = new String();
+ lastAction = mAction;
+
+
mDownTime = in.readLong();
mEventTimeNano = in.readLong();
mAction = in.readInt();
@@ -1257,10 +1307,12 @@
mRawX = in.readFloat();
mRawY = in.readFloat();
final int NP = in.readInt();
- mNumPointers = NP;
+ mNumPointers = NP;//ポインタ個数
final int NS = in.readInt();
- mNumSamples = NS;
- final int NI = NP*NS;
+ mNumSamples = NS;//サンプル個数
+ final int NI = NP*NS;//ポインタ個数*サンプル個数
+ int ND = 0 ;
+
if (NI > 0) {
int[] ids = mPointerIdentifiers;
if (ids.length < NP) {
@@ -1268,14 +1320,18 @@
}
for (int i=0; i<NP; i++) {
ids[i] = in.readInt();
+ text+="ids["+i+"] = "+ ids[i] +"\n";
}
float[] history = mDataSamples;
- final int ND = NI*NUM_SAMPLE_DATA;
+ //final int ND = NI*NUM_SAMPLE_DATA; // NUM_SAMPLE_DATA = 4
+ ND = NI*NUM_SAMPLE_DATA;
if (history.length < ND) {
mDataSamples = history = new float[ND];
}
for (int i=0; i<ND; i++) {
history[i] = in.readFloat();
+ text += "history["+i+"] = "+history[i] +"\n";
+
}
long[] times = mTimeSamples;
if (times == null || times.length < NS) {
@@ -1283,8 +1339,10 @@
}
for (int i=0; i<NS; i++) {
times[i] = in.readLong();
+ text += "times["+i+"] = "+times[i] +"\n";
}
}
+
mXPrecision = in.readFloat();
mYPrecision = in.readFloat();
mDeviceId = in.readInt();
@@ -1289,6 +1347,84 @@
mYPrecision = in.readFloat();
mDeviceId = in.readInt();
mEdgeFlags = in.readInt();
- }
+ /*
+Log.v("MotionEvent:readFromParcel",
+ "mDownTime = "+ mDownTime +"\n"+
+ "mEventTimeNano = "+ mEventTimeNano +"\n" +
+ "mAction = " + mAction + "\n"+
+ "mMetaState = " + mMetaState +"\n"+
+ "mRawX = " + mRawX + "\n"+
+ "mRawY = " + mRawY + "\n"+
+ // "NP = " + NP
+ "mNumPointers = NP = "+ NP + "\n"+
+ //final int NS = in.readInt();
+ "mNumSamples = NS = " + NS + "\n"+
+ "NP*NS=NI = " + NI + "\n" +
+ "NI*4=ND = " + ND +"\n" + //NI*NUM_SAMPLE_DATA; // NUM_SAMPLE_DATA = 4
+ text +
+ "mXPrecision = " + mXPrecision +"\n"+
+ "mYPrecision = " + mYPrecision +"\n"+
+ "mDeviceId = " + mDeviceId +"\n"+
+ "mEdgeFlags = " + mEdgeFlags +"\n" );
+ */
+ //擬似デュアルタッチ対応
+ if( mDeviceId == 65536 && NP>=1 && NS>=1 ) {
+ if( mDataSamples[SAMPLE_SIZE] > 0 ) {
+ final float MAX_SIZE = 960;
+ float dHistory[] = new float[ND*2];
+ int dIds[] = new int[2];
+ dIds[0]=0;
+ dIds[1]=1;
+
+ for(int i=0; i< ND*2; i+=8 ) {
+ dHistory[i]= mDataSamples[i/2]-( mDataSamples[i/2+3]*MAX_SIZE)/2;
+ dHistory[i+1]= mDataSamples[i/2+1];
+ dHistory[i+2]= mDataSamples[i/2+2];
+ dHistory[i+3]= 0;
+ dHistory[i+4]= mDataSamples[i/2]+( mDataSamples[i/2+3]*MAX_SIZE)/2;;
+ dHistory[i+5]= mDataSamples[i/2+1];
+ dHistory[i+6]= mDataSamples[i/2+2];
+ dHistory[i+7]= 0;
+ }
+ mNumPointers = 2; //ポインタ個数2
+ //mNumSamples *=1;//サンプル個数不変
+ mPointerIdentifiers = dIds;//ID2つ
+ mDataSamples = dHistory; // 個別データ
+
+ if(isDual == false && lastAction == ACTION_MOVE ){
+ mAction = ACTION_POINTER_2_DOWN;
+ //Log.i("MotionEvent:readFromParcel","ACTION_POINTER_2_DOWN");
+ isDual = true;
+ }
+
+ /*
+Log.i("MotionEvent:readFromParcel","dual touch");
+Log.v("MotionEvent:readFromParcel",
+ "mNumPointers = "+ mNumPointers + "\n"+
+ "mNumSamples = " + mNumSamples + "\n"+
+ "NP*NS=NI = " + NI + "\n" +
+ "NI*4=ND = " + ND +"\n" ); //NI*NUM_SAMPLE_DATA; // NUM_SAMPLE_DATA = 4
+Log.v("MotionEvent:readFromParcel","mNumPointers * mNumSamples * NUM_SAMPLE_DATA="+mNumPointers * mNumSamples * NUM_SAMPLE_DATA);
+Log.v("MotionEvent:readFromParcel","mDataSamples.length="+mDataSamples.length );
+for( int i=0; i<mNumPointers * mNumSamples * NUM_SAMPLE_DATA; i++ ) {
+Log.v("MotionEvent:readFromParcel",
+ "mDataSamples["+(i)+"] =" + mDataSamples[i] );
+}
+for( int i=0; i<mNumPointers; i++ ) {
+Log.v("MotionEvent:readFromParcel",
+ "mPointerIdentifiers["+(i)+"] =" + mPointerIdentifiers[i] );
+}
+for( int i=0; i<mNumSamples; i++ ) {
+Log.v("MotionEvent:readFromParcel",
+ "mTimeSamples["+(i)+"] =" + mTimeSamples[i] );
+ }*/
+ }
+ else if (isDual == true) {
+ mAction = ACTION_POINTER_2_UP;
+ //Log.i("MotionEvent:readFromParcel","ACTION_POINTER_2_UP");
+ isDual = false;
+ }
+ }
+ }
}
以上
コメント
最終更新:2011年01月06日 00:00