查看源码发现默认在通话中,按数字键是没有反应的,也就是说我们要实现这个需求,首先得按数字键后显示拨号盘,接着实现点击效果。
修改路径:
packages/apps/Dialer/java/com/android/incallui/InCallActivity.java(通话中的页面)
packages/apps/Dialer/java/com/android/incallui/DialpadFragment.java(通话中的拨号盘)
packages/apps/Dialer/java/com/android/dialer/dialpadview/DialpadKeyButton.java(拨号盘的数字 button )
由于直接在当前页面按数字按键,实现按键监听即可,
InCallActivity.java
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
/*lichang*/
Log.d("lichang", "有按键按下" + keyCode);
if (displayMap.containsKey(keyCode)) {
DialpadFragment dialpadFragment = getDialpadFragment();
if (event.getRepeatCount() == 0) {
if (dialpadFragment == null) {
showDialpadFragment(true /* show */, animateDialpadOnShow /* animate */);
dialpadFragment = getDialpadFragment();
dialpadFragment.simulatedClick(displayMap.get(keyCode));
return true;
} else {
if (!dialpadFragment.isVisible()) {
showDialpadFragment(true /* show */, animateDialpadOnShow /* animate */);
dialpadFragment = getDialpadFragment();
}
dialpadFragment.simulatedClick(displayMap.get(keyCode));
return false;
}
}
}
..
}
然后在实现点击效果
DialpadFragment.java
public void simulatedClick(Character c) {
Log.d("lichang", "simulatedClick 模拟点击 " + c);
DialpadKeyButton dialpadKey;
for (int i = 0; i < buttonIds.length; i++) {
if (displayMap.get(buttonIds[i]) == c) {
Log.d("lichang", "simulatedClick 模拟 ");
DialpadView dialpadView = (DialpadView) getView().findViewById(R.id.dialpad_view);
dialpadKey = (DialpadKeyButton) dialpadView.findViewById(buttonIds[i]);
dialpadKey.simulateClickForAccessibility();
}
}
}
接下来简述一下点击事件思路,首先看到 DialpadKeyButton 以为就是个 button,结果调用 performClick() 方法后无效果,于是查看 DialpadKeyButton
路径:
packages/apps/Dialer/java/com/android/dialer/dialpadview/DialpadKeyButton.java
/*
* Copyright (C) 2012 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.dialer.dialpadview;
import android.content.Context;
import android.graphics.RectF;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.widget.FrameLayout;
/**
* Custom class for dialpad buttons.
*
* <p>When touch exploration mode is enabled for accessibility, this class implements the
* lift-to-type interaction model:
*
* <ul>
* <li>Hovering over the button will cause it to gain accessibility focus
* <li>Removing the hover pointer while inside the bounds of the button will perform a click action
* <li>If long-click is supported, hovering over the button for a longer period of time will switch
* to the long-click action
* <li>Moving the hover pointer outside of the bounds of the button will restore to the normal click
* action
* <ul>
*/
public class DialpadKeyButton extends FrameLayout {
/** Accessibility manager instance used to check touch exploration state. */
private AccessibilityManager accessibilityManager;
/** Bounds used to filter HOVER_EXIT events. */
private RectF hoverBounds = new RectF();
/** Alternate content description for long-hover state. */
private CharSequence longHoverContentDesc;
/** Backup of clickable property. Used for accessibility. */
private boolean wasClickable;
/** Backup of long-clickable property. Used for accessibility. */
private boolean wasLongClickable;
private OnPressedListener onPressedListener;
public DialpadKeyButton(Context context, AttributeSet attrs) {
super(context, attrs);
initForAccessibility(context);
}
public DialpadKeyButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initForAccessibility(context);
}
public void setOnPressedListener(OnPressedListener onPressedListener) {
this.onPressedListener = onPressedListener;
}
private void initForAccessibility(Context context) {
accessibilityManager =
(AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
}
public void setLongHoverContentDescription(CharSequence contentDescription) {
longHoverContentDesc = contentDescription;
}
@Override
public void setPressed(boolean pressed) {
super.setPressed(pressed);
if (onPressedListener != null) {
onPressedListener.onPressed(this, pressed);
}
}
@Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
hoverBounds.left = getPaddingLeft();
hoverBounds.right = w - getPaddingRight();
hoverBounds.top = getPaddingTop();
hoverBounds.bottom = h - getPaddingBottom();
}
@Override
public boolean performAccessibilityAction(int action, Bundle arguments) {
if (action == AccessibilityNodeInfo.ACTION_CLICK) {
simulateClickForAccessibility();
return true;
}
return super.performAccessibilityAction(action, arguments);
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
// If the button has a long hover description, ask talkback to announce the action follow by
// the description (for example "double tap and hold to call voicemail").
if (!TextUtils.isEmpty(longHoverContentDesc)) {
AccessibilityAction longClickAction =
new AccessibilityAction(AccessibilityNodeInfo.ACTION_LONG_CLICK, longHoverContentDesc);
info.addAction(longClickAction);
}
}
@Override
public boolean onHoverEvent(MotionEvent event) {
// When touch exploration is turned on, lifting a finger while inside
// the button's hover target bounds should perform a click action.
if (accessibilityManager.isEnabled() && accessibilityManager.isTouchExplorationEnabled()) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_HOVER_ENTER:
// Lift-to-type temporarily disables double-tap activation.
wasClickable = isClickable();
wasLongClickable = isLongClickable();
setClickable(false);
setLongClickable(false);
break;
case MotionEvent.ACTION_HOVER_EXIT:
if (hoverBounds.contains(event.getX(), event.getY())) {
simulateClickForAccessibility();
}
setClickable(wasClickable);
setLongClickable(wasLongClickable);
break;
default: // No-op
break;
}
}
return super.onHoverEvent(event);
}
/**
* When accessibility is on, simulate press and release to preserve the semantic meaning of
* performClick(). Required for Braille support.
*/
public void simulateClickForAccessibility() {
// Checking the press state prevents double activation.
if (isPressed()) {
return;
}
setPressed(true);
// Stay consistent with performClick() by sending the event after
// setting the pressed state but before performing the action.
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
setPressed(false);
}
public interface OnPressedListener {
void onPressed(View view, boolean pressed);
}
}
其中提到了当可访问性打开时,模拟按下和释放,以保留performClick()的语义含义。需要盲文支持。
完整patch如下:
diff --git a/java/com/android/dialer/dialpadview/DialpadKeyButton.java b/java/com/android/dialer/dialpadview/DialpadKeyButton.java
index 47553b6..d2c8e6d 100644
--- a/java/com/android/dialer/dialpadview/DialpadKeyButton.java
+++ b/java/com/android/dialer/dialpadview/DialpadKeyButton.java
@@ -159,7 +159,7 @@ public class DialpadKeyButton extends FrameLayout {
* When accessibility is on, simulate press and release to preserve the semantic meaning of
* performClick(). Required for Braille support.
*/
- private void simulateClickForAccessibility() {
+ public void simulateClickForAccessibility() {
// Checking the press state prevents double activation.
if (isPressed()) {
return;
diff --git a/java/com/android/incallui/DialpadFragment.java b/java/com/android/incallui/DialpadFragment.java
index f069818..e80c2ed 100644
--- a/java/com/android/incallui/DialpadFragment.java
+++ b/java/com/android/incallui/DialpadFragment.java
@@ -191,6 +191,19 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadUi>
currentTextColor = textColor;
}
+ public void simulatedClick(Character c) {
+ Log.d("lichang", "simulatedClick 模拟点击 " + c);
+ DialpadKeyButton dialpadKey;
+ for (int i = 0; i < buttonIds.length; i++) {
+ if (displayMap.get(buttonIds[i]) == c) {
+ Log.d("lichang", "simulatedClick 模拟 ");
+ DialpadView dialpadView = (DialpadView) getView().findViewById(R.id.dialpad_view);
+ dialpadKey = (DialpadKeyButton) dialpadView.findViewById(buttonIds[i]);
+ dialpadKey.simulateClickForAccessibility();
+ }
+ }
+ }
+
@Override
public void onDestroyView() {
dtmfKeyListener = null;
diff --git a/java/com/android/incallui/InCallActivity.java b/java/com/android/incallui/InCallActivity.java
index 4e36046..8ab2c12 100644
--- a/java/com/android/incallui/InCallActivity.java
+++ b/java/com/android/incallui/InCallActivity.java
@@ -53,6 +53,7 @@ import android.telecom.PhoneAccountHandle;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
+import android.util.ArrayMap;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.MotionEvent;
@@ -118,6 +119,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.Map;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; //write external-storage
import static android.Manifest.permission.READ_EXTERNAL_STORAGE; //eard external-storage
@@ -147,6 +149,44 @@ public class InCallActivity extends TransactionSafeFragmentActivity
private static final int DIALPAD_REQUEST_SHOW = 2;
private static final int DIALPAD_REQUEST_HIDE = 3;
+ /*lichang*/
+
+ /** Hash Map to map a view id to a character */
+ private static final Map<Integer, Character> displayMap = new ArrayMap<>();
+
+ /** Set up the static maps */
+ static {
+ // Map the buttons to the display characters
+ displayMap.put(KeyEvent.KEYCODE_1, '1');
+ displayMap.put(KeyEvent.KEYCODE_2, '2');
+ displayMap.put(KeyEvent.KEYCODE_3, '3');
+ displayMap.put(KeyEvent.KEYCODE_4, '4');
+ displayMap.put(KeyEvent.KEYCODE_5, '5');
+ displayMap.put(KeyEvent.KEYCODE_6, '6');
+ displayMap.put(KeyEvent.KEYCODE_7, '7');
+ displayMap.put(KeyEvent.KEYCODE_8, '8');
+ displayMap.put(KeyEvent.KEYCODE_9, '9');
+ displayMap.put(KeyEvent.KEYCODE_0, '0');
+ displayMap.put(KeyEvent.KEYCODE_POUND, '#');
+ displayMap.put(KeyEvent.KEYCODE_STAR, '*');
+ }
+
+ private final int[] buttonIds =
+ new int[] {
+ R.id.zero,
+ R.id.one,
+ R.id.two,
+ R.id.three,
+ R.id.four,
+ R.id.five,
+ R.id.six,
+ R.id.seven,
+ R.id.eight,
+ R.id.nine,
+ R.id.star,
+ R.id.pound
+ };
+
// UNISOC: add rejectmessage action in the notification.
private static final String SHOW_REJECT_MESSAGE_DIALOG = "InCallActivity.reject_message_dialog";
private static Optional<Integer> audioRouteForTesting = Optional.empty();
@@ -822,6 +862,26 @@ public class InCallActivity extends TransactionSafeFragmentActivity
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
+ /*lichang*/
+ Log.d("lichang", "有按键按下" + keyCode);
+ if (displayMap.containsKey(keyCode)) {
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ if (event.getRepeatCount() == 0) {
+ if (dialpadFragment == null) {
+ showDialpadFragment(true /* show */, animateDialpadOnShow /* animate */);
+ dialpadFragment = getDialpadFragment();
+ dialpadFragment.simulatedClick(displayMap.get(keyCode));
+ return true;
+ } else {
+ if (!dialpadFragment.isVisible()) {
+ showDialpadFragment(true /* show */, animateDialpadOnShow /* animate */);
+ dialpadFragment = getDialpadFragment();
+ }
+ dialpadFragment.simulatedClick(displayMap.get(keyCode));
+ return false;
+ }
+ }
+ }
switch (keyCode) {
case KeyEvent.KEYCODE_CALL:
if (!InCallPresenter.getInstance().handleCallKey()) {