Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6b662769 authored by Yeabkal Wubshit's avatar Yeabkal Wubshit
Browse files

Create Input SlopController

Create a basic input slop controller, and use it for rotary inputs.

Bug: 285957835
Test: atest SlopControllerTest

Change-Id: I0cabfed1f064b849f8151f069e1241292cc374cf
parent f3653153
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ filegroup {
        "mapper/RotaryEncoderInputMapper.cpp",
        "mapper/SensorInputMapper.cpp",
        "mapper/SingleTouchInputMapper.cpp",
        "mapper/SlopController.cpp",
        "mapper/SwitchInputMapper.cpp",
        "mapper/TouchCursorInputMapperCommon.cpp",
        "mapper/TouchInputMapper.cpp",
+12 −0
Original line number Diff line number Diff line
@@ -30,6 +30,14 @@ RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceCon
                                                   const InputReaderConfiguration& readerConfig)
      : InputMapper(deviceContext, readerConfig), mOrientation(ui::ROTATION_0) {
    mSource = AINPUT_SOURCE_ROTARY_ENCODER;

    const PropertyMap& config = getDeviceContext().getConfiguration();
    float slopThreshold = config.getInt("rotary_encoder.slop_threshold").value_or(0);
    int32_t slopDurationMs = config.getInt("rotary_encoder.slop_duration_ms").value_or(0);
    if (slopThreshold > 0 && slopDurationMs > 0) {
        mSlopController = std::make_unique<SlopController>(slopThreshold,
                                                           (nsecs_t)(slopDurationMs * 1000000));
    }
}

RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {}
@@ -103,6 +111,10 @@ std::list<NotifyArgs> RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readT
    std::list<NotifyArgs> out;

    float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel();
    if (mSlopController) {
        scroll = mSlopController->consumeEvent(when, scroll);
    }

    bool scrolled = scroll != 0;

    // Send motion event.
+2 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@

#include "CursorScrollAccumulator.h"
#include "InputMapper.h"
#include "SlopController.h"

namespace android {

@@ -46,6 +47,7 @@ private:
    int32_t mSource;
    float mScalingFactor;
    ui::Rotation mOrientation;
    std::unique_ptr<SlopController> mSlopController = nullptr;

    explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
                                      const InputReaderConfiguration& readerConfig);
+81 −0
Original line number Diff line number Diff line
/*
 * Copyright 2023 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.
 */

// clang-format off
#include "../Macros.h"
// clang-format on

#include "SlopController.h"

namespace {
int signOf(float value) {
    if (value == 0) return 0;
    if (value > 0) return 1;
    return -1;
}
} // namespace

namespace android {

SlopController::SlopController(float slopThreshold, nsecs_t slopDurationNanos)
      : mSlopThreshold(slopThreshold), mSlopDurationNanos(slopDurationNanos) {}

SlopController::~SlopController() {}

float SlopController::consumeEvent(nsecs_t eventTimeNanos, float value) {
    if (mSlopDurationNanos == 0) {
        return value;
    }

    if (shouldResetSlopTracking(eventTimeNanos, value)) {
        mCumulativeValue = 0;
        mHasSlopBeenMet = false;
    }

    mLastEventTimeNanos = eventTimeNanos;

    if (mHasSlopBeenMet) {
        // Since slop has already been met, we know that all of the current value would pass the
        // slop threshold. So return that, without any further processing.
        return value;
    }

    mCumulativeValue += value;

    if (abs(mCumulativeValue) >= mSlopThreshold) {
        mHasSlopBeenMet = true;
        // Return the amount of value that exceeds the slop.
        return signOf(value) * (abs(mCumulativeValue) - mSlopThreshold);
    }

    return 0;
}

bool SlopController::shouldResetSlopTracking(nsecs_t eventTimeNanos, float value) {
    const nsecs_t ageNanos = eventTimeNanos - mLastEventTimeNanos;
    if (ageNanos >= mSlopDurationNanos) {
        return true;
    }
    if (value == 0) {
        return false;
    }
    if (signOf(mCumulativeValue) != signOf(value)) {
        return true;
    }
    return false;
}

} // namespace android
+55 −0
Original line number Diff line number Diff line
/*
 * Copyright 2023 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.
 */

#pragma once

#include <utils/Timers.h>

namespace android {

/**
 * Controls a slop logic. Slop here refers to an approach to try and drop insignificant input
 * events. This is helpful in cases where unintentional input events may cause unintended outcomes,
 * like scrolling a screen or keeping the screen awake.
 *
 * Current slop logic:
 *      "If time since last event > Xns, then discard the next N values."
 */
class SlopController {
public:
    SlopController(float slopThreshold, nsecs_t slopDurationNanos);
    virtual ~SlopController();

    /**
     * Consumes an event with a given time and value for slop processing.
     * Returns an amount <=value that should be consumed.
     */
    float consumeEvent(nsecs_t eventTime, float value);

private:
    bool shouldResetSlopTracking(nsecs_t eventTimeNanos, float value);

    /** The amount of event values ignored after an inactivity of the slop duration. */
    const float mSlopThreshold;
    /** The duration of inactivity that resets slop controlling. */
    const nsecs_t mSlopDurationNanos;

    nsecs_t mLastEventTimeNanos = 0;
    float mCumulativeValue = 0;
    bool mHasSlopBeenMet = false;
};

} // namespace android
Loading