From 2ae2a94c4d6203808b87a1bac3c93c2a7550a89f Mon Sep 17 00:00:00 2001 From: gal kahana Date: Sun, 12 Jul 2020 12:53:42 +0300 Subject: [PATCH 1/8] support accessibility functions for appium testing --- .../java/com/zyu/ReactWheelCurvedPicker.java | 166 +++++++++++++++++- 1 file changed, 163 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/zyu/ReactWheelCurvedPicker.java b/android/src/main/java/com/zyu/ReactWheelCurvedPicker.java index 0999ff3..99e9f7e 100644 --- a/android/src/main/java/com/zyu/ReactWheelCurvedPicker.java +++ b/android/src/main/java/com/zyu/ReactWheelCurvedPicker.java @@ -5,10 +5,8 @@ import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Shader; -import android.os.SystemClock; -import android.util.AttributeSet; -import com.aigestudio.wheelpicker.core.AbstractWheelPicker; + import com.aigestudio.wheelpicker.view.WheelCurvedPicker; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactContext; @@ -17,7 +15,15 @@ import com.facebook.react.uimanager.events.Event; import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.react.uimanager.events.RCTEventEmitter; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeProvider; +import android.graphics.Rect; +import android.view.View; +import android.text.TextUtils; +import android.os.Bundle; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -33,6 +39,9 @@ public class ReactWheelCurvedPicker extends WheelCurvedPicker { private Integer mLinegradientFrom = Color.BLACK; // Default starting gradient color private Integer mLinegradientTo = Color.WHITE; // Default end gradient color + /** The instance of the node provider for the virtual tree - lazily instantiated. */ + private AccessibilityNodeProvider mAccessibilityNodeProvider; + public ReactWheelCurvedPicker(ReactContext reactContext) { super(reactContext); mEventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher(); @@ -55,6 +64,32 @@ public void onWheelScrollStateChanged(int state) { }); } + @Override + public void onInitializeAccessibilityNodeInfo (AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + + Rect r = new Rect(); + boolean isVisible = this.getGlobalVisibleRect(r); + info.setBoundsInScreen(r); + info.setVisibleToUser(isVisible); + info.setClassName(this.getClass().getCanonicalName()); + info.setPackageName(getContext().getPackageName()); + info.setScrollable(isVisible); + info.setEnabled(true); + info.setText(data.get(itemIndex)); + } + + @Override + public AccessibilityNodeProvider getAccessibilityNodeProvider() { + // Instantiate the provide only when requested. Since the system + // will call this method multiple times it is a good practice to + // cache the provider instance. + if (mAccessibilityNodeProvider == null) { + mAccessibilityNodeProvider = new VirtualDescendantsProvider(); + } + return mAccessibilityNodeProvider; + } + @Override protected void drawForeground(Canvas canvas) { super.drawForeground(canvas); @@ -102,6 +137,131 @@ public void setValueData(List data) { public int getState() { return state; } + + private class VirtualDescendantsProvider extends AccessibilityNodeProvider { + + /** + * {@inheritDoc} + */ + @Override + public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { + AccessibilityNodeInfo info = null; + ReactWheelCurvedPicker root = ReactWheelCurvedPicker.this; + + if (virtualViewId == View.NO_ID) { + info = AccessibilityNodeInfo.obtain(root); + onInitializeAccessibilityNodeInfo(info); + + int childCount = root.mValueData != null ? root.mValueData.size() : 0; + int sideSize = (root.itemCount % 2 == 0 ? root.itemCount : (root.itemCount - 1))/2; + int minIndex = Math.max(root.itemIndex - sideSize,0); + int maxIndex = Math.min(root.itemIndex + sideSize, childCount-1); + + for (int i = minIndex; i <= maxIndex; ++i) { + info.addChild(root, i); + } + } else { + info = AccessibilityNodeInfo.obtain(); + info.setClassName(root.getClass().getName() + "Item"); + info.setPackageName(getContext().getPackageName()); + info.setSource(root, virtualViewId); + + // A Naive computation of bounds per item, by dividing global space + // to slots per itemsCount, and figuring out the right position + // as offset from the selected item, which is the center + int childCount = root.mValueData != null ? root.mValueData.size() : 0; + int sideSize = (root.itemCount % 2 == 0 ? root.itemCount : (root.itemCount - 1))/2; + int minIndex = Math.max(root.itemIndex - sideSize,0); + int maxIndex = Math.min(root.itemIndex + sideSize, childCount-1); + boolean isInView = (virtualViewId>= minIndex && virtualViewId<=maxIndex); + + Rect r = new Rect(); + boolean isVisible = isInView && root.getGlobalVisibleRect(r); + + if(isInView) { + int itemHeight = r.height()/root.itemCount; + r.top += (virtualViewId - root.itemIndex + sideSize)*itemHeight; + r.bottom = r.top + itemHeight; + } + else { + r.top = r.bottom = r.left = r.right = 0; + } + + info.setBoundsInScreen(r); + info.setVisibleToUser(isVisible); + info.setParent(root); + info.setText(root.data.get(virtualViewId)); + info.setSelected(root.itemIndex == virtualViewId); + info.setEnabled(true); + } + return info; + } + + /** + * {@inheritDoc} + */ + @Override + public List findAccessibilityNodeInfosByText(String searched, + int virtualViewId) { + if (TextUtils.isEmpty(searched)) { + return Collections.emptyList(); + } + String searchedLowerCase = searched.toLowerCase(); + List result = null; + ReactWheelCurvedPicker root = ReactWheelCurvedPicker.this; + + if (virtualViewId == View.NO_ID) { + for (int i = 0; i < root.data.size(); i++) { + String textToLowerCase = root.data.get(i).toLowerCase(); + if (textToLowerCase.contains(searchedLowerCase)) { + if (result == null) { + result = new ArrayList(); + } + result.add(createAccessibilityNodeInfo(i)); + } + } + } else { + String textToLowerCase = root.data.get(virtualViewId).toLowerCase(); + if (textToLowerCase.contains(searchedLowerCase)) { + result = new ArrayList(); + result.add(createAccessibilityNodeInfo(virtualViewId)); + } + } + if (result == null) { + return Collections.emptyList(); + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean performAction(int virtualViewId, int action, Bundle arguments) { + + ReactWheelCurvedPicker root = ReactWheelCurvedPicker.this; + + if (virtualViewId == View.NO_ID) { + switch (action) { + // Allowing lowercase search as in the implementation of findAccessibilityNodeInfosByText + case AccessibilityNodeInfo.ACTION_SET_TEXT: + CharSequence chars = arguments.getCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE); + String searched = chars.toString(); + String searchedLowerCase = searched.toLowerCase(); + for (int i = 0; i < root.data.size(); i++) { + String textToLowerCase = root.data.get(i).toLowerCase(); + if (textToLowerCase.contains(searchedLowerCase)) { + // found! + root.setItemIndex(i); + break; + } + } + return true; + } + } + return false; + } + } } class ItemSelectedEvent extends Event { From 5e950a3b04043865bf4d1bc61de429d5cc212566 Mon Sep 17 00:00:00 2001 From: gal kahana Date: Sun, 12 Jul 2020 16:21:45 +0300 Subject: [PATCH 2/8] slight improvement to ignore clear text commands sent prior to setting actual text. results in not moving selection for not found input --- .../java/com/zyu/ReactWheelCurvedPicker.java | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/android/src/main/java/com/zyu/ReactWheelCurvedPicker.java b/android/src/main/java/com/zyu/ReactWheelCurvedPicker.java index 99e9f7e..2c1e270 100644 --- a/android/src/main/java/com/zyu/ReactWheelCurvedPicker.java +++ b/android/src/main/java/com/zyu/ReactWheelCurvedPicker.java @@ -81,9 +81,6 @@ public void onInitializeAccessibilityNodeInfo (AccessibilityNodeInfo info) { @Override public AccessibilityNodeProvider getAccessibilityNodeProvider() { - // Instantiate the provide only when requested. Since the system - // will call this method multiple times it is a good practice to - // cache the provider instance. if (mAccessibilityNodeProvider == null) { mAccessibilityNodeProvider = new VirtualDescendantsProvider(); } @@ -139,10 +136,6 @@ public int getState() { } private class VirtualDescendantsProvider extends AccessibilityNodeProvider { - - /** - * {@inheritDoc} - */ @Override public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { AccessibilityNodeInfo info = null; @@ -197,9 +190,6 @@ public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { return info; } - /** - * {@inheritDoc} - */ @Override public List findAccessibilityNodeInfosByText(String searched, int virtualViewId) { @@ -233,9 +223,6 @@ public List findAccessibilityNodeInfosByText(String searc return result; } - /** - * {@inheritDoc} - */ @Override public boolean performAction(int virtualViewId, int action, Bundle arguments) { @@ -247,11 +234,12 @@ public boolean performAction(int virtualViewId, int action, Bundle arguments) { case AccessibilityNodeInfo.ACTION_SET_TEXT: CharSequence chars = arguments.getCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE); String searched = chars.toString(); + if(TextUtils.isEmpty(searched)) + return true; // ignore empty text String searchedLowerCase = searched.toLowerCase(); for (int i = 0; i < root.data.size(); i++) { String textToLowerCase = root.data.get(i).toLowerCase(); if (textToLowerCase.contains(searchedLowerCase)) { - // found! root.setItemIndex(i); break; } From 6a0cf759427139ac5ad227ffd42deeaa1df26d13 Mon Sep 17 00:00:00 2001 From: YonatanAltaraz Date: Wed, 25 Nov 2020 17:43:03 +0200 Subject: [PATCH 3/8] add _UNSAFE --- WheelCurvedPicker.android.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WheelCurvedPicker.android.js b/WheelCurvedPicker.android.js index d123c90..6d83d13 100644 --- a/WheelCurvedPicker.android.js +++ b/WheelCurvedPicker.android.js @@ -41,7 +41,7 @@ class WheelCurvedPicker extends React.Component { itemSpace: 20 } - componentWillReceiveProps (props) { + UNSAFE_componentWillReceiveProps (props) { this.setState(this._stateFromProps(props)); } From cb8415bb8c2fbd4464e0a8fe31019e486d872765 Mon Sep 17 00:00:00 2001 From: Raimon Lapuente Date: Wed, 23 Dec 2020 16:42:46 +0100 Subject: [PATCH 4/8] actual change, import change for PickerIOS --- WheelCurvedPicker.ios.js | 8 +++----- index.js | 14 ++++++-------- node_modules/.yarn-integrity | 10 ++++++++++ yarn.lock | 4 ++++ 4 files changed, 23 insertions(+), 13 deletions(-) create mode 100644 node_modules/.yarn-integrity create mode 100644 yarn.lock diff --git a/WheelCurvedPicker.ios.js b/WheelCurvedPicker.ios.js index 875099e..adce0bb 100644 --- a/WheelCurvedPicker.ios.js +++ b/WheelCurvedPicker.ios.js @@ -1,8 +1,6 @@ -'use strict'; +"use strict"; -import React from 'react'; -import { - View, -} from 'react-native'; +import React from "react"; +import { View } from "react-native"; module.exports = View; diff --git a/index.js b/index.js index bc7ed07..0481d40 100644 --- a/index.js +++ b/index.js @@ -1,12 +1,10 @@ -'use strict'; +"use strict"; -import React from 'react'; +import React from "react"; -import { - PickerIOS, - Platform, -} from 'react-native'; +import { Platform } from "react-native"; +import { PickerIOS } from "@react-native-community/picker"; -import WheelCurvedPicker from './WheelCurvedPicker' +import WheelCurvedPicker from "./WheelCurvedPicker"; -module.exports = (Platform.OS === 'ios' ? PickerIOS : WheelCurvedPicker) +module.exports = Platform.OS === "ios" ? PickerIOS : WheelCurvedPicker; diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity new file mode 100644 index 0000000..1a2b97a --- /dev/null +++ b/node_modules/.yarn-integrity @@ -0,0 +1,10 @@ +{ + "systemParams": "darwin-x64-79", + "modulesFolders": [], + "flags": [], + "linkedModules": [], + "topLevelPatterns": [], + "lockfileEntries": {}, + "files": [], + "artifacts": {} +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..fb57ccd --- /dev/null +++ b/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + From 527c554e6b5f1037f1f9cf507acd604f0121ebe2 Mon Sep 17 00:00:00 2001 From: Raimon Lapuente Date: Mon, 28 Dec 2020 10:27:29 +0100 Subject: [PATCH 5/8] revert formating and automatic files --- WheelCurvedPicker.ios.js | 8 +++++--- index.js | 14 ++++++++------ node_modules/.yarn-integrity | 10 ---------- yarn.lock | 4 ---- 4 files changed, 13 insertions(+), 23 deletions(-) delete mode 100644 node_modules/.yarn-integrity delete mode 100644 yarn.lock diff --git a/WheelCurvedPicker.ios.js b/WheelCurvedPicker.ios.js index adce0bb..875099e 100644 --- a/WheelCurvedPicker.ios.js +++ b/WheelCurvedPicker.ios.js @@ -1,6 +1,8 @@ -"use strict"; +'use strict'; -import React from "react"; -import { View } from "react-native"; +import React from 'react'; +import { + View, +} from 'react-native'; module.exports = View; diff --git a/index.js b/index.js index 0481d40..7017bcd 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,12 @@ -"use strict"; +'use strict'; -import React from "react"; +import React from 'react'; -import { Platform } from "react-native"; -import { PickerIOS } from "@react-native-community/picker"; +import { + Platform, +} from 'react-native'; +import { PickerIOS } from '@react-native-community/picker' -import WheelCurvedPicker from "./WheelCurvedPicker"; +import WheelCurvedPicker from './WheelCurvedPicker' -module.exports = Platform.OS === "ios" ? PickerIOS : WheelCurvedPicker; +module.exports = (Platform.OS === 'ios' ? PickerIOS : WheelCurvedPicker) diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity deleted file mode 100644 index 1a2b97a..0000000 --- a/node_modules/.yarn-integrity +++ /dev/null @@ -1,10 +0,0 @@ -{ - "systemParams": "darwin-x64-79", - "modulesFolders": [], - "flags": [], - "linkedModules": [], - "topLevelPatterns": [], - "lockfileEntries": {}, - "files": [], - "artifacts": {} -} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index fb57ccd..0000000 --- a/yarn.lock +++ /dev/null @@ -1,4 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - From d287dcbe4cd78ac517c8f3bb165370db007460c6 Mon Sep 17 00:00:00 2001 From: Raimon Lapuente Date: Mon, 28 Dec 2020 16:33:16 +0100 Subject: [PATCH 6/8] specify minimum rn version --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 6f285dc..0718345 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,11 @@ "keywords": [ "react-native", "picker", - "wheel" + "wheel" ], + "peerDependencies": { + "react-native": ">=0.62" + }, "author": "Yu Zheng", "license": "ISC", "bugs": { From c63ea527b9f5f17e961275a7c4db0d3317002c02 Mon Sep 17 00:00:00 2001 From: Raimon Lapuente Date: Tue, 29 Dec 2020 07:39:56 +0100 Subject: [PATCH 7/8] increase version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0718345..83dc169 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-wheel-picker", - "version": "1.2.0", + "version": "1.3.0", "description": "React native cross platform picker.", "main": "index.js", "scripts": { From 3ec838d3d49bdf410c3db02f7a403e0f59f730bc Mon Sep 17 00:00:00 2001 From: Raimon Lapuente Date: Tue, 29 Dec 2020 09:40:43 +0100 Subject: [PATCH 8/8] add the picker as dependency --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 83dc169..93815b6 100644 --- a/package.json +++ b/package.json @@ -24,5 +24,7 @@ "url": "https://github.com/lesliesam/react-native-wheel-picker/issues" }, "homepage": "https://github.com/lesliesam/react-native-wheel-picker#readme", - "dependencies": {} + "dependencies": { + "@react-native-community/picker": "^1.8.1" + } }