@momo-kits/calculator-keyboard 0.112.1-rn76.6 → 0.112.1-rn76.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Dũng (Wem)
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # react-native-calculator-keyboard
2
+
3
+ react native calculator keyboard
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ npm install react-native-calculator-keyboard
9
+ ```
10
+
11
+ ## Usage
12
+
13
+
14
+ ```js
15
+ import { CalculatorKeyboardView } from "react-native-calculator-keyboard";
16
+
17
+ // ...
18
+
19
+ <CalculatorKeyboardView color="tomato" />
20
+ ```
21
+
22
+
23
+ ## Contributing
24
+
25
+ See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
26
+
27
+ ## License
28
+
29
+ MIT
30
+
31
+ ---
32
+
33
+ Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
@@ -0,0 +1,101 @@
1
+ buildscript {
2
+ // Buildscript is evaluated before everything else so we can't use getExtOrDefault
3
+ def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["CalculatorKeyboard_kotlinVersion"]
4
+
5
+ repositories {
6
+ google()
7
+ mavenCentral()
8
+ }
9
+
10
+ dependencies {
11
+ classpath "com.android.tools.build:gradle:7.2.1"
12
+ // noinspection DifferentKotlinGradleVersion
13
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14
+ }
15
+ }
16
+
17
+ def reactNativeArchitectures() {
18
+ def value = rootProject.getProperties().get("reactNativeArchitectures")
19
+ return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
20
+ }
21
+
22
+ def isNewArchitectureEnabled() {
23
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
24
+ }
25
+
26
+ apply plugin: "com.android.library"
27
+ apply plugin: "kotlin-android"
28
+
29
+ if (isNewArchitectureEnabled()) {
30
+ apply plugin: "com.facebook.react"
31
+ }
32
+
33
+ def getExtOrDefault(name) {
34
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["CalculatorKeyboard_" + name]
35
+ }
36
+
37
+ def getExtOrIntegerDefault(name) {
38
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["CalculatorKeyboard_" + name]).toInteger()
39
+ }
40
+
41
+ def supportsNamespace() {
42
+ def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
43
+ def major = parsed[0].toInteger()
44
+ def minor = parsed[1].toInteger()
45
+
46
+ // Namespace support was added in 7.3.0
47
+ return (major == 7 && minor >= 3) || major >= 8
48
+ }
49
+
50
+ android {
51
+ if (supportsNamespace()) {
52
+ namespace "com.calculatorkeyboard"
53
+
54
+ sourceSets {
55
+ main {
56
+ manifest.srcFile "src/main/AndroidManifestNew.xml"
57
+ }
58
+ }
59
+ }
60
+
61
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
62
+
63
+ defaultConfig {
64
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
65
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
66
+
67
+ }
68
+
69
+ buildTypes {
70
+ release {
71
+ minifyEnabled false
72
+ }
73
+ }
74
+
75
+ lintOptions {
76
+ disable "GradleCompatible"
77
+ }
78
+
79
+ compileOptions {
80
+ sourceCompatibility JavaVersion.VERSION_1_8
81
+ targetCompatibility JavaVersion.VERSION_1_8
82
+ }
83
+ }
84
+
85
+ repositories {
86
+ mavenCentral()
87
+ google()
88
+ }
89
+
90
+ def kotlin_version = getExtOrDefault("kotlinVersion")
91
+
92
+ dependencies {
93
+ // For < 0.71, this will be from the local maven repo
94
+ // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
95
+ //noinspection GradleDynamicVersion
96
+ implementation "com.facebook.react:react-native:+"
97
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
98
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
99
+ implementation("org.mariuszgromada.math:MathParser.org-mXparser:5.2.1")
100
+ }
101
+
@@ -0,0 +1,5 @@
1
+ CalculatorKeyboard_kotlinVersion=1.7.0
2
+ CalculatorKeyboard_minSdkVersion=21
3
+ CalculatorKeyboard_targetSdkVersion=31
4
+ CalculatorKeyboard_compileSdkVersion=31
5
+ CalculatorKeyboard_ndkversion=21.4.7075529
@@ -0,0 +1,3 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ package="com.calculatorkeyboard">
3
+ </manifest>
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,17 @@
1
+ package com.calculatorkeyboard
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+
9
+ class CalculatorKeyboardPackage : ReactPackage {
10
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
11
+ return emptyList()
12
+ }
13
+
14
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
15
+ return listOf(RCTInputCalculator())
16
+ }
17
+ }
@@ -0,0 +1,28 @@
1
+ package com.calculatorkeyboard
2
+
3
+ import android.content.Context
4
+ import android.graphics.Rect
5
+ import com.facebook.react.views.textinput.ReactEditText
6
+
7
+ class CalculatorEditText(context: Context) : ReactEditText(context) {
8
+ var onFocusListener: OnFocusChangeListener? = null
9
+
10
+ override fun requestFocus(direction: Int, previouslyFocusedRect: Rect?): Boolean {
11
+ if (isFocused) {
12
+ return true
13
+ }
14
+ isFocusable = true
15
+ isFocusableInTouchMode = true
16
+ return super.requestFocus(direction, previouslyFocusedRect)
17
+ }
18
+
19
+ override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
20
+ super.onFocusChanged(focused, direction, previouslyFocusedRect)
21
+ onFocusListener?.onFocusChange(this, isFocused)
22
+ }
23
+
24
+
25
+ interface OnFocusChangeListener {
26
+ fun onFocusChange(view: CalculatorEditText, hasFocus: Boolean)
27
+ }
28
+ }
@@ -0,0 +1,242 @@
1
+ package com.calculatorkeyboard
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.graphics.Color
5
+ import android.graphics.drawable.GradientDrawable
6
+ import android.view.Gravity
7
+ import android.widget.Button
8
+ import android.widget.ImageButton
9
+ import androidx.appcompat.app.AppCompatActivity
10
+ import androidx.constraintlayout.widget.ConstraintLayout
11
+ import androidx.core.graphics.ColorUtils
12
+ import com.facebook.react.uimanager.ThemedReactContext
13
+ import org.mariuszgromada.math.mxparser.Expression
14
+
15
+
16
+ @SuppressLint("SetTextI18n", "ViewConstructor")
17
+ class CustomKeyboardView(context: ThemedReactContext, private val editText: CalculatorEditText) :
18
+ ConstraintLayout(context) {
19
+ private val keys = listOf(
20
+ listOf("AC", "÷", "×", "back"),
21
+ listOf("7", "8", "9", "-"),
22
+ listOf("4", "5", "6", "+"),
23
+ listOf("1", "2", "3", "="),
24
+ listOf("000", "0")
25
+ )
26
+ private val specialKeys = listOf("=", "-", "×", "÷", "AC", "back", "+")
27
+ private val separatorWidth = 8f
28
+ private var keyboardColor: Int = Color.parseColor("#F7ACD5")
29
+
30
+ init {
31
+ val activity = context.currentActivity as? AppCompatActivity
32
+ if (activity != null) {
33
+ val displayMetrics = resources.displayMetrics
34
+ val widthButton = (displayMetrics.widthPixels - separatorWidth * 2 - 3 * separatorWidth) / 4f
35
+
36
+ renderUI(widthButton)
37
+ }
38
+
39
+ }
40
+
41
+ private fun renderUI(buttonWidth: Float) {
42
+ val buttonHeight = buttonWidth / 2
43
+ var yOffset = separatorWidth
44
+ for ((_, row) in keys.withIndex()) {
45
+ var xOffset = separatorWidth
46
+ for ((_, key) in row.withIndex()) {
47
+ val width = if (key == "000") buttonWidth * 2 + separatorWidth else buttonWidth
48
+ val height = if (key == "=") buttonWidth + separatorWidth else buttonHeight
49
+
50
+ val button = if (key == "back") {
51
+ createImageButton(key, xOffset, yOffset, buttonWidth.toInt(), buttonHeight.toInt())
52
+ } else {
53
+ createButton(key, xOffset, yOffset, width.toInt(), height.toInt())
54
+ }
55
+
56
+ addView(button)
57
+
58
+ xOffset += width + separatorWidth
59
+ }
60
+ yOffset += buttonHeight + separatorWidth
61
+ }
62
+ }
63
+
64
+ private fun createButton(
65
+ key: String,
66
+ xOffset: Float,
67
+ yOffset: Float,
68
+ buttonWidth: Int,
69
+ buttonHeight: Int,
70
+ ): Button {
71
+ val specialKeys = listOf("=", "-", "×", "÷", "AC", "back", "+")
72
+ return Button(context).apply {
73
+ val shapeInit = GradientDrawable().apply {
74
+ shape = GradientDrawable.RECTANGLE
75
+ cornerRadius = 24f
76
+ setColor(Color.WHITE)
77
+ setBackgroundColor(Color.WHITE)
78
+ }
79
+ gravity = Gravity.CENTER
80
+ background = shapeInit
81
+ text = key
82
+ setTypeface(typeface)
83
+ textSize = 24.toFloat()
84
+ setTextColor(Color.BLACK)
85
+ stateListAnimator = null
86
+ layoutParams = LayoutParams(
87
+ buttonWidth,
88
+ buttonHeight
89
+ ).apply {
90
+ constrainedWidth = false
91
+ }
92
+
93
+ if (specialKeys.contains(key)) {
94
+ if (key == "=") {
95
+ background = GradientDrawable().apply {
96
+ shape = GradientDrawable.RECTANGLE
97
+ cornerRadius = 24f
98
+ setColor(Color.parseColor("#EB2F96"))
99
+ }
100
+ }
101
+ background = GradientDrawable().apply {
102
+ shape = GradientDrawable.RECTANGLE
103
+ cornerRadius = 24f
104
+ setColor(Color.parseColor("#F7ACD5"))
105
+ }
106
+ setTextColor(Color.WHITE)
107
+ }
108
+
109
+
110
+ translationX = xOffset.toInt().toFloat()
111
+ translationY = yOffset.toInt().toFloat()
112
+ setOnClickListener { onKeyPress(key) }
113
+ }
114
+ }
115
+
116
+ private fun createImageButton(
117
+ key: String,
118
+ xOffset: Float,
119
+ yOffset: Float,
120
+ buttonWidth: Int,
121
+ buttonHeight: Int,
122
+ ): ImageButton {
123
+ return ImageButton(context).apply {
124
+ val shapeInit = GradientDrawable().apply {
125
+ shape = GradientDrawable.RECTANGLE
126
+ cornerRadius = 24f
127
+ setColor(Color.parseColor("#F7ACD5"))
128
+ }
129
+ background = shapeInit
130
+ stateListAnimator = null
131
+ layoutParams = LayoutParams(
132
+ buttonWidth,
133
+ buttonHeight
134
+ ).apply {
135
+ constrainedWidth = false
136
+ }
137
+ translationX = xOffset
138
+ translationY = yOffset
139
+ setImageResource(android.R.drawable.ic_input_delete)
140
+ setOnClickListener { onKeyPress(key) }
141
+ }
142
+ }
143
+
144
+ fun updateButtonColors(color: Int) {
145
+ keyboardColor = color
146
+ for (i in 0 until childCount) {
147
+ val child = getChildAt(i)
148
+ if (child is Button) {
149
+ val key = child.text.toString()
150
+ if (specialKeys.contains(key)) {
151
+ if (key == "=") {
152
+ child.background = GradientDrawable().apply {
153
+ shape = GradientDrawable.RECTANGLE
154
+ cornerRadius = 24f
155
+ setColor(keyboardColor)
156
+ }
157
+ } else {
158
+ child.background = GradientDrawable().apply {
159
+ shape = GradientDrawable.RECTANGLE
160
+ cornerRadius = 24f
161
+ setColor(ColorUtils.setAlphaComponent(keyboardColor, 128))
162
+ }
163
+ }
164
+ child.setTextColor(Color.WHITE)
165
+ }
166
+ } else if (child is ImageButton) {
167
+ child.background = GradientDrawable().apply {
168
+ shape = GradientDrawable.RECTANGLE
169
+ cornerRadius = 24f
170
+ setColor(ColorUtils.setAlphaComponent(keyboardColor, 128))
171
+ }
172
+ }
173
+ }
174
+ }
175
+
176
+ private fun onKeyPress(key: String) {
177
+ when (key) {
178
+ "AC" -> {
179
+ clearText()
180
+ }
181
+
182
+ "back" -> {
183
+ onBackSpace()
184
+ }
185
+
186
+ "=" -> {
187
+ calculateResult()
188
+ }
189
+
190
+ "×", "+", "-", "÷" -> keyDidPress(" $key ")
191
+ else -> {
192
+ editText.text?.insert(editText.selectionStart, key)
193
+ }
194
+ }
195
+ }
196
+
197
+ private fun keyDidPress(key: String) {
198
+ println("Key pressed: $key")
199
+ editText.text?.replace(editText.selectionStart, editText.selectionEnd, key)
200
+ }
201
+
202
+ private fun clearText() {
203
+ editText.text?.clear()
204
+ }
205
+
206
+ private fun onBackSpace() {
207
+ val start = editText.selectionStart
208
+ val end = editText.selectionEnd
209
+ if (start > 0) {
210
+ val newText = end.let { editText.text?.replaceRange(start - 1, it, "") }
211
+ editText.setText(newText)
212
+ editText.setSelection(start - 1)
213
+ }
214
+ }
215
+
216
+ private fun calculateResult() {
217
+ val text = editText?.text.toString().replace("×", "*").replace("÷", "/")
218
+ val pattern = "^\\s*(-?\\d+(\\.\\d+)?\\s*[-+*/]\\s*)*-?\\d+(\\.\\d+)?\\s*$"
219
+ val regex = Regex(pattern)
220
+ if (regex.matches(text)) {
221
+ try {
222
+ val result = eval(text).toString()
223
+ editText.setTextKeepState(result)
224
+ } catch (e: Exception) {
225
+ e.printStackTrace()
226
+ }
227
+ } else {
228
+ println("Invalid expression")
229
+ }
230
+ }
231
+
232
+ private fun eval(str: String): Long? {
233
+ val e = Expression(str)
234
+ println("Expression: $e")
235
+ return if (e.checkSyntax()) {
236
+ e.calculate().toLong()
237
+ } else {
238
+ null
239
+ }
240
+ }
241
+
242
+ }
@@ -0,0 +1,128 @@
1
+ package com.calculatorkeyboard
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.app.Activity
5
+ import android.graphics.Color
6
+ import android.view.KeyEvent
7
+ import android.view.View
8
+ import android.view.ViewGroup
9
+ import android.view.WindowManager
10
+ import android.view.inputmethod.InputMethodManager
11
+ import androidx.constraintlayout.widget.ConstraintLayout
12
+ import com.facebook.react.bridge.UiThreadUtil
13
+ import com.facebook.react.uimanager.ThemedReactContext
14
+ import com.facebook.react.uimanager.annotations.ReactProp
15
+ import com.facebook.react.views.textinput.ReactEditText
16
+ import com.facebook.react.views.textinput.ReactTextInputManager
17
+
18
+
19
+ class RCTInputCalculator : ReactTextInputManager() {
20
+ private var layout: ConstraintLayout? = null
21
+ private var keyboardView: CustomKeyboardView? = null
22
+ private var calculatorHeight = 0
23
+
24
+ override fun getName() = REACT_CLASS
25
+
26
+ companion object {
27
+ const val REACT_CLASS = "RCTInputCalculator"
28
+ }
29
+
30
+ @ReactProp(name = "value")
31
+ fun setValue(view: ReactEditText, value: String?) {
32
+ view.setText(value)
33
+ }
34
+
35
+ @ReactProp(name = "keyboardColor")
36
+ fun setKeyboardColor(view: ReactEditText, color: String) {
37
+ keyboardView?.updateButtonColors(Color.parseColor(color))
38
+ }
39
+
40
+ override fun createViewInstance(context: ThemedReactContext): ReactEditText {
41
+ val editText = CalculatorEditText(context)
42
+ layout = ConstraintLayout(context)
43
+ keyboardView = CustomKeyboardView(context, editText)
44
+ keyboardView?.setBackgroundColor(Color.parseColor("#f2f2f6"))
45
+
46
+ val displayMetrics = context.currentActivity!!.resources.displayMetrics
47
+ val screenHeight = displayMetrics.heightPixels
48
+ calculatorHeight = (displayMetrics.widthPixels * 0.675).toInt()
49
+
50
+ val lParams = ConstraintLayout.LayoutParams(
51
+ ConstraintLayout.LayoutParams.MATCH_PARENT,
52
+ ConstraintLayout.LayoutParams.WRAP_CONTENT
53
+ ).apply {
54
+ height = calculatorHeight
55
+ bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID
56
+ setMargins(0, screenHeight - calculatorHeight, 0, 0)
57
+ }
58
+ if (context.currentActivity != null) {
59
+ (layout as ConstraintLayout).addView(keyboardView, lParams)
60
+ }
61
+
62
+ editText.setOnClickListener { v: View ->
63
+ UiThreadUtil.runOnUiThread {
64
+ (context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager).hideSoftInputFromWindow(
65
+ v.windowToken,
66
+ 0
67
+ )
68
+ editText.setShowSoftInputOnFocus(false)
69
+ if (!editText.isFocused) {
70
+ editText.requestFocusFromJS()
71
+ }
72
+ }
73
+ }
74
+
75
+ editText.onFocusListener = object : CalculatorEditText.OnFocusChangeListener {
76
+ override fun onFocusChange(view: CalculatorEditText, hasFocus: Boolean) {
77
+ UiThreadUtil.runOnUiThread {
78
+ if (hasFocus) {
79
+ addContentView(context)
80
+ } else {
81
+ removeContentView(context)
82
+ }
83
+ view.setOnKeyListener { view, keyCode, _ ->
84
+ if (keyCode == KeyEvent.KEYCODE_BACK && hasFocus) {
85
+ view.isFocusable = false
86
+ view.clearFocus()
87
+ return@setOnKeyListener true
88
+ }
89
+ false
90
+ }
91
+ }
92
+ }
93
+ }
94
+
95
+ return editText
96
+ }
97
+
98
+ private fun addContentView(context: ThemedReactContext) {
99
+ if (layout!!.parent == null) {
100
+ context.currentActivity!!.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
101
+ layout!!.y = calculatorHeight.toFloat()
102
+
103
+ context.currentActivity!!.addContentView(
104
+ layout,
105
+ ViewGroup.LayoutParams(
106
+ ViewGroup.LayoutParams.MATCH_PARENT,
107
+ ViewGroup.LayoutParams.MATCH_PARENT
108
+ )
109
+ )
110
+ layout!!.animate().translationY(0f).setDuration(250)
111
+ }
112
+ }
113
+
114
+ private fun removeContentView(context: ThemedReactContext) {
115
+ if (layout!!.parent != null) {
116
+ layout!!.y = 0f
117
+ layout!!.animate().translationY(calculatorHeight.toFloat()).setDuration(250).withEndAction {
118
+ context.currentActivity!!.window
119
+ .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
120
+ if (layout!!.parent != null) {
121
+ (layout!!.parent as ViewGroup).removeView(layout)
122
+ }
123
+ }
124
+ }
125
+ }
126
+
127
+ }
128
+