@momo-kits/calculator-keyboard 0.167.1-beta.88 → 0.167.1-duo.12

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,12 +3,9 @@ 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
8
6
  import android.graphics.Color
9
7
  import android.graphics.drawable.GradientDrawable
10
8
  import android.text.Editable
11
- import android.util.DisplayMetrics
12
9
  import android.view.Gravity
13
10
  import android.view.MotionEvent
14
11
  import android.widget.Button
@@ -20,8 +17,6 @@ import androidx.core.graphics.toColorInt
20
17
  import com.momokits.calculatorkeyboard.native.utils.CalculatorEngine
21
18
  import com.momokits.calculatorkeyboard.native.utils.CalculatorFormatter
22
19
  import com.momokits.calculatorkeyboard.R
23
- import java.net.URL
24
- import kotlin.concurrent.thread
25
20
 
26
21
  @SuppressLint("SetTextI18n", "ViewConstructor")
27
22
  class CustomKeyboardView(
@@ -186,26 +181,6 @@ class CustomKeyboardView(
186
181
  setImageTintList(ColorStateList.valueOf(Color.BLACK))
187
182
  setOnClickListener { onKeyPress(key, false) }
188
183
  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) }
209
184
  }
210
185
  }
211
186
 
@@ -425,13 +400,4 @@ class CustomKeyboardView(
425
400
  }
426
401
  customKeyButton?.setTextColor(textColor)
427
402
  }
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
- }
437
403
  }
@@ -1,10 +1,6 @@
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
-
8
4
  @interface CalculatorKeyboardView ()
9
5
  @property (nonatomic, strong) NSArray<NSArray<NSString *> *> *numWithCTAKeys;
10
6
  @property (nonatomic, strong) NSArray<NSArray<NSString *> *> *defaultKeys;
@@ -14,6 +10,8 @@ static UIImage *sBackIcon;
14
10
 
15
11
  @implementation CalculatorKeyboardView {
16
12
  CGFloat _separatorWidth;
13
+ UIView *_contentView;
14
+ NSArray<NSArray<UIButton *> *> *_buttonRows;
17
15
  }
18
16
 
19
17
  - (instancetype)initWithFrame:(CGRect)frame
@@ -89,28 +87,26 @@ static UIImage *sBackIcon;
89
87
 
90
88
  self.backgroundColor = [Utils colorFromHex:@"#f2f2f6"];
91
89
 
92
- CGFloat buttonWidth = (UIScreen.mainScreen.bounds.size.width - _separatorWidth * 2 - 4 * _separatorWidth) / 5;
93
- CGFloat buttonHeight = (240 - _separatorWidth * 2 - 3 * _separatorWidth) / 4;
94
-
95
90
  // Create content view
96
91
  UIView *contentView = [[UIView alloc] init];
97
92
  contentView.translatesAutoresizingMaskIntoConstraints = NO;
98
93
  [self addSubview:contentView];
94
+ _contentView = contentView;
99
95
 
100
- // Set contentView constraints
96
+ // Set contentView constraints, keeping clear of the side safe area (landscape, iPhone Duo)
101
97
  [NSLayoutConstraint activateConstraints:@[
102
- [contentView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:_separatorWidth],
103
- [contentView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-_separatorWidth],
98
+ [contentView.leadingAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.leadingAnchor constant:_separatorWidth],
99
+ [contentView.trailingAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.trailingAnchor constant:-_separatorWidth],
104
100
  [contentView.topAnchor constraintEqualToAnchor:self.topAnchor constant:_separatorWidth],
105
101
  [contentView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-_separatorWidth]
106
102
  ]];
107
103
 
108
- CGFloat yOffset = 0;
104
+ NSMutableArray<NSArray<UIButton *> *> *buttonRows = [NSMutableArray array];
109
105
  NSArray<NSArray<NSString *> *> *keys = [_keyboardMode isEqualToString:@"NumDefault"] ? _defaultKeys : _numWithCTAKeys;
110
106
 
111
107
  for (NSInteger rowIndex = 0; rowIndex < keys.count; rowIndex++) {
112
108
  NSArray<NSString *> *row = keys[rowIndex];
113
- CGFloat xOffset = 0;
109
+ NSMutableArray<UIButton *> *buttonRow = [NSMutableArray array];
114
110
 
115
111
  for (NSInteger colIndex = 0; colIndex < row.count; colIndex++) {
116
112
  NSString *key = row[colIndex];
@@ -128,23 +124,12 @@ static UIImage *sBackIcon;
128
124
  button.accessibilityIdentifier = key;
129
125
  button.tag = isMainCTAKey ? 1 : 0;
130
126
 
131
- CGRect buttonFrame = CGRectMake(xOffset, yOffset, buttonWidth, buttonHeight);
132
- if (isMainKey) {
133
- buttonFrame.size.height = buttonHeight * 2 + _separatorWidth;
134
- }
135
- if ([key isEqualToString:@"000"]) {
136
- buttonFrame.size.width = buttonWidth * 2 + _separatorWidth;
137
- }
138
-
139
- button.frame = buttonFrame;
140
-
141
127
  if ([key isEqualToString:@"back"]) {
142
128
  [button setTitle:@"" forState:UIControlStateNormal];
143
129
  UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:24 weight:UIImageSymbolWeightBold];
144
130
  UIImage *image = [UIImage systemImageNamed:@"delete.backward" withConfiguration:config];
145
131
  [button setImage:image forState:UIControlStateNormal];
146
132
  button.tintColor = UIColor.blackColor;
147
- [self loadBackIconForButton:button];
148
133
  }
149
134
 
150
135
  if ([_specialKeys containsObject:key] || isMainKey) {
@@ -158,38 +143,49 @@ static UIImage *sBackIcon;
158
143
 
159
144
  [button addTarget:self action:@selector(keyPressed:) forControlEvents:UIControlEventTouchUpInside];
160
145
  [contentView addSubview:button];
161
-
162
- xOffset += buttonFrame.size.width + _separatorWidth;
146
+ [buttonRow addObject:button];
163
147
  }
164
148
 
165
- yOffset += buttonHeight + _separatorWidth;
149
+ [buttonRows addObject:buttonRow];
166
150
  }
151
+
152
+ _buttonRows = buttonRows;
153
+ [self setNeedsLayout];
167
154
  }
168
155
 
169
- - (void)loadBackIconForButton:(UIButton *)button
156
+ // Size the keys from the live keyboard width, which follows the window through
157
+ // rotation, fold and unfold, instead of the screen width at setup
158
+ - (void)layoutSubviews
170
159
  {
171
- if (sBackIcon) {
172
- [button setImage:sBackIcon forState:UIControlStateNormal];
173
- return;
160
+ [super layoutSubviews];
161
+
162
+ CGFloat buttonWidth = (_contentView.bounds.size.width - 4 * _separatorWidth) / 5;
163
+ CGFloat buttonHeight = (240 - _separatorWidth * 2 - 3 * _separatorWidth) / 4;
164
+ CGFloat yOffset = 0;
165
+
166
+ for (NSInteger rowIndex = 0; rowIndex < _buttonRows.count; rowIndex++) {
167
+ NSArray<UIButton *> *row = _buttonRows[rowIndex];
168
+ CGFloat xOffset = 0;
169
+
170
+ for (NSInteger colIndex = 0; colIndex < row.count; colIndex++) {
171
+ UIButton *button = row[colIndex];
172
+ NSString *key = button.accessibilityIdentifier;
173
+ BOOL isMainKey = (colIndex == 4 && rowIndex == 2);
174
+
175
+ CGRect buttonFrame = CGRectMake(xOffset, yOffset, buttonWidth, buttonHeight);
176
+ if (isMainKey) {
177
+ buttonFrame.size.height = buttonHeight * 2 + _separatorWidth;
178
+ }
179
+ if ([key isEqualToString:@"000"]) {
180
+ buttonFrame.size.width = buttonWidth * 2 + _separatorWidth;
181
+ }
182
+
183
+ button.frame = buttonFrame;
184
+ xOffset += buttonFrame.size.width + _separatorWidth;
185
+ }
186
+
187
+ yOffset += buttonHeight + _separatorWidth;
174
188
  }
175
-
176
- __weak UIButton *weakButton = button;
177
- [[NSURLSession.sharedSession dataTaskWithURL:[NSURL URLWithString:kBackIconURL]
178
- completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
179
- CGImageRef cgImage = [UIImage imageWithData:data].CGImage;
180
- if (!cgImage) return;
181
-
182
- // Scale makes the image size kBackIconSize points without redrawing it
183
- UIImage *icon = [[UIImage imageWithCGImage:cgImage
184
- scale:CGImageGetWidth(cgImage) / kBackIconSize
185
- orientation:UIImageOrientationUp]
186
- imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
187
-
188
- dispatch_async(dispatch_get_main_queue(), ^{
189
- sBackIcon = icon;
190
- [weakButton setImage:icon forState:UIControlStateNormal];
191
- });
192
- }] resume];
193
189
  }
194
190
 
195
191
  - (void)updateCustomKeyTitle
@@ -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, UIScreen.mainScreen.bounds.size.width, 240 + bottomInset);
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
- UIWindow *window = UIApplication.sharedApplication.windows.firstObject;
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/calculator-keyboard",
3
- "version": "0.167.1-beta.88",
3
+ "version": "0.167.1-duo.12",
4
4
  "description": "react native calculator keyboard",
5
5
  "main": "./src/index.tsx",
6
6
  "files": [