Merge changes from topic "emergency-dialer-metrics" am: fa47390a8f am: 6f4617b6d2
am: f542347b36
Change-Id: Ie2e77308094805bdf0ca3512c2ab91e9361972fa
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index 5d18f8e..84c2d65 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -23,6 +23,7 @@
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.app.KeyguardManager;
import android.app.WallpaperManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -31,12 +32,18 @@
import android.database.DataSetObserver;
import android.graphics.Color;
import android.graphics.Point;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
import android.media.AudioManager;
import android.media.ToneGenerator;
+import android.metrics.LogMaker;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.PersistableBundle;
+import android.os.SystemClock;
import android.provider.Settings;
import android.telecom.ParcelableCallAnalytics;
import android.telecom.PhoneAccount;
@@ -71,6 +78,8 @@
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.colorextraction.drawable.GradientDrawable;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.phone.common.dialpad.DialpadKeyButton;
import com.android.phone.common.util.ViewUtil;
import com.android.phone.common.widget.ResizingTextEditText;
@@ -108,13 +117,81 @@
public class EmergencyDialer extends Activity implements View.OnClickListener,
View.OnLongClickListener, View.OnKeyListener, TextWatcher,
DialpadKeyButton.OnPressedListener, ColorExtractor.OnColorsChangedListener,
- EmergencyShortcutButton.OnConfirmClickListener {
+ EmergencyShortcutButton.OnConfirmClickListener, SensorEventListener {
+
+ private class MetricsWriter {
+ // Metrics constants indicating the entry type that user opened emergency dialer.
+ // This info is sent from system UI with EXTRA_ENTRY_TYPE. Please make them being
+ // in sync with those in com.android.systemui.util.EmergencyDialerConstants.
+ public static final int ENTRY_TYPE_UNKNOWN = 0;
+ public static final int ENTRY_TYPE_LOCKSCREEN_BUTTON = 1;
+ public static final int ENTRY_TYPE_POWER_MENU = 2;
+
+ // Metrics constants indicating the UI that user made phone call.
+ public static final int CALL_SOURCE_DIALPAD = 0;
+ public static final int CALL_SOURCE_SHORTCUT = 1;
+
+ // Metrics constants indicating the phone number type of a call user made.
+ public static final int PHONE_NUMBER_TYPE_GENERAL = 0;
+ public static final int PHONE_NUMBER_TYPE_EMERGENCY = 1;
+
+ // Metrics constants indicating the actions performed by user.
+ public static final int USER_ACTION_NONE = 0x0;
+ public static final int USER_ACTION_OPEN_DIALPAD = 0x1;
+ public static final int USER_ACTION_OPEN_EMERGENCY_INFO = 0x2;
+ public static final int USER_ACTION_MAKE_CALL_VIA_DIALPAD = 0x4;
+ public static final int USER_ACTION_MAKE_CALL_VIA_SHORTCUT = 0x8;
+
+ private MetricsLogger mMetricsLogger = new MetricsLogger();
+
+ public void writeMetricsForEnter() {
+ int entryType = getIntent().getIntExtra(EXTRA_ENTRY_TYPE, ENTRY_TYPE_UNKNOWN);
+ KeyguardManager keyguard = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
+ mMetricsLogger.write(new LogMaker(MetricsEvent.EMERGENCY_DIALER)
+ .setType(MetricsEvent.TYPE_OPEN)
+ .setSubtype(entryType)
+ .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_IS_SCREEN_LOCKED,
+ keyguard.isKeyguardLocked() ? 1 : 0));
+ }
+
+ public void writeMetricsForExit() {
+ int entryType = getIntent().getIntExtra(EXTRA_ENTRY_TYPE, ENTRY_TYPE_UNKNOWN);
+ long userStayDuration = SystemClock.elapsedRealtime() - mUserEnterTimeMillis;
+ mMetricsLogger.write(new LogMaker(MetricsEvent.EMERGENCY_DIALER)
+ .setType(MetricsEvent.TYPE_CLOSE)
+ .setSubtype(entryType)
+ .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_USER_ACTIONS, mUserActions)
+ .addTaggedData(
+ MetricsEvent.FIELD_EMERGENCY_DIALER_DURATION_MS, userStayDuration));
+ }
+
+ public void writeMetricsForMakingCall(int callSource, int phoneNumberType,
+ boolean hasShortcut) {
+ mMetricsLogger.write(new LogMaker(MetricsEvent.EMERGENCY_DIALER_MAKE_CALL)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .setSubtype(callSource)
+ .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_PHONE_NUMBER_TYPE,
+ phoneNumberType)
+ .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_PHONE_NUMBER_HAS_SHORTCUT,
+ hasShortcut ? 1 : 0)
+ .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_IN_POCKET,
+ mIsProximityNear ? 1 : 0));
+ }
+ }
+
// Keys used with onSaveInstanceState().
private static final String LAST_NUMBER = "lastNumber";
// Intent action for this activity.
public static final String ACTION_DIAL = "com.android.phone.EmergencyDialer.DIAL";
+ /**
+ * Extra included in {@link #ACTION_DIAL} to indicate the entry type that user starts
+ * the emergency dialer.
+ */
+ public static final String EXTRA_ENTRY_TYPE =
+ "com.android.phone.EmergencyDialer.extra.ENTRY_TYPE";
+
// List of dialer button IDs.
private static final int[] DIALER_KEYS = new int[] {
R.id.one, R.id.two, R.id.three,
@@ -212,6 +289,22 @@
private boolean mAreEmergencyDialerShortcutsEnabled;
+ private MetricsWriter mMetricsWriter;
+ private SensorManager mSensorManager;
+ private Sensor mProximitySensor;
+ private boolean mIsProximityNear = false;
+
+ /**
+ * The time, in millis, since boot when user opened emergency dialer.
+ * This is used when calculating the user stay duration for metrics data.
+ */
+ private long mUserEnterTimeMillis = 0;
+
+ /**
+ * Bit flag indicating the actions performed by user. This is used for metrics data.
+ */
+ private int mUserActions = MetricsWriter.USER_ACTION_NONE;
+
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Do nothing
@@ -246,6 +339,12 @@
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ mMetricsWriter = new MetricsWriter();
+ mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
+ if (mSensorManager != null) {
+ mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ }
+
// Allow this activity to be displayed in front of the keyguard / lockscreen.
setShowWhenLocked(true);
// Allow turning screen on
@@ -484,6 +583,13 @@
public void onConfirmClick(EmergencyShortcutButton button) {
if (button == null) return;
+ mUserActions |= MetricsWriter.USER_ACTION_MAKE_CALL_VIA_SHORTCUT;
+
+ // We interest on the context when user has intention to make phone call,
+ // so write metrics here for shortcut number even the call may not be created.
+ mMetricsWriter.writeMetricsForMakingCall(MetricsWriter.CALL_SOURCE_SHORTCUT,
+ MetricsWriter.PHONE_NUMBER_TYPE_EMERGENCY, true);
+
String phoneNumber = button.getPhoneNumber();
if (!TextUtils.isEmpty(phoneNumber)) {
@@ -517,11 +623,13 @@
return;
}
case R.id.floating_action_button_dialpad: {
+ mUserActions |= MetricsWriter.USER_ACTION_OPEN_DIALPAD;
mDigits.getText().clear();
switchView(mDialpadView, mEmergencyShortcutView, true);
return;
}
case R.id.emergency_info_button: {
+ mUserActions |= MetricsWriter.USER_ACTION_OPEN_EMERGENCY_INFO;
Intent intent = (Intent) view.getTag(R.id.tag_intent);
if (intent != null) {
startActivity(intent);
@@ -623,6 +731,11 @@
@Override
protected void onStart() {
super.onStart();
+
+ mUserEnterTimeMillis = SystemClock.elapsedRealtime();
+ mUserActions = MetricsWriter.USER_ACTION_NONE;
+ mMetricsWriter.writeMetricsForEnter();
+
// It does not support dark text theme, when emergency dialer shortcuts are enabled.
// And set background color to black.
if (mAreEmergencyDialerShortcutsEnabled) {
@@ -668,6 +781,11 @@
protected void onResume() {
super.onResume();
+ if (mProximitySensor != null) {
+ mSensorManager.registerListener(
+ this, mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+
// retrieve the DTMF tone play back setting.
mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),
Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
@@ -692,11 +810,15 @@
@Override
public void onPause() {
super.onPause();
+ if (mProximitySensor != null) {
+ mSensorManager.unregisterListener(this, mProximitySensor);
+ }
}
@Override
protected void onStop() {
super.onStop();
+ mMetricsWriter.writeMetricsForExit();
mColorExtractor.removeOnColorsChangedListener(this);
}
@@ -734,6 +856,7 @@
* place the call, but check to make sure it is a viable number.
*/
private void placeCall() {
+ mUserActions |= MetricsWriter.USER_ACTION_MAKE_CALL_VIA_DIALPAD;
mLastNumber = mDigits.getText().toString();
// Convert into emergency number according to emergency conversion map.
@@ -750,6 +873,10 @@
playTone(ToneGenerator.TONE_PROP_NACK);
return;
}
+
+ mMetricsWriter.writeMetricsForMakingCall(MetricsWriter.CALL_SOURCE_DIALPAD,
+ MetricsWriter.PHONE_NUMBER_TYPE_EMERGENCY, isShortcutNumber(mLastNumber));
+
Bundle extras = new Bundle();
extras.putInt(TelecomManager.EXTRA_CALL_SOURCE,
ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_DIALPAD);
@@ -758,6 +885,11 @@
} else {
if (DBG) Log.d(LOG_TAG, "rejecting bad requested number " + mLastNumber);
+ // We interest on the context when user has intention to make phone call,
+ // so write metrics here for non-emergency numbers even these numbers are rejected.
+ mMetricsWriter.writeMetricsForMakingCall(MetricsWriter.CALL_SOURCE_DIALPAD,
+ MetricsWriter.PHONE_NUMBER_TYPE_GENERAL, false);
+
showDialog(BAD_EMERGENCY_NUMBER_DIALOG);
}
mDigits.getText().delete(0, mDigits.getText().length());
@@ -1145,4 +1277,30 @@
}
});
}
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ float distance = event.values[0];
+ mIsProximityNear = (distance < mProximitySensor.getMaximumRange());
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // Not used.
+ }
+
+ private boolean isShortcutNumber(String number) {
+ if (TextUtils.isEmpty(number) || mEmergencyShortcutButtonList == null) {
+ return false;
+ }
+
+ boolean isShortcut = false;
+ for (EmergencyShortcutButton button : mEmergencyShortcutButtonList) {
+ if (button != null && number.equals(button.getPhoneNumber())) {
+ isShortcut = true;
+ break;
+ }
+ }
+ return isShortcut;
+ }
}
diff --git a/src/com/android/phone/EmergencyShortcutButton.java b/src/com/android/phone/EmergencyShortcutButton.java
index 275dac0..59b3794 100644
--- a/src/com/android/phone/EmergencyShortcutButton.java
+++ b/src/com/android/phone/EmergencyShortcutButton.java
@@ -19,6 +19,8 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
+import android.metrics.LogMaker;
+import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
@@ -30,6 +32,9 @@
import androidx.annotation.NonNull;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
/**
* Emergency shortcut button displays a local emergency phone number information(including phone
* number, and phone type). To decrease false clicking, it need to click twice to confirm to place
@@ -61,6 +66,12 @@
private boolean mConfirmViewHiding;
+ /**
+ * The time, in millis, since boot when user taps on shortcut button to reveal confirm view.
+ * This is used for metrics when calculating the interval between reveal tap and confirm tap.
+ */
+ private long mTimeOfRevealTapInMillis = 0;
+
public EmergencyShortcutButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -185,6 +196,8 @@
switch (view.getId()) {
case R.id.emergency_call_number_info_view:
if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
+ // TalkBack itself includes a prompt to confirm click action implicitly,
+ // so we don't need an additional confirmation with second tap on button.
if (mOnConfirmClickListener != null) {
mOnConfirmClickListener.onConfirmClick(this);
}
@@ -193,6 +206,15 @@
}
break;
case R.id.emergency_call_confirm_view:
+ if (mTimeOfRevealTapInMillis != 0) {
+ long timeBetweenTwoTaps =
+ SystemClock.elapsedRealtime() - mTimeOfRevealTapInMillis;
+ // Reset reveal time to zero for next reveal-confirm taps pair.
+ mTimeOfRevealTapInMillis = 0;
+
+ writeMetricsForConfirmTap(timeBetweenTwoTaps);
+ }
+
if (mOnConfirmClickListener != null) {
mOnConfirmClickListener.onConfirmClick(this);
}
@@ -204,6 +226,7 @@
mConfirmViewHiding = false;
mConfirmView.setVisibility(View.VISIBLE);
+ mTimeOfRevealTapInMillis = SystemClock.elapsedRealtime();
int centerX = mCallNumberInfoView.getLeft() + mCallNumberInfoView.getWidth() / 2;
int centerY = mCallNumberInfoView.getTop() + mCallNumberInfoView.getHeight() / 2;
Animator reveal = ViewAnimationUtils.createCircularReveal(
@@ -240,6 +263,8 @@
@Override
public void onAnimationEnd(Animator animation) {
mConfirmView.setVisibility(INVISIBLE);
+ // Reset reveal time to zero for next reveal-confirm taps pair.
+ mTimeOfRevealTapInMillis = 0;
}
});
reveal.start();
@@ -254,4 +279,12 @@
hideSelectedButton();
}
};
+
+ private void writeMetricsForConfirmTap(long timeBetweenTwoTaps) {
+ LogMaker logContent = new LogMaker(MetricsEvent.EMERGENCY_DIALER_SHORTCUT_CONFIRM_TAP)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_SHORTCUT_TAPS_INTERVAL,
+ timeBetweenTwoTaps);
+ MetricsLogger.action(logContent);
+ }
}