Android 11添加电容笔电量监测需求

news2024/12/26 11:36:32

软件平台:Android11

硬件平台:QCS6125

    需求:PAD接入电容笔,该笔通过驱动上报坐标及当前电量等数据,即走系统的input通道,需要系统层监测到该硬件数据,这里主要展示电量,对用户显示提醒。

基本实现思路:通过在InputManager的本地层注册监听回调,实现监测的目的。

    直接上代码改动:

1、input的native层面改动frameworks/native:

diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 84838ec8a..7b900e758 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -102,7 +102,8 @@ NotifyMotionArgs::NotifyMotionArgs(int32_t id, nsecs_t eventTime, int32_t device
                                    const PointerCoords* pointerCoords, float xPrecision,
                                    float yPrecision, float xCursorPosition, float yCursorPosition,
                                    nsecs_t downTime,
-                                   const std::vector<TouchVideoFrame>& videoFrames)
+                                   const std::vector<TouchVideoFrame>& videoFrames,
+                                   uint32_t penBattery)
       : NotifyArgs(id, eventTime),
         deviceId(deviceId),
         source(source),
@@ -121,7 +122,8 @@ NotifyMotionArgs::NotifyMotionArgs(int32_t id, nsecs_t eventTime, int32_t device
         xCursorPosition(xCursorPosition),
         yCursorPosition(yCursorPosition),
         downTime(downTime),
-        videoFrames(videoFrames) {
+        videoFrames(videoFrames),
+        penBattery(penBattery) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         this->pointerProperties[i].copyFrom(pointerProperties[i]);
         this->pointerCoords[i].copyFrom(pointerCoords[i]);
@@ -147,7 +149,8 @@ NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other)
         xCursorPosition(other.xCursorPosition),
         yCursorPosition(other.yCursorPosition),
         downTime(other.downTime),
-        videoFrames(other.videoFrames) {
+        videoFrames(other.videoFrames),
+        penBattery(other.penBattery) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         pointerProperties[i].copyFrom(other.pointerProperties[i]);
         pointerCoords[i].copyFrom(other.pointerCoords[i]);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index fe016af01..f86178917 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3155,11 +3155,11 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
           "displayId=%" PRId32 ", policyFlags=0x%x, "
           "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
           "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
-          "yCursorPosition=%f, downTime=%" PRId64,
+          "yCursorPosition=%f, penBattery=%d, downTime=%" PRId64,
           args->id, args->eventTime, args->deviceId, args->source, args->displayId,
           args->policyFlags, args->action, args->actionButton, args->flags, args->metaState,
           args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision,
-          args->xCursorPosition, args->yCursorPosition, args->downTime);
+          args->xCursorPosition, args->yCursorPosition, args->penBattery, args->downTime);
     for (uint32_t i = 0; i < args->pointerCount; i++) {
         ALOGD("  Pointer %d: id=%d, toolType=%d, "
               "x=%f, y=%f, pressure=%f, size=%f, "
@@ -3192,6 +3192,20 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
               std::to_string(t.duration().count()).c_str());
     }
 
+    if (args->action == AMOTION_EVENT_ACTION_DOWN) {
+        uint32_t penBattery = args->penBattery;
+        if (PEN_BATTERY_MIN <= penBattery && penBattery <= PEN_BATTERY_MAX) {
+            ALOGD("reportPenBattery # penBattery:%d", penBattery);
+
+            android::base::Timer timer;
+            mPolicy->reportPenBattery(args->displayId, args->eventTime, penBattery);
+            if (timer.duration() > SLOW_INTERCEPTION_THRESHOLD) {
+                ALOGW("Excessive delay in reportPenBattery; took %s ms",
+                      std::to_string(timer.duration().count()).c_str());
+            }
+        }
+    }
+
     bool needWake;
     { // acquire lock
         mLock.lock();
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 667af9bbd..cb9b930f1 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -83,6 +83,9 @@ public:
     virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
                                                uint32_t& policyFlags) = 0;
 
+    /* 上报主动笔的电量 */
+    virtual void reportPenBattery(const int32_t displayId, nsecs_t when, uint32_t penBattery) {}
+
     /* Allows the policy a chance to intercept a key before dispatching. */
     virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
                                                   const KeyEvent* keyEvent,
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index 8317b051e..c54830aa8 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -23,6 +23,14 @@
 #include <input/TouchVideoFrame.h>
 #include <utils/RefBase.h>
 
+// 主动笔电量值范围
+#define PEN_BATTERY_UNKNOWN 255
+#define PEN_BATTERY_MIN     0
+#define PEN_BATTERY_MAX     100
+
+// 主动笔电量值的 input-event-code
+#define ABS_PEN_BATT        0x1d
+
 namespace android {
 
 class InputListenerInterface;
@@ -121,6 +129,9 @@ struct NotifyMotionArgs : public NotifyArgs {
     nsecs_t downTime;
     std::vector<TouchVideoFrame> videoFrames;
 
+    // 主动笔电量值
+    uint32_t penBattery;
+
     inline NotifyMotionArgs() { }
 
     NotifyMotionArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
@@ -130,7 +141,8 @@ struct NotifyMotionArgs : public NotifyArgs {
                      const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
                      float xPrecision, float yPrecision, float xCursorPosition,
                      float yCursorPosition, nsecs_t downTime,
-                     const std::vector<TouchVideoFrame>& videoFrames);
+                     const std::vector<TouchVideoFrame>& videoFrames,
+                     uint32_t penBattery = PEN_BATTERY_UNKNOWN);
 
     NotifyMotionArgs(const NotifyMotionArgs& other);
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index decbea4c3..2445fcf0f 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -1407,6 +1407,13 @@ void TouchInputMapper::process(const RawEvent* rawEvent) {
     mCursorScrollAccumulator.process(rawEvent);
     mTouchButtonAccumulator.process(rawEvent);
 
+    if (rawEvent->type == EV_ABS && rawEvent->code == ABS_PEN_BATT) {
+        mPenBattery = rawEvent->value;
+        if (mPenBattery < PEN_BATTERY_MIN || mPenBattery > PEN_BATTERY_MAX) {
+            mPenBattery = PEN_BATTERY_UNKNOWN;
+        }
+    }
+
     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
         sync(rawEvent->when);
     }
@@ -2498,7 +2505,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag
                               policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
                               buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
                               1, &pointerProperties, &pointerCoords, 0, 0, x, y,
-                              mPointerGesture.downTime, /* videoFrames */ {});
+                              mPointerGesture.downTime, /* videoFrames */ {}, mPenBattery);
         getListener()->notifyMotion(&args);
     }
 
@@ -3412,7 +3419,6 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
 
     if (mPointerSimple.down && !down) {
         mPointerSimple.down = false;
-
         // Send up.
         NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
                               policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
@@ -3420,7 +3426,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
                               &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
                               xCursorPosition, yCursorPosition, mPointerSimple.downTime,
-                              /* videoFrames */ {});
+                              /* videoFrames */ {}, mPenBattery);
         getListener()->notifyMotion(&args);
     }
 
@@ -3434,7 +3440,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
                               &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
                               xCursorPosition, yCursorPosition, mPointerSimple.downTime,
-                              /* videoFrames */ {});
+                              /* videoFrames */ {}, mPenBattery);
         getListener()->notifyMotion(&args);
     }
 
@@ -3450,7 +3456,8 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
                                   MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
                                   &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                                   mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
+                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {},
+                                  mPenBattery);
             getListener()->notifyMotion(&args);
         }
 
@@ -3461,7 +3468,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
                               &mPointerSimple.currentCoords, mOrientedXPrecision,
                               mOrientedYPrecision, xCursorPosition, yCursorPosition,
-                              mPointerSimple.downTime, /* videoFrames */ {});
+                              mPointerSimple.downTime, /* videoFrames */ {}, mPenBattery);
         getListener()->notifyMotion(&args);
     }
 
@@ -3476,7 +3483,8 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
                                   MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
                                   &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                                   mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
+                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {},
+                                  mPenBattery);
             getListener()->notifyMotion(&args);
         }
 
@@ -3487,7 +3495,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
                               &mPointerSimple.currentCoords, mOrientedXPrecision,
                               mOrientedYPrecision, xCursorPosition, yCursorPosition,
-                              mPointerSimple.downTime, /* videoFrames */ {});
+                              mPointerSimple.downTime, /* videoFrames */ {}, mPenBattery);
         getListener()->notifyMotion(&args);
     }
 
@@ -3509,7 +3517,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
                               &pointerCoords, mOrientedXPrecision, mOrientedYPrecision,
                               xCursorPosition, yCursorPosition, mPointerSimple.downTime,
-                              /* videoFrames */ {});
+                              /* videoFrames */ {}, mPenBattery);
         getListener()->notifyMotion(&args);
     }
 
@@ -3577,11 +3585,12 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32
     std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
     std::for_each(frames.begin(), frames.end(),
                   [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
+
     NotifyMotionArgs args(getContext()->getNextId(), when, deviceId, source, displayId, policyFlags,
                           action, actionButton, flags, metaState, buttonState,
                           MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
                           pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
-                          downTime, std::move(frames));
+                          downTime, std::move(frames), mPenBattery);
     getListener()->notifyMotion(&args);
 }
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 1c2cc18f9..8bdfa513e 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -380,6 +380,9 @@ protected:
 
     std::vector<VirtualKey> mVirtualKeys;
 
+    // 主动笔电量值
+    int32_t mPenBattery = PEN_BATTERY_UNKNOWN;
+
     virtual void configureParameters();
     virtual void dumpParameters(std::string& dump);
     virtual void configureRawPointerAxes();
@@ -766,4 +769,4 @@ private:
 
 } // namespace android
 
-#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
\ No newline at end of file
+#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H

2、framework java部分改动frameworks/base:

diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e366edd8567..ea32451662f 100755
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -716,6 +716,9 @@
     <protected-broadcast android:name="com.android.systemui.action.START_MORE_SETTINGS" />
     <protected-broadcast android:name="com.android.systemui.demo" />
 
+    <!-- 主动笔电量通知 -->
+    <protected-broadcast android:name="com.android.server.policy.PEN_BATTERY_NOTIFY" />
+
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 115899a2a51..22b344de870 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1988,6 +1988,12 @@ public class InputManagerService extends IInputManager.Stub
                 displayId, whenNanos, policyFlags);
     }
 
+    // Native callback.
+    private int reportPenBatteryInteractive(int displayId, long whenNanos, int penBattery) {
+        return mWindowManagerCallbacks.reportPenBatteryInteractive(displayId, whenNanos,
+                penBattery);
+    }
+
     // Native callback.
     private long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
         return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
@@ -2228,6 +2234,9 @@ public class InputManagerService extends IInputManager.Stub
         int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
                 int policyFlags);
 
+        /* 用户交互时上报主动笔的电量 */
+        int reportPenBatteryInteractive(int displayId, long whenNanos, int penBattery);
+
         public long interceptKeyBeforeDispatching(IBinder token,
                 KeyEvent event, int policyFlags);
 
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 05b178ce530..7a7e114896b 100755
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -226,6 +226,8 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+
 import android.yuanfudao.util.CommonUtils;
 
 /**
@@ -644,6 +646,24 @@ public class PhoneWindowManager implements WindowManagerPolicy {
     private static final int MSG_RINGER_TOGGLE_CHORD = 26;
     private static final int MSG_RESET_ADB_ACTION = 100;
 
+    // 主动笔电量广播
+    private static final String ACTION_PEN_BATTERY_NOTIFY = "com.android.server.policy.PEN_BATTERY_NOTIFY";
+    private static final String PEN_BATTERY_LEVEL = "pen_battery_level";
+
+    // 主动笔电量值范围
+    private static final int PEN_BATTERY_UNKNOWN = 255;
+    private static final int PEN_BATTERY_MIN = 0;
+    private static final int PEN_BATTERY_MAX = 100;
+
+    // 主动笔电量值
+    private int mPenBattery = PEN_BATTERY_UNKNOWN;
+
+    // 主动笔电量广播,唤醒后首次需要发送;启动后首次也需要发送
+    private boolean mPenBatteryNeedNotify = true;
+
+    // 主动笔电量低电时需要发送广播
+    private final int[] mPenBatteryLow = { 10, 20 };
+
     private class PolicyHandler extends Handler {
         @Override
         public void handleMessage(Message msg) {
@@ -4269,6 +4289,51 @@ public class PhoneWindowManager implements WindowManagerPolicy {
         return 0;
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public int reportPenBatteryInteractive(int displayId, long whenNanos, int penBattery) {
+        Slog.d(TAG, "reportPenBatteryInteractive # penBattery:" + penBattery + ", mPenBattery:" + mPenBattery
+                + ", mPenBatteryNeedNotify:" + mPenBatteryNeedNotify);
+        // 唤醒后主动笔电量值低于20%时需要发送广播
+        boolean needNotify = mPenBatteryNeedNotify && mPenBattery <= mPenBatteryLow[1];
+        mPenBatteryNeedNotify = false;
+        if (PEN_BATTERY_MIN <= penBattery && penBattery <= PEN_BATTERY_MAX) {
+            if (mPenBattery != penBattery) {
+                // 主动笔电量值降低到阀值及一下时需要发送广播
+                needNotify = needNotify || isPenBatteryLowNotify(mPenBattery, penBattery);
+                mPenBattery = penBattery;
+            }
+
+            if (needNotify) {
+                notifyPenBattery(mPenBattery);
+            }
+        }
+        return 0;
+    }
+
+    /* 发送主动笔电量通知广播 */
+    private void notifyPenBattery(int penBattery) {
+        Slog.d(TAG, "notifyPenBattery # penBattery:" + penBattery + ", isScreenOn:" + isScreenOn());
+        if (isScreenOn()) {
+            final Intent intent = new Intent(ACTION_PEN_BATTERY_NOTIFY);
+            intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+            intent.putExtra(PEN_BATTERY_LEVEL, penBattery);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
+    }
+
+    /* 主动笔电量值降低到阀值及以下时需要发送广播 */
+    private boolean isPenBatteryLowNotify(int oldValue, int newValue) {
+        boolean needNotify = false;
+        for (int value : mPenBatteryLow) {
+            if (newValue <= value && value < oldValue) {
+                needNotify = true;
+                break;
+            }
+        }
+        return needNotify;
+    }
+
     private boolean shouldDispatchInputWhenNonInteractive(int displayId, int keyCode) {
         // Apply the default display policy to unknown displays as well.
         final boolean isDefaultDisplay = displayId == DEFAULT_DISPLAY
@@ -4560,6 +4625,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
         if (mDisplayFoldController != null) {
             mDisplayFoldController.finishedWakingUp();
         }
+
+        mPenBatteryNeedNotify = true;
     }
 
     private void wakeUpFromPowerKey(long eventTime) {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index cffeaf3f476..e4ef6c77cd8 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -986,6 +986,9 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
     int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
             int policyFlags);
 
+    /* 用户交互时上报主动笔的电量 */
+    int reportPenBatteryInteractive(int displayId, long whenNanos, int penBattery);
+
     /**
      * Called from the input dispatcher thread before a key is dispatched to a window.
      *
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 9c4ac890fed..16966987011 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -326,6 +326,12 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
                 displayId, whenNanos, policyFlags);
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public int reportPenBatteryInteractive(int displayId, long whenNanos, int penBattery) {
+        return mService.mPolicy.reportPenBatteryInteractive(displayId, whenNanos, penBattery);
+    }
+
     /**
      * Provides an opportunity for the window manager policy to process a key before
      * ordinary dispatch.
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 30050547049..91e6a4324c5 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -93,6 +93,7 @@ static struct {
     jmethodID filterInputEvent;
     jmethodID interceptKeyBeforeQueueing;
     jmethodID interceptMotionBeforeQueueingNonInteractive;
+    jmethodID reportPenBatteryInteractive;
     jmethodID interceptKeyBeforeDispatching;
     jmethodID dispatchUnhandledKey;
     jmethodID checkInjectEventsPermission;
@@ -250,6 +251,8 @@ public:
                                             uint32_t& policyFlags) override;
     virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
                                                uint32_t& policyFlags) override;
+    virtual void reportPenBattery(const int32_t displayId, nsecs_t when,
+                                  uint32_t penBattery) override;
     virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
                                                   const KeyEvent* keyEvent,
                                                   uint32_t policyFlags) override;
@@ -1057,6 +1060,21 @@ void NativeInputManager::interceptMotionBeforeQueueing(const int32_t displayId,
     }
 }
 
+void NativeInputManager::reportPenBattery(const int32_t displayId, nsecs_t when,
+                                          uint32_t penBattery) {
+    ATRACE_CALL();
+    // bool interactive = mInteractive.load();
+    // if (interactive) {
+        JNIEnv* env = jniEnv();
+        jint wmActions =
+                env->CallIntMethod(mServiceObj, gServiceClassInfo.reportPenBatteryInteractive,
+                                   displayId, when, penBattery);
+        if (checkAndClearExceptionFromCallback(env, "reportPenBatteryInteractive")) {
+            wmActions = 0;
+        }
+    // }
+}
+
 void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when,
         uint32_t& policyFlags) {
     if (wmActions & WM_ACTION_PASS_TO_USER) {
@@ -1866,6 +1884,9 @@ int register_android_server_InputManager(JNIEnv* env) {
     GET_METHOD_ID(gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive, clazz,
             "interceptMotionBeforeQueueingNonInteractive", "(IJI)I");
 
+    GET_METHOD_ID(gServiceClassInfo.reportPenBatteryInteractive, clazz,
+                  "reportPenBatteryInteractive", "(IJI)I");
+
     GET_METHOD_ID(gServiceClassInfo.interceptKeyBeforeDispatching, clazz,
             "interceptKeyBeforeDispatching",
             "(Landroid/os/IBinder;Landroid/view/KeyEvent;I)J");

3、SystemUI展示电量:

diff --git a/res/layout/pen_battery_notify_dialog_layout.xml b/res/layout/pen_battery_notify_dialog_layout.xml
new file mode 100755
index 0000000..80150c5
--- /dev/null
+++ b/res/layout/pen_battery_notify_dialog_layout.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/pen_battery"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:padding="24dp"
+    android:background="@drawable/custom_power_dialog_bg"
+    android:gravity="center_vertical"
+    android:focusable="true"
+    android:clickable="true"
+    android:orientation="horizontal">
+    <!-- <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:focusable="false"
+        android:clickable="false"
+        android:focusableInTouchMode="false"
+        android:src="@drawable/battery_black_splash_icon"
+    /> -->
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:focusable="false"
+        android:clickable="false"
+        android:focusableInTouchMode="false"
+        android:textColor="@color/black"
+        android:textSize="24sp"
+        android:text="AI手写笔电量:" />
+
+
+    <!-- <ImageView
+        android:id="@+id/battery_level_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="20dp"
+        android:focusable="false"
+        android:clickable="false"
+        android:focusableInTouchMode="false"
+        android:textColor="@color/black"
+        android:src="@drawable/battery_black_splash_icon"
+    /> -->
+
+    <!-- 显示主动笔电量 -->
+    <TextView
+        android:id="@+id/battery_level_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:focusable="false"
+        android:clickable="false"
+        android:focusableInTouchMode="false"
+        android:textColor="@color/black"
+        android:textSize="24sp" />
+</LinearLayout>
diff --git a/src/com/android/systemui/power/PenBatteryNotifyDialog.java b/src/com/android/systemui/power/PenBatteryNotifyDialog.java
new file mode 100755
index 0000000..d677c59
--- /dev/null
+++ b/src/com/android/systemui/power/PenBatteryNotifyDialog.java
@@ -0,0 +1,121 @@
+package com.android.systemui.power;^M
+^M
+import android.content.Context;^M
+import android.graphics.Color;^M
+import android.graphics.drawable.ColorDrawable;^M
+import android.os.Bundle;^M
+import android.util.Slog;^M
+import android.view.GestureDetector;^M
+import android.view.Gravity;^M
+import android.view.MotionEvent;^M
+import android.view.View;^M
+import android.view.Window;^M
+import android.view.WindowManager;^M
+import android.widget.ImageView;^M
+import android.widget.TextView;^M
+^M
+import androidx.annotation.NonNull;^M
+^M
+import com.android.systemui.R;^M
+import com.android.systemui.statusbar.phone.SystemUIDialog;^M
+^M
+public class PenBatteryNotifyDialog extends SystemUIDialog {^M
+    private static final String TAG = "PenBatteryNotifyDialog";^M
+    // static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);^M
+    static final boolean DEBUG = true;^M
+    private static final int MOVE_MIN = 50;^M
+^M
+    private View mRootView;^M
+    private TextView mBatteryLevelView;^M
+    private ImageView mBatteryIcon;^M
+    private GestureDetector mGestureDetector;^M
+    private int mBatteryLevel = 0;^M
+^M
+    public PenBatteryNotifyDialog(@NonNull Context context) {^M
+        super(context);^M
+    }^M
+^M
+    public PenBatteryNotifyDialog(@NonNull Context context, int themeResId) {^M
+        super(context, themeResId);^M
+    }^M
+^M
+    @Override^M
+    protected void onCreate(Bundle savedInstanceState) {^M
+        super.onCreate(savedInstanceState);^M
+        initDialogStyle();^M
+        setContentView(R.layout.pen_battery_notify_dialog_layout);^M
+        initView();^M
+    }^M
+^M
+    private void initView() {^M
+        // 监听上划手势,上划关闭弹窗^M
+        mGestureDetector =^M
+                new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {^M
+                    @Override^M
+                    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,^M
+                            float velocityY) {^M
+                        if (DEBUG) {^M
+                            Slog.d(TAG, "onFling() e1:" + e1 + ", e2:" + e2 + ", velocityX:"^M
+                                    + velocityX + ", velocityY:" + velocityY);^M
+                        }^M
+^M
+                        if (isShowing() && e1.getY() - e2.getY() > MOVE_MIN^M
+                                && Math.abs(velocityY) > MOVE_MIN) {^M
+                            Slog.i(TAG, "onFling() dialog dismiss!");^M
+                            dismiss();^M
+                            return true;^M
+                        }^M
+                        return false;^M
+                    }^M
+                });^M
+^M
+        mRootView = findViewById(R.id.pen_battery);^M
+        mRootView.setOnTouchListener((view, event) -> {^M
+            if (DEBUG) {^M
+                Slog.d(TAG, "onTouchEvent() event:" + event);^M
+            }^M
+            return mGestureDetector.onTouchEvent(event);^M
+        });^M
+^M
+        // mBatteryIcon = (ImageView) findViewById(R.id.battery_level_icon);^M
+        mBatteryLevelView = (TextView) findViewById(R.id.battery_level_text);^M
+        updatePenBatteryLevel();^M
+    }^M
+^M
+    public void setPenBatteryLevel(int level) {^M
+        mBatteryLevel = level;^M
+        updatePenBatteryLevel();^M
+    }^M
+^M
+    private void updatePenBatteryLevel() {^M
+        if (mBatteryLevelView != null) {^M
+            mBatteryLevelView.setText(mBatteryLevel + "%");^M
+        }^M
+    }^M
+^M
+    private void initDialogStyle() {^M
+        setCanceledOnTouchOutside(false);^M
+^M
+        Window window = getWindow();^M
+        // 设置弹窗背景透明^M
+        // window.setBackgroundDrawableResource(android.R.color.transparent);^M
+        window.setDimAmount(0.0f);^M
+        // 设置背景颜色为透明,主要是去掉四周圆角^M
+        window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));^M
+        // 设置点击dialog外面,dialog会消失^M
+        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);^M
+^M
+        WindowManager.LayoutParams lp = window.getAttributes();^M
+        // lp.format = PixelFormat.TRANSLUCENT;^M
+        // lp.windowAnimations = 0;^M
+        lp.dimAmount = 0.0f;^M
+        // 设置显示位置:屏幕顶部居中显示^M
+        lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;^M
+        // lp.x = 40;^M
+        lp.y = 130;^M
+        // 设置大小^M
+        // lp.width = 300;^M
+        // lp.height = 300;^M
+        window.setAttributes(lp);^M
+    }^M
+}^M
diff --git a/src/com/android/systemui/power/PowerUI.java b/src/com/android/systemui/power/PowerUI.java
index 710de41..6dad1f6 100755
--- a/src/com/android/systemui/power/PowerUI.java
+++ b/src/com/android/systemui/power/PowerUI.java
@@ -248,6 +248,25 @@ public class PowerUI extends SystemUI implements CommandQueue.Callbacks {
         public LowBatteryAutoShutDownDialog lowBatteryAutoShutDownDialog;
         private static final int BATTERY_LEVEL_NEED_SHUT_DOWN = 1 ;
 
+        // 主动笔电量广播
+        private static final String ACTION_PEN_BATTERY_NOTIFY = "com.android.server.policy.PEN_BATTERY_NOTIFY";
+        private static final String PEN_BATTERY_LEVEL = "pen_battery_level";
+
+        // 主动笔电量值范围
+        private static final int PEN_BATTERY_UNKNOWN = 255;
+        private static final int PEN_BATTERY_MIN = 0;
+        private static final int PEN_BATTERY_MAX = 100;
+
+        // 主动笔电量提示弹窗
+        private PenBatteryNotifyDialog mPenBatteryNotifyDialog;
+        private Runnable mPenBatteryDialogDissmisRunnable = () -> {
+            if (mPenBatteryNotifyDialog != null && mPenBatteryNotifyDialog.isShowing()) {
+                Log.i(TAG, "mPenBatteryNotifyDialog time out and dismiss!");
+                mPenBatteryNotifyDialog.dismiss();
+            }
+        };
+
+
         public void init() {
             // Register for Intent broadcasts for...
             IntentFilter filter = new IntentFilter();
@@ -261,6 +280,8 @@ public class PowerUI extends SystemUI implements CommandQueue.Callbacks {
              */
             filter.addAction(Intent.ACTION_SCREEN_ON);
             filter.addAction(Intent.ACTION_USER_SWITCHED);
+            // 主动笔电量广播
+            filter.addAction(ACTION_PEN_BATTERY_NOTIFY);
             mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler);
             // Force get initial values. Relying on Sticky behavior until API for getting info.
             if (!mHasReceivedBattery) {
@@ -397,11 +418,41 @@ public class PowerUI extends SystemUI implements CommandQueue.Callbacks {
 
             } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                 mWarnings.userSwitched();
+            } else if (ACTION_PEN_BATTERY_NOTIFY.equals(action)) {
+                // 收到广播显示主动笔电量提示弹窗
+                final int penBattery = intent.getIntExtra(PEN_BATTERY_LEVEL, PEN_BATTERY_UNKNOWN);
+                Log.i(TAG, "penBattery:" + penBattery);
+                if (PEN_BATTERY_MIN <= penBattery && penBattery <= PEN_BATTERY_MAX) {
+                    showPenBatteryNotifyDialog(context, penBattery);
+                }
             } else {
                 Slog.w(TAG, "unknown intent: " + intent);
             }
         }
 
+        /*
+         * 显示主动笔电量提示弹窗,如果弹窗正在显示则仅更新电量数值
+         *
+         * @param context Context
+         *
+         * @param penBatteryLevel 主动笔电量
+         */
+        private void showPenBatteryNotifyDialog(Context context, int penBatteryLevel) {
+            Log.i(TAG, "showPenBatteryNotifyDialog() penBatteryLevel:" + penBatteryLevel);
+            if (mPenBatteryNotifyDialog == null) {
+                mPenBatteryNotifyDialog = new PenBatteryNotifyDialog(context);
+                mPenBatteryNotifyDialog.setShowForAllUsers(true);
+            }
+            SystemUIDialog.setWindowOnTop(mPenBatteryNotifyDialog);
+            mPenBatteryNotifyDialog.setPenBatteryLevel(penBatteryLevel);
+            if (!mPenBatteryNotifyDialog.isShowing()) {
+                mPenBatteryNotifyDialog.show();
+            }
+            // 3s 后自动关闭
+            mHandler.removeCallbacks(mPenBatteryDialogDissmisRunnable);
+            mHandler.postDelayed(mPenBatteryDialogDissmisRunnable, 3000);
+        }
+
         /**
          * handle Battery Status Changed Situation.
          * @param context Context

Mark it...增量编译验证即可.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2265819.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用 gdbserver 调试 arm-linux 上的 AWTK 应用程序

很多嵌入式 linux 开发者都能熟练的使用 gdb/lldb 调试应用程序&#xff0c;但是还有不少朋友在调试开发板上的程序时&#xff0c;仍然在使用原始的 printf。本文介绍一下使用 gdbserver 通过网络调试开发板上的 AWTK 应用程序的方法&#xff0c;供有需要的朋友参考。 1. 下载 …

20241225在ubuntu22.04.5下使用smartmontools命令查看ssd的寿命

20241225在ubuntu22.04.5下使用smartmontools命令查看ssd的寿命 2024/12/25 15:10 rootrootrootroot-ThinkBook-16-G5-IRH:~$ sudo apt install smartmontools rootrootrootroot-ThinkBook-16-G5-IRH:~$ sudo fdisk -l Disk /dev/nvme0n1: 3.73 TiB, 4096805658624 bytes, 800…

ASP.NET |日常开发中定时任务详解

ASP.NET &#xff5c;日常开发中定时任务详解 前言一、定时任务的概念与用途1.1 定义1.2 应用场景 二、在ASP.NET中实现定时任务的方式2.1 使用System.Timers.Timer2.2 使用Quartz.NET 三、定时任务的部署与管理3.1 部署考虑因素3.2 管理与监控 结束语优质源码分享 ASP.NET &am…

整车厂如何规划构建汽车集成安全团队的软件研发能力

&#xff08;一&#xff09;、汽车集成安全团队职责 汽车集成安全团队肩负着保障汽车整体安全性的重任&#xff0c;从多个维度守护驾乘人员安全与车辆稳定运行&#xff0c;其主要职责如下&#xff1a; 功能安全管理 标准遵循与流程制定&#xff1a;严格依据ISO 26262等功能安…

使用 Python 创建多栏 Word 文档 – 详解

目录 引言 一、工具与安装 二、Python 在 Word 中创建简单的多栏布局 三、Python 在 Word 文档的栏间添加分隔线 四、Python 从Word文档的指定位置开启多栏设置 五、Python 为多栏 Word 文档的各栏添加页码 引言 在文档设计中&#xff0c;排版不仅决定了内容的呈现方式&…

使用强化学习与遗传算法优化3D低空物流路径_版本2

在快速发展的物流与自主系统领域&#xff0c;优化无人机在三维空间中的飞行路径至关重要。无论是在城市环境中导航还是在复杂地形中穿行&#xff0c;确保高效、安全且节能的航线规划能够显著提升运营效率。本文将深入探讨一种创新方法&#xff0c;结合强化学习&#xff08;Rein…

[手机Linux] 七,NextCloud优化设置

安装完成后在个人设置里发现很多警告&#xff0c;一一消除。 只能一条一条解决了。 关于您的设置有一些错误。 1&#xff0c;PHP 内存限制低于建议值 512 MB。 设置php配置文件&#xff1a; /usr/local/php/etc/php.ini 把里面的&#xff1a; memory_limit 128M 根据你自…

【设备 磁盘】重要备份存放U盘的风险 + winhex 磁盘清零(清理windows无法格式化的磁盘)

简述 清理用设备管理器和DiskGenious无法打开的磁盘 winhex安装 官网https://www.x-ways.net/winhex/下载&#xff0c;解压后以管理员身份运行 注意&#xff1a;非完全版不能像磁盘写入编辑后的数据 使用 解压后直接点击打开即可 打开磁盘 “全选”后&#xff0c;选择…

从LockSupport开始带来的思考

LockSupport是什么 LockSupport是JUC下的一个线程同步工具类&#xff0c;实现了线程的阻塞和唤醒操作。相比其他同步机制&#xff0c;如Synchronized、ReentrantLock等&#xff0c;LockSupport的性能更高、更灵活&#xff0c;同时也可以避免线程操作不当引起的死锁问题。Java中…

树莓集团:以产教融合助力人才培养

在当今快速发展的数字时代&#xff0c;人才是推动产业进步和创新的核心驱动力。树莓集团作为数字产业生态链建设者&#xff0c;深刻认识到人才培养的关键意义&#xff0c;积极探索并大力践行产教融合模式&#xff0c;为数字产业源源不断地输送高素质专业人才&#xff0c;在助力…

基于ISO 21434的汽车网络安全实践

商业领域的IT系统和嵌入式产品的IT系统正在融合为一种多功能系统。相应地&#xff0c;关注汽车网络安全的ISO 21434标准应运而生。该标准的意义在于提供了一个指南&#xff0c;可用于降低产品、项目和组织中存在的安全风险。为了有效实施ISO 21434标准&#xff0c;本文介绍了遵…

3.银河麒麟V10 离线安装Nginx

1. 下载nginx离线安装包 前往官网下载离线压缩包 2. 下载3个依赖 openssl依赖&#xff0c;前往 官网下载 pcre2依赖下载&#xff0c;前往Git下载 zlib依赖下载&#xff0c;前往Git下载 下载完成后完整的包如下&#xff1a; 如果网速下载不到请使用网盘下载 通过网盘分享的文件…

视频监控平台:Liveweb视频汇聚融合平台智慧安防视频监控应用方案

Liveweb是一款功能强大、灵活部署的安防视频监控平台&#xff0c;支持多种主流标准协议&#xff0c;包括GB28181、RTSP/Onvif、RTMP等&#xff0c;同时兼容海康Ehome、海大宇等厂家的私有协议和SDK接入。该平台不仅提供传统安防监控功能&#xff0c;还支持接入AI智能分析&#…

【视觉惯性SLAM:对极几何】

对极几何&#xff08;Epipolar Geometry&#xff09;介绍 对极几何是立体视觉中的核心内容之一&#xff0c;它描述了两个相机在观察同一个三维场景时&#xff0c;成像平面之间的几何关系。对极几何能够约束图像中对应点的位置关系&#xff0c;是双目立体匹配、三维重建、以及位…

Scala_【2】变量和数据类型

第二章 注释标识符的命名规范命名规则关键字 变量字符串输出数据类型关系变量和数据类型整数类型&#xff08;Byte、Short、Int、Long&#xff09;浮点类型&#xff08;Float、Double&#xff09;字符类型&#xff08;Char&#xff09;布尔类型&#xff08;Boolean&#xff09;…

华为实训课笔记 2024 1223-1224

华为实训 12/2312/24 12/23 [Huawei]stp enable --开启STP display stp brief --查询STP MSTID Port Role STP State Protection 实例ID 端口 端口角色 端口状态 是否开启保护[Huawei]display stp vlan xxxx --查询制定vlan的生成树计算结…

HarmonyOS NEXT 实战之元服务:静态案例效果--航空出行

背景&#xff1a; 前几篇学习了元服务&#xff0c;后面几期就让我们开发简单的元服务吧&#xff0c;里面丰富的内容大家自己加&#xff0c;本期案例 仅供参考 先上本期效果图 &#xff0c;里面图片自行替换 效果图1完整代码案例如下&#xff1a; import { authentication } …

WebRTC搭建与应用(五)-Coturn踩坑记

WebRTC搭建与应用(五)-Coturn踩坑记 近期由于项目需要在研究前端WebGL渲染转为云渲染&#xff0c;借此机会对WebRTC等有了初步了解&#xff0c;在此记录一下&#xff0c;以防遗忘。 第五章 WebRTC搭建与应用(五)-Coturn踩坑记 文章目录 WebRTC搭建与应用(五)-Coturn踩坑记前…

STM32-笔记14-排队控制系统

一、项目需求 1. 红外传感器检测有人通过并计数&#xff1b; 2. 计数值显示在LCD1602 3. 允许通过时&#xff0c;LED1闪烁&#xff0c;蜂鸣器不响&#xff0c;继电器不闭合&#xff1b; 4. 不允许通过时&#xff0c;LED2闪烁&#xff0c;蜂鸣器响&#xff0c;继电器闭合&#…