18a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev/* 28a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev * Copyright (C) 2016 The Android Open Source Project 38a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev * 48a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev * Licensed under the Apache License, Version 2.0 (the "License"); 58a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev * you may not use this file except in compliance with the License. 68a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev * You may obtain a copy of the License at 78a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev * 88a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev * http://www.apache.org/licenses/LICENSE-2.0 98a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev * 108a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev * Unless required by applicable law or agreed to in writing, software 118a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev * distributed under the License is distributed on an "AS IS" BASIS, 128a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev * See the License for the specific language governing permissions and 148a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev * limitations under the License. 158a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev */ 168a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.core.view; 188a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 198a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport static org.mockito.Matchers.any; 208a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport static org.mockito.Matchers.argThat; 218a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport static org.mockito.Matchers.eq; 228a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport static org.mockito.Mockito.inOrder; 238a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport static org.mockito.Mockito.mock; 248a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport static org.mockito.Mockito.times; 258a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport static org.mockito.Mockito.verify; 268a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport static org.mockito.Mockito.verifyNoMoreInteractions; 278a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport static org.mockito.Mockito.when; 288a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 298a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport android.app.Instrumentation; 308a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport android.graphics.Point; 318a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport android.os.SystemClock; 328a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport android.support.test.InstrumentationRegistry; 3335232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikasimport android.support.test.filters.LargeTest; 348a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport android.support.test.filters.SmallTest; 358a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport android.support.test.rule.ActivityTestRule; 368a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport android.support.test.runner.AndroidJUnit4; 378a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport android.view.InputDevice; 388a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport android.view.MotionEvent; 398a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport android.view.View; 408a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport android.view.ViewConfiguration; 418a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 429dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.annotation.NonNull; 439dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.test.R; 449dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikas 458a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport org.junit.Before; 468a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport org.junit.Rule; 478a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport org.junit.Test; 488a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport org.junit.runner.RunWith; 498a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport org.mockito.ArgumentMatcher; 508a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevimport org.mockito.InOrder; 518a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 528a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev@RunWith(AndroidJUnit4.class) 538a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheevpublic class DragStartHelperTest { 548a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 558a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev @Rule 568a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev public ActivityTestRule<DragStartHelperTestActivity> mActivityRule = 578a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev new ActivityTestRule<>(DragStartHelperTestActivity.class); 588a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 598a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev private Instrumentation mInstrumentation; 608a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev private View mDragSource; 618a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 628a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev interface DragStartListener { 638a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev boolean onDragStart(View view, DragStartHelper helper, Point touchPosition); 648a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 658a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 668a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev @NonNull 678a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev private DragStartListener createListener(boolean returnValue) { 688a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final DragStartListener listener = mock(DragStartListener.class); 698a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev when(listener.onDragStart(any(View.class), any(DragStartHelper.class), any(Point.class))) 708a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev .thenReturn(returnValue); 718a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev return listener; 728a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 738a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 748a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev @NonNull 758a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev private DragStartHelper createDragStartHelper(final DragStartListener listener) { 768a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev return new DragStartHelper(mDragSource, new DragStartHelper.OnDragStartListener() { 778a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev @Override 788a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev public boolean onDragStart(View v, DragStartHelper helper) { 798a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev Point touchPosition = new Point(); 808a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev helper.getTouchPosition(touchPosition); 818a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev return listener.onDragStart(v, helper, touchPosition); 828a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 838a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev }); 848a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 858a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 868a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev private static int[] getViewCenter(View view) { 878a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final int[] xy = new int[2]; 888a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev view.getLocationOnScreen(xy); 898a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev xy[0] += view.getWidth() / 2; 908a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev xy[1] += view.getHeight() / 2; 918a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev return xy; 928a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 938a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 948a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev private static MotionEvent obtainTouchEvent( 958a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev int action, View anchor, int offsetX, int offsetY) { 968a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final long eventTime = SystemClock.uptimeMillis(); 978a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final int[] xy = getViewCenter(anchor); 988a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev return MotionEvent.obtain( 998a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev eventTime, eventTime, action, xy[0] + offsetX, xy[1] + offsetY, 0); 1008a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 1018a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 1028a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev private void sendTouchEvent(int action, View anchor, int offsetX, int offsetY) { 1038a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mInstrumentation.sendPointerSync(obtainTouchEvent(action, anchor, offsetX, offsetY)); 1048a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 1058a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 1068a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev private static MotionEvent obtainMouseEvent( 1078a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev int action, int buttonState, View anchor, int offsetX, int offsetY) { 1088a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final long eventTime = SystemClock.uptimeMillis(); 1098a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 1108a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final int[] xy = getViewCenter(anchor); 1118a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 1128a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev MotionEvent.PointerProperties[] props = new MotionEvent.PointerProperties[] { 1138a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev new MotionEvent.PointerProperties() 1148a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev }; 1158a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev props[0].id = 0; 1168a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev props[0].toolType = MotionEvent.TOOL_TYPE_MOUSE; 1178a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 1188a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[] { 1198a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev new MotionEvent.PointerCoords() 1208a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev }; 1218a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev coords[0].x = xy[0] + offsetX; 1228a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev coords[0].y = xy[1] + offsetY; 1238a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 1248a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev return MotionEvent.obtain(eventTime, eventTime, action, 1, props, coords, 0, 1258a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev buttonState, 0, 0, -1, 0, InputDevice.SOURCE_MOUSE, 0); 1268a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 1278a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 1288a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev private void sendMouseEvent( 1298a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev int action, int buttonState, View anchor, int offsetX, int offsetY) { 1308a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mInstrumentation.sendPointerSync(obtainMouseEvent( 1318a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev action, buttonState, anchor, offsetX, offsetY)); 1328a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 1338a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 134963facb8fce35022f296c38fadafd9a959ab1655Aurimas Liutikas static class TouchPositionMatcher implements ArgumentMatcher<Point> { 1358a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 1368a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev private final Point mExpectedPosition; 1378a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 1388a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev TouchPositionMatcher(int x, int y) { 1398a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mExpectedPosition = new Point(x, y); 1408a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 1418a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 1428a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev TouchPositionMatcher(View anchor, int x, int y) { 1438a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev this(anchor.getWidth() / 2 + x, anchor.getHeight() / 2 + y); 1448a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 1458a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 146e2104f4b5c8e3ad63570306a25e61502dfe4c418Aurimas Liutikas @Override 147963facb8fce35022f296c38fadafd9a959ab1655Aurimas Liutikas public boolean matches(Point actual) { 1488a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev return mExpectedPosition.equals(actual); 1498a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 1508a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 151e2104f4b5c8e3ad63570306a25e61502dfe4c418Aurimas Liutikas @Override 152963facb8fce35022f296c38fadafd9a959ab1655Aurimas Liutikas public String toString() { 153963facb8fce35022f296c38fadafd9a959ab1655Aurimas Liutikas return "TouchPositionMatcher: " + mExpectedPosition; 1548a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 1558a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 1568a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 1578a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev private void waitForLongPress() { 1588a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev SystemClock.sleep(ViewConfiguration.getLongPressTimeout() * 2); 1598a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 1608a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 1618a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev @Before 1628a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev public void setUp() { 1638a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mInstrumentation = InstrumentationRegistry.getInstrumentation(); 1648a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mDragSource = mActivityRule.getActivity().findViewById(R.id.drag_source); 1658a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 1668a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 16735232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikas @SmallTest 1688a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev @Test 16971e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev public void mouseClick() throws Throwable { 17071e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev final DragStartListener listener = createListener(true); 17171e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev final DragStartHelper helper = createDragStartHelper(listener); 17271e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev helper.attach(); 17371e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev 17471e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_DOWN, MotionEvent.BUTTON_PRIMARY, mDragSource, 0, 0); 17571e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_UP, MotionEvent.BUTTON_PRIMARY, mDragSource, 0, 0); 17671e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev 17771e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev // A simple mouse click does not trigger OnDragStart. 17871e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev verifyNoMoreInteractions(listener); 17971e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev } 18071e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev 18135232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikas @SmallTest 18271e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev @Test 18371e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev public void mousePressWithSecondaryButton() throws Throwable { 18471e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev final DragStartListener listener = createListener(true); 18571e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev final DragStartHelper helper = createDragStartHelper(listener); 18671e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev helper.attach(); 18771e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev 18871e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_DOWN, MotionEvent.BUTTON_PRIMARY, mDragSource, 0, 0); 18971e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_MOVE, 19071e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev MotionEvent.BUTTON_PRIMARY | MotionEvent.BUTTON_SECONDARY, mDragSource, 0, 0); 19171e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_MOVE, MotionEvent.BUTTON_PRIMARY, mDragSource, 0, 0); 19271e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev 19371e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev // ACTION_MOVE with the same position does not trigger OnDragStart. 19471e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev verifyNoMoreInteractions(listener); 19571e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev } 19671e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev 19735232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikas @SmallTest 19871e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev @Test 1998a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev public void mouseDrag() throws Throwable { 2008a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final DragStartListener listener = createListener(true); 2018a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final DragStartHelper helper = createDragStartHelper(listener); 2028a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev helper.attach(); 2038a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 2048a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_DOWN, MotionEvent.BUTTON_PRIMARY, mDragSource, 0, 0); 2058a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_MOVE, MotionEvent.BUTTON_PRIMARY, mDragSource, 1, 2); 2068a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_MOVE, MotionEvent.BUTTON_PRIMARY, mDragSource, 3, 4); 2078a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_MOVE, MotionEvent.BUTTON_PRIMARY, mDragSource, 5, 6); 2088a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 2098a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev // Returning true from the callback prevents further callbacks. 2108a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev verify(listener, times(1)).onDragStart( 2118a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev eq(mDragSource), eq(helper), argThat(new TouchPositionMatcher(mDragSource, 1, 2))); 2128a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev verifyNoMoreInteractions(listener); 2138a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 2148a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 21535232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikas @SmallTest 2168a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev @Test 2178a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev public void mouseDragWithNonprimaryButton() throws Throwable { 2188a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final DragStartListener listener = createListener(true); 2198a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final DragStartHelper helper = createDragStartHelper(listener); 2208a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev helper.attach(); 2218a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 2228a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_DOWN, MotionEvent.BUTTON_SECONDARY, mDragSource, 0, 0); 2238a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_MOVE, MotionEvent.BUTTON_SECONDARY, mDragSource, 1, 2); 2248a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_UP, MotionEvent.BUTTON_SECONDARY, mDragSource, 3, 4); 2258a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 2268a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_DOWN, MotionEvent.BUTTON_TERTIARY, mDragSource, 0, 0); 2278a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_MOVE, MotionEvent.BUTTON_TERTIARY, mDragSource, 1, 2); 2288a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_UP, MotionEvent.BUTTON_TERTIARY, mDragSource, 3, 4); 2298a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 23071e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev // Dragging mouse with a non-primary button down does not trigger OnDragStart. 2318a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev verifyNoMoreInteractions(listener); 2328a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 2338a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 23435232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikas @SmallTest 2358a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev @Test 2368a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev public void mouseDragUsingTouchListener() throws Throwable { 2378a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final DragStartListener listener = createListener(true); 2388a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final DragStartHelper helper = createDragStartHelper(listener); 2398a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 2408a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mDragSource.setOnTouchListener(new View.OnTouchListener() { 2418a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev @Override 2428a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev public boolean onTouch(View view, MotionEvent motionEvent) { 2438a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev helper.onTouch(view, motionEvent); 2448a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev return true; 2458a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 2468a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev }); 2478a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 2488a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_DOWN, MotionEvent.BUTTON_PRIMARY, mDragSource, 0, 0); 2498a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_MOVE, MotionEvent.BUTTON_PRIMARY, mDragSource, 1, 2); 2508a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_MOVE, MotionEvent.BUTTON_PRIMARY, mDragSource, 3, 4); 2518a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_MOVE, MotionEvent.BUTTON_PRIMARY, mDragSource, 5, 6); 2528a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 2538a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev // Returning true from the callback prevents further callbacks. 2548a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev verify(listener, times(1)).onDragStart( 2558a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev eq(mDragSource), eq(helper), argThat(new TouchPositionMatcher(mDragSource, 1, 2))); 2568a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev verifyNoMoreInteractions(listener); 2578a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 2588a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 25935232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikas @SmallTest 2608a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev @Test 2618a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev public void mouseDragWhenListenerReturnsFalse() throws Throwable { 2628a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final DragStartListener listener = createListener(false); 2638a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final DragStartHelper helper = createDragStartHelper(listener); 2648a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev helper.attach(); 2658a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 2668a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_DOWN, MotionEvent.BUTTON_PRIMARY, mDragSource, 0, 0); 2678a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_MOVE, MotionEvent.BUTTON_PRIMARY, mDragSource, 1, 2); 2688a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_MOVE, MotionEvent.BUTTON_PRIMARY, mDragSource, 3, 4); 2698a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_MOVE, MotionEvent.BUTTON_PRIMARY, mDragSource, 5, 6); 2708a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 2718a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev // When the listener returns false every ACTION_MOVE triggers OnDragStart. 2728a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev InOrder inOrder = inOrder(listener); 2738a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev inOrder.verify(listener, times(1)).onDragStart( 2748a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev eq(mDragSource), eq(helper), argThat(new TouchPositionMatcher(mDragSource, 1, 2))); 2758a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev inOrder.verify(listener, times(1)).onDragStart( 2768a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev eq(mDragSource), eq(helper), argThat(new TouchPositionMatcher(mDragSource, 3, 4))); 2778a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev inOrder.verify(listener, times(1)).onDragStart( 2788a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev eq(mDragSource), eq(helper), argThat(new TouchPositionMatcher(mDragSource, 5, 6))); 2798a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev inOrder.verifyNoMoreInteractions(); 2808a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 2818a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 28235232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikas @LargeTest 2838a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev @Test 2848a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev public void mouseLongPress() throws Throwable { 2858a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final DragStartListener listener = createListener(true); 2868a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final DragStartHelper helper = createDragStartHelper(listener); 2878a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev helper.attach(); 2888a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 2898a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendMouseEvent(MotionEvent.ACTION_DOWN, MotionEvent.BUTTON_PRIMARY, mDragSource, 1, 2); 2908a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev waitForLongPress(); 2918a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 2928a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev // Long press triggers OnDragStart. 2938a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev verify(listener, times(1)).onDragStart( 2948a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev eq(mDragSource), eq(helper), argThat(new TouchPositionMatcher(mDragSource, 1, 2))); 2958a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev verifyNoMoreInteractions(listener); 2968a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 2978a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 29835232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikas @SmallTest 2998a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev @Test 3008a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev public void touchDrag() throws Throwable { 3018a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final DragStartListener listener = createListener(false); 3028a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final DragStartHelper helper = createDragStartHelper(listener); 3038a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev helper.attach(); 3048a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 3058a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendTouchEvent(MotionEvent.ACTION_DOWN, mDragSource, 0, 0); 3068a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendTouchEvent(MotionEvent.ACTION_MOVE, mDragSource, 1, 2); 3078a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendTouchEvent(MotionEvent.ACTION_MOVE, mDragSource, 3, 4); 3088a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendTouchEvent(MotionEvent.ACTION_MOVE, mDragSource, 5, 6); 3098a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 3108a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev // Touch and drag (without delay) does not trigger OnDragStart. 3118a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev verifyNoMoreInteractions(listener); 3128a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 3138a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 31435232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikas @SmallTest 3158a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev @Test 31671e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev public void touchTap() throws Throwable { 31771e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev final DragStartListener listener = createListener(false); 31871e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev final DragStartHelper helper = createDragStartHelper(listener); 31971e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev helper.attach(); 32071e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev 32171e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev sendTouchEvent(MotionEvent.ACTION_DOWN, mDragSource, 0, 0); 32271e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev sendTouchEvent(MotionEvent.ACTION_UP, mDragSource, 0, 0); 32371e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev 32471e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev // A simple tap does not trigger OnDragStart. 32571e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev verifyNoMoreInteractions(listener); 32671e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev } 32771e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev 32835232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikas @LargeTest 32971e918dd8d07e3338240e9a4ca0910814e19c47dVladislav Kaznacheev @Test 3308a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev public void touchLongPress() throws Throwable { 3318a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final DragStartListener listener = createListener(true); 3328a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final DragStartHelper helper = createDragStartHelper(listener); 3338a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev helper.attach(); 3348a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 3358a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendTouchEvent(MotionEvent.ACTION_DOWN, mDragSource, 1, 2); 3368a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev waitForLongPress(); 3378a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 3388a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev // Long press triggers OnDragStart. 3398a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev verify(listener, times(1)).onDragStart( 3408a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev eq(mDragSource), eq(helper), argThat(new TouchPositionMatcher(mDragSource, 1, 2))); 3418a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev verifyNoMoreInteractions(listener); 3428a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 3438a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 34435232c6eaeb9b99f390cb8ef0ac83bf45fa0b3faAurimas Liutikas @LargeTest 3458a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev @Test 3468a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev public void touchLongPressUsingLongClickListener() throws Throwable { 3478a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final DragStartListener listener = createListener(true); 3488a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 3498a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final DragStartHelper helper = createDragStartHelper(listener); 3508a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mDragSource.setOnLongClickListener(new View.OnLongClickListener() { 3518a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev @Override 3528a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev public boolean onLongClick(View view) { 3538a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev return helper.onLongClick(view); 3548a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 3558a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev }); 3568a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 3578a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev sendTouchEvent(MotionEvent.ACTION_DOWN, mDragSource, 1, 2); 3588a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev waitForLongPress(); 3598a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 3608a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev // Long press triggers OnDragStart. 3618a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev // Since ACTION_DOWN is not handled, the touch offset is not available. 3628a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev verify(listener, times(1)).onDragStart( 3638a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev eq(mDragSource), eq(helper), argThat(new TouchPositionMatcher(0, 0))); 3648a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev verifyNoMoreInteractions(listener); 3658a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 3668a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev} 367