/* * Copyright (C) 2015 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.android.systemui.classifier; import android.content.Context; import android.database.ContentObserver; import android.hardware.SensorEvent; import android.os.Build; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; import android.view.MotionEvent; import java.util.ArrayList; /** * An classifier trying to determine whether it is a human interacting with the phone or not. */ public class HumanInteractionClassifier extends Classifier { private static final String HIC_ENABLE = "HIC_enable"; private static HumanInteractionClassifier sInstance = null; private final Handler mHandler = new Handler(); private final Context mContext; private ArrayList mStrokeClassifiers = new ArrayList<>(); private ArrayList mGestureClassifiers = new ArrayList<>(); private final int mStrokeClassifiersSize; private final int mGestureClassifiersSize; private HistoryEvaluator mHistoryEvaluator; private boolean mEnableClassifier = false; private int mCurrentType = Classifier.GENERIC; protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { updateConfiguration(); } }; private HumanInteractionClassifier(Context context) { mContext = context; mClassifierData = new ClassifierData(mContext.getResources().getDisplayMetrics().xdpi, mContext.getResources().getDisplayMetrics().ydpi); mHistoryEvaluator = new HistoryEvaluator(); mStrokeClassifiers.add(new AnglesVarianceClassifier(mClassifierData)); mStrokeClassifiers.add(new SpeedClassifier(mClassifierData)); mStrokeClassifiers.add(new DurationCountClassifier(mClassifierData)); mStrokeClassifiers.add(new EndPointRatioClassifier(mClassifierData)); mStrokeClassifiers.add(new EndPointLengthClassifier(mClassifierData)); mStrokeClassifiers.add(new AccelerationClassifier(mClassifierData)); mStrokeClassifiers.add(new SpeedVarianceClassifier(mClassifierData)); mStrokeClassifiers.add(new LengthCountClassifier(mClassifierData)); mGestureClassifiers.add(new PointerCountClassifier(mClassifierData)); mGestureClassifiers.add(new ProximityClassifier(mClassifierData)); mStrokeClassifiersSize = mStrokeClassifiers.size(); mGestureClassifiersSize = mGestureClassifiers.size(); mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(HIC_ENABLE), false, mSettingsObserver, UserHandle.USER_ALL); updateConfiguration(); } public static HumanInteractionClassifier getInstance(Context context) { if (sInstance == null) { sInstance = new HumanInteractionClassifier(context); } return sInstance; } private void updateConfiguration() { mEnableClassifier = Build.IS_DEBUGGABLE && 0 != Settings.Global.getInt( mContext.getContentResolver(), HIC_ENABLE, 0); } public void setType(int type) { mCurrentType = type; } @Override public void onTouchEvent(MotionEvent event) { if (mEnableClassifier) { mClassifierData.update(event); for (int i = 0; i < mStrokeClassifiersSize; i++) { mStrokeClassifiers.get(i).onTouchEvent(event); } for (int i = 0; i < mGestureClassifiersSize; i++) { mGestureClassifiers.get(i).onTouchEvent(event); } int size = mClassifierData.getEndingStrokes().size(); for (int i = 0; i < size; i++) { Stroke stroke = mClassifierData.getEndingStrokes().get(i); float evaluation = 0.0f; for (int j = 0; j < mStrokeClassifiersSize; j++) { evaluation += mStrokeClassifiers.get(j).getFalseTouchEvaluation( mCurrentType, stroke); } mHistoryEvaluator.addStroke(evaluation); } int action = event.getActionMasked(); if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { float evaluation = 0.0f; for (int i = 0; i < mGestureClassifiersSize; i++) { evaluation += mGestureClassifiers.get(i).getFalseTouchEvaluation(mCurrentType); } mHistoryEvaluator.addGesture(evaluation); setType(Classifier.GENERIC); } mClassifierData.cleanUp(event); } } @Override public void onSensorChanged(SensorEvent event) { for (int i = 0; i < mStrokeClassifiers.size(); i++) { mStrokeClassifiers.get(i).onSensorChanged(event); } for (int i = 0; i < mGestureClassifiers.size(); i++) { mGestureClassifiers.get(i).onSensorChanged(event); } } public boolean isFalseTouch() { if (mEnableClassifier) { return mHistoryEvaluator.getEvaluation() >= 5.0f; } return false; } public boolean isEnabled() { return mEnableClassifier; } }