#include "ui/android/view_android.h"
#include "base/android/jni_android.h"
#include "base/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/android/event_forwarder.h"
#include "ui/android/test_view_android_delegate.h"
#include "ui/android/view_android.h"
#include "ui/android/view_android_observer.h"
#include "ui/android/window_android.h"
#include "ui/events/android/event_handler_android.h"
#include "ui/events/android/motion_event_android_factory.h"
#include "ui/events/android/motion_event_android_java.h"
#include "ui/events/motionevent_jni_headers/MotionEvent_jni.h"
#include "ui/events/test/scoped_event_test_tick_clock.h"
namespace ui {
using base::android::JavaParamRef;
class TestViewAndroid : public ViewAndroid {
public:
TestViewAndroid(ViewAndroid::LayoutType layout_type)
: ViewAndroid(layout_type) {}
float GetDipScale() override { return 1.f; }
};
class TestEventHandler : public EventHandlerAndroid {
public:
TestEventHandler() {}
bool OnTouchEvent(const MotionEventAndroid& event) override {
touch_called_ = true;
return handle_event_;
}
void OnSizeChanged() override { onsize_called_ = true; }
void SetHandleEvent(bool handle_event) { handle_event_ = handle_event; }
bool TouchEventHandled() { return touch_called_ && handle_event_; }
bool TouchEventCalled() { return touch_called_; }
bool OnSizeCalled() { return onsize_called_; }
void Reset() {
touch_called_ = false;
onsize_called_ = false;
}
private:
bool handle_event_{true};
bool touch_called_{false};
bool onsize_called_{false};
};
class ViewAndroidBoundsTest : public testing::Test {
public:
ViewAndroidBoundsTest()
: root_(ViewAndroid::LayoutType::kMatchParent),
view1_(ViewAndroid::LayoutType::kNormal),
view2_(ViewAndroid::LayoutType::kNormal),
view3_(ViewAndroid::LayoutType::kNormal),
viewm_(ViewAndroid::LayoutType::kMatchParent) {
root_.GetEventForwarder();
view1_.set_event_handler(&handler1_);
view2_.set_event_handler(&handler2_);
view3_.set_event_handler(&handler3_);
viewm_.set_event_handler(&handlerm_);
}
void Reset() {
handler1_.Reset();
handler2_.Reset();
handler3_.Reset();
handlerm_.Reset();
test_clock_.SetNowTicks(base::TimeTicks());
}
void GenerateTouchEventAt(float x, float y) {
ui::MotionEventAndroid::Pointer pointer0(0, x, y, 0, 0, 0, 0, 0, 0);
JNIEnv* env = base::android::AttachCurrentThread();
base::android::ScopedJavaLocalRef<jobject> obj =
JNI_MotionEvent::Java_MotionEvent_obtain(
env, 0, 0, 0, 0,
0, 0);
auto event = ui::MotionEventAndroidFactory::CreateFromJava(
env, obj,
1.f,
0,
0,
0,
base::TimeTicks(),
0,
1,
0,
0,
0,
0,
0,
0,
0,
false, &pointer0,
nullptr);
root_.OnTouchEvent(*event);
}
void ExpectHit(const TestEventHandler& hitHandler) {
TestEventHandler* handlers[4] = {&handler1_, &handler2_, &handler3_,
&handlerm_};
for (auto* handler : handlers) {
if (&hitHandler == handler)
EXPECT_TRUE(handler->TouchEventHandled());
else
EXPECT_FALSE(handler->TouchEventHandled());
}
Reset();
}
TestViewAndroid root_;
TestViewAndroid view1_;
TestViewAndroid view2_;
TestViewAndroid view3_;
TestViewAndroid viewm_;
TestEventHandler handler1_;
TestEventHandler handler2_;
TestEventHandler handler3_;
TestEventHandler handlerm_;
ui::test::ScopedEventTestTickClock test_clock_;
};
TEST_F(ViewAndroidBoundsTest, MatchesViewInFront) {
view1_.SetLayoutForTesting(50, 50, 400, 600);
view2_.SetLayoutForTesting(50, 50, 400, 600);
root_.AddChild(&view2_);
root_.AddChild(&view1_);
GenerateTouchEventAt(100.f, 100.f);
ExpectHit(handler1_);
root_.MoveToFront(&view2_);
GenerateTouchEventAt(100.f, 100.f);
ExpectHit(handler2_);
root_.MoveToBack(&view2_);
GenerateTouchEventAt(100.f, 100.f);
ExpectHit(handler1_);
}
TEST_F(ViewAndroidBoundsTest, MatchesViewArea) {
view1_.SetLayoutForTesting(50, 50, 200, 200);
view2_.SetLayoutForTesting(20, 20, 400, 600);
root_.AddChild(&view2_);
root_.AddChild(&view1_);
GenerateTouchEventAt(100.f, 100.f);
ExpectHit(handler1_);
GenerateTouchEventAt(300.f, 400.f);
ExpectHit(handler2_);
}
TEST_F(ViewAndroidBoundsTest, MatchesViewAfterMove) {
view1_.SetLayoutForTesting(50, 50, 200, 200);
view2_.SetLayoutForTesting(20, 20, 400, 600);
root_.AddChild(&view2_);
root_.AddChild(&view1_);
GenerateTouchEventAt(100.f, 100.f);
ExpectHit(handler1_);
view1_.SetLayoutForTesting(150, 150, 200, 200);
GenerateTouchEventAt(100.f, 100.f);
ExpectHit(handler2_);
}
TEST_F(ViewAndroidBoundsTest, MatchesViewSizeOfkMatchParent) {
view1_.SetLayoutForTesting(20, 20, 400, 600);
view2_.SetLayoutForTesting(50, 50, 200, 200);
root_.AddChild(&view1_);
root_.AddChild(&view2_);
view1_.AddChild(&viewm_);
GenerateTouchEventAt(100.f, 100.f);
ExpectHit(handler2_);
GenerateTouchEventAt(300.f, 400.f);
ExpectHit(handler1_);
handler1_.SetHandleEvent(false);
GenerateTouchEventAt(300.f, 400.f);
EXPECT_TRUE(handler1_.TouchEventCalled());
ExpectHit(handlerm_);
}
TEST_F(ViewAndroidBoundsTest, MatchesViewsWithOffset) {
view1_.SetLayoutForTesting(10, 20, 150, 100);
view2_.SetLayoutForTesting(20, 30, 40, 30);
view3_.SetLayoutForTesting(70, 30, 40, 30);
root_.AddChild(&view1_);
view1_.AddChild(&view2_);
view1_.AddChild(&view3_);
GenerateTouchEventAt(70, 30);
ExpectHit(handler1_);
handler1_.SetHandleEvent(false);
GenerateTouchEventAt(40, 60);
EXPECT_TRUE(handler1_.TouchEventCalled());
ExpectHit(handler2_);
GenerateTouchEventAt(100, 70);
EXPECT_TRUE(handler1_.TouchEventCalled());
ExpectHit(handler3_);
}
TEST_F(ViewAndroidBoundsTest, OnSizeChanged) {
root_.AddChild(&view1_);
view1_.AddChild(&viewm_);
view1_.AddChild(&view3_);
view1_.OnSizeChanged(100, 100);
EXPECT_TRUE(handler1_.OnSizeCalled());
EXPECT_TRUE(handlerm_.OnSizeCalled());
EXPECT_FALSE(handler3_.OnSizeCalled());
Reset();
EXPECT_DCHECK_DEATH(viewm_.OnSizeChanged(100, 200));
EXPECT_FALSE(handlerm_.OnSizeCalled());
EXPECT_FALSE(handler3_.OnSizeCalled());
viewm_.RemoveFromParent();
viewm_.OnSizeChangedInternal(gfx::Size(0, 0));
Reset();
view1_.OnSizeChanged(100, 100);
EXPECT_FALSE(handlerm_.OnSizeCalled());
view1_.AddChild(&viewm_);
EXPECT_TRUE(handlerm_.OnSizeCalled());
EXPECT_FALSE(handler3_.OnSizeCalled());
viewm_.RemoveFromParent();
Reset();
view1_.OnSizeChanged(100, 100);
view1_.AddChild(&viewm_);
EXPECT_FALSE(handlerm_.OnSizeCalled());
EXPECT_FALSE(handler3_.OnSizeCalled());
}
TEST(ViewAndroidTest, ChecksMultipleEventForwarders) {
ViewAndroid parent;
ViewAndroid child;
parent.GetEventForwarder();
child.GetEventForwarder();
EXPECT_DCHECK_DEATH(parent.AddChild(&child));
ViewAndroid parent2;
ViewAndroid child2;
parent2.GetEventForwarder();
parent2.AddChild(&child2);
EXPECT_DCHECK_DEATH(child2.GetEventForwarder());
ViewAndroid window;
ViewAndroid wcv1, wcv2;
ViewAndroid rwhv1a, rwhv1b, rwhv2;
wcv1.GetEventForwarder();
wcv2.GetEventForwarder();
window.AddChild(&wcv1);
wcv1.AddChild(&rwhv1a);
wcv1.AddChild(&rwhv1b);
wcv2.AddChild(&rwhv2);
window.AddChild(&wcv2);
EXPECT_DCHECK_DEATH(rwhv2.GetEventForwarder());
}
class Observer : public ViewAndroidObserver {
public:
Observer() = default;
void OnAttachedToWindow() override { attached_ = true; }
void OnDetachedFromWindow() override { attached_ = false; }
void OnViewAndroidDestroyed() override { destroyed_ = true; }
void OnDelegateSet() override { delegate_set_ = true; }
bool attached_ = false;
bool destroyed_ = false;
bool delegate_set_ = false;
};
TEST(ViewAndroidTest, Observer) {
std::unique_ptr<ui::WindowAndroid::ScopedWindowAndroidForTesting> window =
ui::WindowAndroid::CreateForTesting();
Observer top_observer;
Observer bottom_observer;
Observer top_observer2;
{
ViewAndroid top;
ViewAndroid bottom;
top.AddObserver(&top_observer);
bottom.AddObserver(&bottom_observer);
top.AddChild(&bottom);
EXPECT_FALSE(top_observer.attached_);
EXPECT_FALSE(bottom_observer.attached_);
window->get()->AddChild(&top);
EXPECT_TRUE(top_observer.attached_);
EXPECT_TRUE(bottom_observer.attached_);
top.AddObserver(&top_observer2);
EXPECT_FALSE(top_observer2.attached_);
bottom.RemoveFromParent();
EXPECT_FALSE(bottom_observer.attached_);
top.RemoveFromParent();
EXPECT_FALSE(top_observer.attached_);
window->get()->AddChild(&top);
EXPECT_TRUE(top_observer.attached_);
top.AddChild(&bottom);
EXPECT_TRUE(bottom_observer.attached_);
TestViewAndroidDelegate view_android_delegate;
EXPECT_FALSE(top_observer.delegate_set_);
view_android_delegate.SetupTestDelegate(&top);
EXPECT_TRUE(top_observer.delegate_set_);
top.RemoveFromParent();
EXPECT_FALSE(top_observer.attached_);
EXPECT_FALSE(bottom_observer.attached_);
top.RemoveObserver(&top_observer2);
}
EXPECT_TRUE(top_observer.destroyed_);
EXPECT_FALSE(top_observer2.destroyed_);
EXPECT_TRUE(bottom_observer.destroyed_);
}
TEST(ViewAndroidTest, WindowAndroidDestructionDetachesAllViewAndroid) {
std::unique_ptr<ui::WindowAndroid::ScopedWindowAndroidForTesting> window =
ui::WindowAndroid::CreateForTesting();
ViewAndroid top;
ViewAndroid bottom;
Observer top_observer;
Observer bottom_observer;
top.AddObserver(&top_observer);
bottom.AddObserver(&bottom_observer);
window->get()->AddChild(&top);
top.AddChild(&bottom);
EXPECT_TRUE(top_observer.attached_);
EXPECT_TRUE(bottom_observer.attached_);
window.reset();
EXPECT_FALSE(top_observer.attached_);
EXPECT_FALSE(bottom_observer.attached_);
top.RemoveObserver(&top_observer);
bottom.RemoveObserver(&bottom_observer);
}
}