@momo-kits/calculator-keyboard 0.167.1-test.2 → 0.168.1-beta.2
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.
|
@@ -3,9 +3,12 @@ package com.momokits.calculatorkeyboard.native
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
5
|
import android.content.res.ColorStateList
|
|
6
|
+
import android.graphics.Bitmap
|
|
7
|
+
import android.graphics.BitmapFactory
|
|
6
8
|
import android.graphics.Color
|
|
7
9
|
import android.graphics.drawable.GradientDrawable
|
|
8
10
|
import android.text.Editable
|
|
11
|
+
import android.util.DisplayMetrics
|
|
9
12
|
import android.view.Gravity
|
|
10
13
|
import android.view.MotionEvent
|
|
11
14
|
import android.widget.Button
|
|
@@ -17,6 +20,8 @@ import androidx.core.graphics.toColorInt
|
|
|
17
20
|
import com.momokits.calculatorkeyboard.native.utils.CalculatorEngine
|
|
18
21
|
import com.momokits.calculatorkeyboard.native.utils.CalculatorFormatter
|
|
19
22
|
import com.momokits.calculatorkeyboard.R
|
|
23
|
+
import java.net.URL
|
|
24
|
+
import kotlin.concurrent.thread
|
|
20
25
|
|
|
21
26
|
@SuppressLint("SetTextI18n", "ViewConstructor")
|
|
22
27
|
class CustomKeyboardView(
|
|
@@ -181,6 +186,26 @@ class CustomKeyboardView(
|
|
|
181
186
|
setImageTintList(ColorStateList.valueOf(Color.BLACK))
|
|
182
187
|
setOnClickListener { onKeyPress(key, false) }
|
|
183
188
|
applyImagePressedOpacity()
|
|
189
|
+
loadBackIcon()
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Swap the bundled fallback icon for the design-system icon once it's downloaded
|
|
194
|
+
private fun ImageButton.loadBackIcon() {
|
|
195
|
+
backIconCache?.let { return setImageBitmap(it) }
|
|
196
|
+
|
|
197
|
+
thread {
|
|
198
|
+
val bitmap = runCatching {
|
|
199
|
+
URL(BACK_ICON_URL).openConnection().apply {
|
|
200
|
+
connectTimeout = BACK_ICON_TIMEOUT_MS
|
|
201
|
+
readTimeout = BACK_ICON_TIMEOUT_MS
|
|
202
|
+
}.getInputStream().use(BitmapFactory::decodeStream)
|
|
203
|
+
}.getOrNull() ?: return@thread
|
|
204
|
+
|
|
205
|
+
// Density makes the drawable render at BACK_ICON_SIZE_DP without resampling the bitmap
|
|
206
|
+
bitmap.density = bitmap.width * DisplayMetrics.DENSITY_DEFAULT / BACK_ICON_SIZE_DP
|
|
207
|
+
backIconCache = bitmap
|
|
208
|
+
post { setImageBitmap(bitmap) }
|
|
184
209
|
}
|
|
185
210
|
}
|
|
186
211
|
|
|
@@ -400,4 +425,13 @@ class CustomKeyboardView(
|
|
|
400
425
|
}
|
|
401
426
|
customKeyButton?.setTextColor(textColor)
|
|
402
427
|
}
|
|
428
|
+
|
|
429
|
+
companion object {
|
|
430
|
+
private const val BACK_ICON_URL = "https://img.mservice.com.vn/app/img/kits/chat_back_space.png"
|
|
431
|
+
private const val BACK_ICON_SIZE_DP = 24
|
|
432
|
+
private const val BACK_ICON_TIMEOUT_MS = 10_000
|
|
433
|
+
|
|
434
|
+
@Volatile
|
|
435
|
+
private var backIconCache: Bitmap? = null
|
|
436
|
+
}
|
|
403
437
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#import "CalculatorKeyboardView.h"
|
|
2
2
|
#import "Utils.h"
|
|
3
3
|
|
|
4
|
+
static NSString *const kBackIconURL = @"https://img.mservice.com.vn/app/img/kits/chat_back_space.png";
|
|
5
|
+
static const CGFloat kBackIconSize = 24;
|
|
6
|
+
static UIImage *sBackIcon;
|
|
7
|
+
|
|
4
8
|
@interface CalculatorKeyboardView ()
|
|
5
9
|
@property (nonatomic, strong) NSArray<NSArray<NSString *> *> *numWithCTAKeys;
|
|
6
10
|
@property (nonatomic, strong) NSArray<NSArray<NSString *> *> *defaultKeys;
|
|
@@ -10,6 +14,8 @@
|
|
|
10
14
|
|
|
11
15
|
@implementation CalculatorKeyboardView {
|
|
12
16
|
CGFloat _separatorWidth;
|
|
17
|
+
UIView *_contentView;
|
|
18
|
+
NSArray<NSArray<UIButton *> *> *_buttonRows;
|
|
13
19
|
}
|
|
14
20
|
|
|
15
21
|
- (instancetype)initWithFrame:(CGRect)frame
|
|
@@ -85,28 +91,26 @@
|
|
|
85
91
|
|
|
86
92
|
self.backgroundColor = [Utils colorFromHex:@"#f2f2f6"];
|
|
87
93
|
|
|
88
|
-
CGFloat buttonWidth = (UIScreen.mainScreen.bounds.size.width - _separatorWidth * 2 - 4 * _separatorWidth) / 5;
|
|
89
|
-
CGFloat buttonHeight = (240 - _separatorWidth * 2 - 3 * _separatorWidth) / 4;
|
|
90
|
-
|
|
91
94
|
// Create content view
|
|
92
95
|
UIView *contentView = [[UIView alloc] init];
|
|
93
96
|
contentView.translatesAutoresizingMaskIntoConstraints = NO;
|
|
94
97
|
[self addSubview:contentView];
|
|
98
|
+
_contentView = contentView;
|
|
95
99
|
|
|
96
|
-
// Set contentView constraints
|
|
100
|
+
// Set contentView constraints, keeping clear of the side safe area (landscape, iPhone Duo)
|
|
97
101
|
[NSLayoutConstraint activateConstraints:@[
|
|
98
|
-
[contentView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:_separatorWidth],
|
|
99
|
-
[contentView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-_separatorWidth],
|
|
102
|
+
[contentView.leadingAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.leadingAnchor constant:_separatorWidth],
|
|
103
|
+
[contentView.trailingAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.trailingAnchor constant:-_separatorWidth],
|
|
100
104
|
[contentView.topAnchor constraintEqualToAnchor:self.topAnchor constant:_separatorWidth],
|
|
101
105
|
[contentView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-_separatorWidth]
|
|
102
106
|
]];
|
|
103
107
|
|
|
104
|
-
|
|
108
|
+
NSMutableArray<NSArray<UIButton *> *> *buttonRows = [NSMutableArray array];
|
|
105
109
|
NSArray<NSArray<NSString *> *> *keys = [_keyboardMode isEqualToString:@"NumDefault"] ? _defaultKeys : _numWithCTAKeys;
|
|
106
110
|
|
|
107
111
|
for (NSInteger rowIndex = 0; rowIndex < keys.count; rowIndex++) {
|
|
108
112
|
NSArray<NSString *> *row = keys[rowIndex];
|
|
109
|
-
|
|
113
|
+
NSMutableArray<UIButton *> *buttonRow = [NSMutableArray array];
|
|
110
114
|
|
|
111
115
|
for (NSInteger colIndex = 0; colIndex < row.count; colIndex++) {
|
|
112
116
|
NSString *key = row[colIndex];
|
|
@@ -124,22 +128,13 @@
|
|
|
124
128
|
button.accessibilityIdentifier = key;
|
|
125
129
|
button.tag = isMainCTAKey ? 1 : 0;
|
|
126
130
|
|
|
127
|
-
CGRect buttonFrame = CGRectMake(xOffset, yOffset, buttonWidth, buttonHeight);
|
|
128
|
-
if (isMainKey) {
|
|
129
|
-
buttonFrame.size.height = buttonHeight * 2 + _separatorWidth;
|
|
130
|
-
}
|
|
131
|
-
if ([key isEqualToString:@"000"]) {
|
|
132
|
-
buttonFrame.size.width = buttonWidth * 2 + _separatorWidth;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
button.frame = buttonFrame;
|
|
136
|
-
|
|
137
131
|
if ([key isEqualToString:@"back"]) {
|
|
138
132
|
[button setTitle:@"" forState:UIControlStateNormal];
|
|
139
133
|
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:24 weight:UIImageSymbolWeightBold];
|
|
140
134
|
UIImage *image = [UIImage systemImageNamed:@"delete.backward" withConfiguration:config];
|
|
141
135
|
[button setImage:image forState:UIControlStateNormal];
|
|
142
136
|
button.tintColor = UIColor.blackColor;
|
|
137
|
+
[self loadBackIconForButton:button];
|
|
143
138
|
}
|
|
144
139
|
|
|
145
140
|
if ([_specialKeys containsObject:key] || isMainKey) {
|
|
@@ -153,7 +148,44 @@
|
|
|
153
148
|
|
|
154
149
|
[button addTarget:self action:@selector(keyPressed:) forControlEvents:UIControlEventTouchUpInside];
|
|
155
150
|
[contentView addSubview:button];
|
|
151
|
+
[buttonRow addObject:button];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
[buttonRows addObject:buttonRow];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
_buttonRows = buttonRows;
|
|
158
|
+
[self setNeedsLayout];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Size the keys from the live keyboard width, which follows the window through
|
|
162
|
+
// rotation, fold and unfold, instead of the screen width at setup
|
|
163
|
+
- (void)layoutSubviews
|
|
164
|
+
{
|
|
165
|
+
[super layoutSubviews];
|
|
166
|
+
|
|
167
|
+
CGFloat buttonWidth = (_contentView.bounds.size.width - 4 * _separatorWidth) / 5;
|
|
168
|
+
CGFloat buttonHeight = (240 - _separatorWidth * 2 - 3 * _separatorWidth) / 4;
|
|
169
|
+
CGFloat yOffset = 0;
|
|
170
|
+
|
|
171
|
+
for (NSInteger rowIndex = 0; rowIndex < _buttonRows.count; rowIndex++) {
|
|
172
|
+
NSArray<UIButton *> *row = _buttonRows[rowIndex];
|
|
173
|
+
CGFloat xOffset = 0;
|
|
174
|
+
|
|
175
|
+
for (NSInteger colIndex = 0; colIndex < row.count; colIndex++) {
|
|
176
|
+
UIButton *button = row[colIndex];
|
|
177
|
+
NSString *key = button.accessibilityIdentifier;
|
|
178
|
+
BOOL isMainKey = (colIndex == 4 && rowIndex == 2);
|
|
156
179
|
|
|
180
|
+
CGRect buttonFrame = CGRectMake(xOffset, yOffset, buttonWidth, buttonHeight);
|
|
181
|
+
if (isMainKey) {
|
|
182
|
+
buttonFrame.size.height = buttonHeight * 2 + _separatorWidth;
|
|
183
|
+
}
|
|
184
|
+
if ([key isEqualToString:@"000"]) {
|
|
185
|
+
buttonFrame.size.width = buttonWidth * 2 + _separatorWidth;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
button.frame = buttonFrame;
|
|
157
189
|
xOffset += buttonFrame.size.width + _separatorWidth;
|
|
158
190
|
}
|
|
159
191
|
|
|
@@ -161,6 +193,32 @@
|
|
|
161
193
|
}
|
|
162
194
|
}
|
|
163
195
|
|
|
196
|
+
- (void)loadBackIconForButton:(UIButton *)button
|
|
197
|
+
{
|
|
198
|
+
if (sBackIcon) {
|
|
199
|
+
[button setImage:sBackIcon forState:UIControlStateNormal];
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
__weak UIButton *weakButton = button;
|
|
204
|
+
[[NSURLSession.sharedSession dataTaskWithURL:[NSURL URLWithString:kBackIconURL]
|
|
205
|
+
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
|
206
|
+
CGImageRef cgImage = [UIImage imageWithData:data].CGImage;
|
|
207
|
+
if (!cgImage) return;
|
|
208
|
+
|
|
209
|
+
// Scale makes the image size kBackIconSize points without redrawing it
|
|
210
|
+
UIImage *icon = [[UIImage imageWithCGImage:cgImage
|
|
211
|
+
scale:CGImageGetWidth(cgImage) / kBackIconSize
|
|
212
|
+
orientation:UIImageOrientationUp]
|
|
213
|
+
imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
|
214
|
+
|
|
215
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
216
|
+
sBackIcon = icon;
|
|
217
|
+
[weakButton setImage:icon forState:UIControlStateNormal];
|
|
218
|
+
});
|
|
219
|
+
}] resume];
|
|
220
|
+
}
|
|
221
|
+
|
|
164
222
|
- (void)updateCustomKeyTitle
|
|
165
223
|
{
|
|
166
224
|
if (!self.customKeyButton || !_customKeyText) {
|
package/ios/InputCalculator.mm
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
#import <React/RCTConversions.h>
|
|
5
5
|
#import <React/RCTFabricComponentsPlugins.h>
|
|
6
|
+
#import <React/RCTUtils.h>
|
|
6
7
|
#import <react/renderer/components/InputCalculatorSpec/InputCalculatorComponentDescriptor.h>
|
|
7
8
|
#import <react/renderer/components/InputCalculatorSpec/EventEmitters.h>
|
|
8
9
|
#import <react/renderer/components/InputCalculatorSpec/Props.h>
|
|
@@ -45,9 +46,13 @@ typedef struct {
|
|
|
45
46
|
_textField.delegate = (id<UITextFieldDelegate>)self;
|
|
46
47
|
|
|
47
48
|
// Create keyboard view
|
|
49
|
+
// UIKit sizes the input view to the keyboard width; the keys follow it in layoutSubviews
|
|
50
|
+
UIWindow *window = RCTKeyWindow();
|
|
51
|
+
CGFloat keyboardWidth = window ? window.bounds.size.width : UIScreen.mainScreen.bounds.size.width;
|
|
48
52
|
CGFloat bottomInset = [self getBottomInset];
|
|
49
|
-
CGRect keyboardFrame = CGRectMake(0, 0,
|
|
53
|
+
CGRect keyboardFrame = CGRectMake(0, 0, keyboardWidth, 240 + bottomInset);
|
|
50
54
|
_keyboardView = [[CalculatorKeyboardView alloc] initWithFrame:keyboardFrame];
|
|
55
|
+
_keyboardView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
|
51
56
|
_keyboardView.input = (id)self; // Bridge pattern
|
|
52
57
|
|
|
53
58
|
// Set custom keyboard
|
|
@@ -94,7 +99,8 @@ typedef struct {
|
|
|
94
99
|
|
|
95
100
|
- (CGFloat)getBottomInset
|
|
96
101
|
{
|
|
97
|
-
|
|
102
|
+
// key window of the active scene; windows.firstObject is unreliable with the UIScene life cycle
|
|
103
|
+
UIWindow *window = RCTKeyWindow();
|
|
98
104
|
CGFloat bottom = window.safeAreaInsets.bottom > 0 ? 21 : 0;
|
|
99
105
|
return bottom;
|
|
100
106
|
}
|