DragStartHelper.java revision 8a771cded580e2b83a0eb158cae7ee13ce0b85cd
12196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev/* 22196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Copyright (C) 2016 The Android Open Source Project 32196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * 42196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Licensed under the Apache License, Version 2.0 (the "License"); 52196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * you may not use this file except in compliance with the License. 62196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * You may obtain a copy of the License at 72196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * 82196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * http://www.apache.org/licenses/LICENSE-2.0 92196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * 102196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Unless required by applicable law or agreed to in writing, software 112196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * distributed under the License is distributed on an "AS IS" BASIS, 122196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * See the License for the specific language governing permissions and 142196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * limitations under the License. 152196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev */ 162196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 172196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheevpackage android.support.v13.view; 182196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 192196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 208f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viveretteimport android.annotation.TargetApi; 212196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheevimport android.graphics.Point; 228f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viveretteimport android.support.annotation.RequiresApi; 232196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheevimport android.support.v4.view.InputDeviceCompat; 242196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheevimport android.support.v4.view.MotionEventCompat; 252196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheevimport android.view.MotionEvent; 262196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheevimport android.view.View; 272196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 282196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev/** 292196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * DragStartHelper is a utility class for implementing drag and drop support. 302196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <p> 312196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * It detects gestures commonly used to start drag (long click for any input source, 322196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * click and drag for mouse). 332196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <p> 342196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * It also keeps track of the screen location where the drag started, and helps determining 35d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * the hot spot position for a drag shadow. 362196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <p> 37d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * Implement {@link DragStartHelper.OnDragStartListener} to start the drag operation: 382196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <pre> 39d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * DragStartHelper.OnDragStartListener listener = new DragStartHelper.OnDragStartListener { 40d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * protected void onDragStart(View view, DragStartHelper helper) { 41d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) { 42d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) { 43d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * super.onProvideShadowMetrics(shadowSize, shadowTouchPoint); 44d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * helper.getTouchPosition(shadowTouchPoint); 45d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * } 46d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * }; 47d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * view.startDrag(mClipData, shadowBuilder, mLocalState, mDragFlags); 482196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * } 492196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * }; 50d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * mDragStartHelper = new DragStartHelper(mDraggableView, listener); 512196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * </pre> 522196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Once created, DragStartHelper can be attached to a view (this will replace existing long click 532196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * and touch listeners): 542196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <pre> 552196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * mDragStartHelper.attach(); 562196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * </pre> 572196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * It may also be used in combination with existing listeners: 582196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <pre> 592196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * public boolean onTouch(View view, MotionEvent event) { 60d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * if (mDragStartHelper.onTouch(view, event)) { 61d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * return true; 62d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * } 63d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * return handleTouchEvent(view, event); 642196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * } 652196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * public boolean onLongClick(View view) { 66d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * if (mDragStartHelper.onLongClick(view)) { 67d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * return true; 68d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * } 69d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * return handleLongClickEvent(view); 702196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * } 712196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * </pre> 722196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev */ 738f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette@RequiresApi(13) 748f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette@TargetApi(13) 75d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheevpublic class DragStartHelper { 762196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev final private View mView; 77d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev final private OnDragStartListener mListener; 78d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev 79d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev private int mLastTouchX, mLastTouchY; 808a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev private boolean mDragging; 81d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev 82d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev /** 83d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * Interface definition for a callback to be invoked when a drag start gesture is detected. 84d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev */ 85d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev public interface OnDragStartListener { 86d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev /** 87d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * Called when a drag start gesture has been detected. 88d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * 89d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * @param v The view over which the drag start gesture has been detected. 90d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * @param helper The DragStartHelper object which detected the gesture. 918a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev * @return True if the listener has started the drag operation, false otherwise. 92d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev */ 93d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev boolean onDragStart(View v, DragStartHelper helper); 94d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev } 952196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 962196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev /** 972196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Create a DragStartHelper associated with the specified view. 98d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev * The newly created helper is not initially attached to the view, {@link #attach} must be 992196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * called explicitly. 1002196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * @param view A View 1012196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev */ 102d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev public DragStartHelper(View view, OnDragStartListener listener) { 1032196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev mView = view; 104d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev mListener = listener; 1052196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 1062196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 1072196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev /** 1082196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Attach the helper to the view. 1092196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <p> 1102196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * This will replace previously existing touch and long click listeners. 1112196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev */ 1122196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev public void attach() { 113d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev mView.setOnLongClickListener(mLongClickListener); 114d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev mView.setOnTouchListener(mTouchListener); 1152196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 1162196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 1172196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev /** 1182196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Detach the helper from the view. 1192196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * <p> 1202196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * This will reset touch and long click listeners to {@code null}. 1212196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev */ 122d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev public void detach() { 1232196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev mView.setOnLongClickListener(null); 1242196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev mView.setOnTouchListener(null); 1252196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 1262196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 1272196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev /** 1282196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Handle a touch event. 1292196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * @param v The view the touch event has been dispatched to. 1302196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * @param event The MotionEvent object containing full information about 1312196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * the event. 1322196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * @return True if the listener has consumed the event, false otherwise. 1332196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev */ 134d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev public boolean onTouch(View v, MotionEvent event) { 1358a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final int x = (int) event.getX(); 1368a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev final int y = (int) event.getY(); 1378a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev switch (event.getAction()) { 1388a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev case MotionEvent.ACTION_DOWN: 1398a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mLastTouchX = x; 1408a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mLastTouchY = y; 1418a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev break; 1428a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 1438a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev case MotionEvent.ACTION_MOVE: 1448a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev if (!MotionEventCompat.isFromSource(event, InputDeviceCompat.SOURCE_MOUSE) 1458a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev || (MotionEventCompat.getButtonState(event) 1468a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev & MotionEventCompat.BUTTON_PRIMARY) == 0) { 1478a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev break; 1488a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 1498a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev if (mDragging) { 1508a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev // Ignore ACTION_MOVE events once the drag operation is in progress. 1518a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev break; 1528a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 1538a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev if (mLastTouchX == x && mLastTouchY == y) { 1548a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev // Do not call the listener unless the pointer position has actually changed. 1558a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev break; 1568a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev } 1578a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mLastTouchX = x; 1588a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mLastTouchY = y; 1598a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mDragging = mListener.onDragStart(v, this); 1608a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev return mDragging; 1618a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev 1628a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev case MotionEvent.ACTION_UP: 1638a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev case MotionEvent.ACTION_CANCEL: 1648a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev mDragging = false; 1658a771cded580e2b83a0eb158cae7ee13ce0b85cdVladislav Kaznacheev break; 1662196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 1672196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev return false; 1682196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 1692196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 1702196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev /** 1712196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Handle a long click event. 1722196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * @param v The view that was clicked and held. 1732196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * @return true if the callback consumed the long click, false otherwise. 1742196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev */ 175d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev public boolean onLongClick(View v) { 176d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev return mListener.onDragStart(v, this); 1772196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 1782196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 1792196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev /** 1802196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * Compute the position of the touch event that started the drag operation. 1812196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev * @param point The position of the touch event that started the drag operation. 1822196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev */ 183d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev public void getTouchPosition(Point point) { 184d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev point.set(mLastTouchX, mLastTouchY); 1852196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 1862196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 187d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev private final View.OnLongClickListener mLongClickListener = new View.OnLongClickListener() { 188d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev @Override 189d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev public boolean onLongClick(View v) { 190d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev return DragStartHelper.this.onLongClick(v); 1912196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 192d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev }; 1932196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 194d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev private final View.OnTouchListener mTouchListener = new View.OnTouchListener() { 1952196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev @Override 196d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev public boolean onTouch(View v, MotionEvent event) { 197d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev return DragStartHelper.this.onTouch(v, event); 1982196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev } 199d1533217e5b9bf24ac4e1f04a00c34cb61267bc9Vladislav Kaznacheev }; 2002196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev} 2012196f53d080334b4e5bb5c25fba1e40578f3588bVladislav Kaznacheev 202