@momo-kits/native-kits 0.118.1-beta.3 → 0.120.0-beta.20
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/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +2 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +5 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +13 -6
- package/ios/Application/Components.swift +2 -2
- package/ios/Button/Button.swift +3 -3
- package/ios/Checkbox/Checkbox.swift +1 -2
- package/ios/Input/Input.swift +61 -64
- package/ios/Typography/Text.swift +140 -0
- package/local.properties +2 -2
- package/package.json +1 -1
|
@@ -96,7 +96,8 @@ val AppLanguage = staticCompositionLocalOf<String?> {
|
|
|
96
96
|
data class KitConfig(
|
|
97
97
|
val trustBanner: TrustBannerData? = null,
|
|
98
98
|
val headerBar: String? = null,
|
|
99
|
-
val headerGradient: String? = null
|
|
99
|
+
val headerGradient: String? = null,
|
|
100
|
+
val isWhiteList: Boolean = false
|
|
100
101
|
)
|
|
101
102
|
|
|
102
103
|
@Composable
|
|
@@ -36,7 +36,6 @@ import androidx.compose.ui.text.input.KeyboardType
|
|
|
36
36
|
import androidx.compose.ui.unit.dp
|
|
37
37
|
import androidx.compose.ui.zIndex
|
|
38
38
|
import vn.momo.kits.const.AppTheme
|
|
39
|
-
import vn.momo.kits.const.Colors
|
|
40
39
|
import vn.momo.kits.const.Radius
|
|
41
40
|
import vn.momo.kits.const.Spacing
|
|
42
41
|
import vn.momo.kits.const.Typography
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
package vn.momo.kits.components
|
|
2
2
|
|
|
3
|
+
import androidx.compose.foundation.border
|
|
4
|
+
import androidx.compose.foundation.layout.Box
|
|
3
5
|
import androidx.compose.runtime.Composable
|
|
4
6
|
import androidx.compose.ui.Modifier
|
|
5
7
|
import androidx.compose.ui.graphics.Color
|
|
@@ -8,8 +10,10 @@ import androidx.compose.ui.text.TextStyle
|
|
|
8
10
|
import androidx.compose.ui.text.style.TextAlign
|
|
9
11
|
import androidx.compose.ui.text.style.TextDecoration
|
|
10
12
|
import androidx.compose.ui.text.style.TextOverflow
|
|
13
|
+
import androidx.compose.ui.unit.dp
|
|
11
14
|
import androidx.compose.ui.unit.sp
|
|
12
15
|
import vn.momo.kits.const.AppTheme
|
|
16
|
+
import vn.momo.kits.const.Colors
|
|
13
17
|
import vn.momo.kits.const.Typography
|
|
14
18
|
import vn.momo.kits.const.getFontFamily
|
|
15
19
|
import vn.momo.kits.const.scaleSize
|
|
@@ -45,4 +49,5 @@ fun Text(
|
|
|
45
49
|
onTextLayout = onTextLayout,
|
|
46
50
|
overflow = overflow
|
|
47
51
|
)
|
|
52
|
+
|
|
48
53
|
}
|
|
@@ -12,32 +12,39 @@ import org.jetbrains.compose.resources.Font
|
|
|
12
12
|
import org.jetbrains.compose.resources.FontResource
|
|
13
13
|
import org.jetbrains.compose.resources.InternalResourceApi
|
|
14
14
|
import org.jetbrains.compose.resources.ResourceItem
|
|
15
|
+
import vn.momo.kits.application.AppConfig
|
|
16
|
+
import vn.momo.kits.application.KitConfig
|
|
15
17
|
import vn.momo.kits.platform.getScreenDimensions
|
|
18
|
+
import kotlin.math.max
|
|
16
19
|
import kotlin.math.min
|
|
17
20
|
|
|
18
21
|
const val DEFAULT_SCREEN_SIZE = 375f
|
|
19
|
-
const val MAX_FONT_SCALE = 1.
|
|
22
|
+
const val MAX_FONT_SCALE = 1.5f
|
|
20
23
|
const val MAX_DEVICE_SCALE = 5
|
|
21
24
|
|
|
22
25
|
@Composable
|
|
23
|
-
fun scaleSize(size: Float): Float {
|
|
26
|
+
fun scaleSize(size: Float, config: KitConfig? = AppConfig.current): Float {
|
|
27
|
+
val isWhiteList = config?.isWhiteList ?: false
|
|
24
28
|
val deviceWidth = getScreenDimensions().width
|
|
25
29
|
val deviceScale = deviceWidth / DEFAULT_SCREEN_SIZE
|
|
26
30
|
|
|
27
31
|
val density = LocalDensity.current
|
|
28
32
|
val fontScale = density.fontScale
|
|
29
33
|
|
|
30
|
-
|
|
34
|
+
val defaultFontSize = size / fontScale //To ignore OS scale
|
|
35
|
+
var fontSizeScaleDevice = defaultFontSize
|
|
36
|
+
var fontSizeScaleOS = defaultFontSize
|
|
31
37
|
|
|
32
38
|
if (deviceScale > 1) {
|
|
33
|
-
|
|
39
|
+
fontSizeScaleDevice =
|
|
40
|
+
min(deviceScale * fontSizeScaleDevice, fontSizeScaleDevice + MAX_DEVICE_SCALE)
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
if (fontScale > 1) {
|
|
37
|
-
|
|
44
|
+
fontSizeScaleOS = min(fontScale * fontSizeScaleOS, fontSizeScaleOS * MAX_FONT_SCALE)
|
|
38
45
|
}
|
|
39
46
|
|
|
40
|
-
return
|
|
47
|
+
return if (isWhiteList) max(fontSizeScaleDevice, fontSizeScaleOS) else fontSizeScaleDevice
|
|
41
48
|
}
|
|
42
49
|
|
|
43
50
|
@OptIn(InternalResourceApi::class)
|
|
@@ -214,8 +214,8 @@ public struct Header: View {
|
|
|
214
214
|
}
|
|
215
215
|
} else {
|
|
216
216
|
Text(title)
|
|
217
|
-
.font(Font.system(size: 17).weight(.bold))
|
|
218
|
-
.lineSpacing(22)
|
|
217
|
+
.font(Font.system(size: scaleSize(17)).weight(.bold))
|
|
218
|
+
.lineSpacing(scaleSize(22))
|
|
219
219
|
.foregroundColor(tintColor)
|
|
220
220
|
.frame(maxWidth: titlePosition == .center ? UIScreen.main.bounds.width - 252 : nil)
|
|
221
221
|
.frame(maxWidth: titlePosition == .center ? .infinity : UIScreen.main.bounds.width - 172, alignment: titlePosition == .center ? .center : .leading)
|
package/ios/Button/Button.swift
CHANGED
|
@@ -62,11 +62,11 @@ public struct Button: View {
|
|
|
62
62
|
func renderTitle(title: String, type: ButtonType, size: ButtonSize) -> Text {
|
|
63
63
|
switch size {
|
|
64
64
|
case .large:
|
|
65
|
-
return Text(title).font(.system(size: 16, weight: Font.Weight.bold))
|
|
65
|
+
return Text(title).font(.system(size: scaleSize(16), weight: Font.Weight.bold))
|
|
66
66
|
case .medium:
|
|
67
|
-
return Text(title).font(.system(size: 14, weight: Font.Weight.bold))
|
|
67
|
+
return Text(title).font(.system(size: scaleSize(14), weight: Font.Weight.bold))
|
|
68
68
|
case .small:
|
|
69
|
-
return Text(title).font(.system(size: 12, weight: Font.Weight.bold))
|
|
69
|
+
return Text(title).font(.system(size: scaleSize(12), weight: Font.Weight.bold))
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
}
|
package/ios/Input/Input.swift
CHANGED
|
@@ -5,6 +5,7 @@ import SwiftUI
|
|
|
5
5
|
fileprivate struct FloatingComponent: View {
|
|
6
6
|
var floatingValue: String
|
|
7
7
|
var floatingIcon: AnyView?
|
|
8
|
+
var disabled: Bool = false
|
|
8
9
|
|
|
9
10
|
public init (_ floatingValue: String = "", floatingIcon: AnyView? = nil){
|
|
10
11
|
self.floatingValue = floatingValue
|
|
@@ -13,12 +14,9 @@ fileprivate struct FloatingComponent: View {
|
|
|
13
14
|
|
|
14
15
|
public var body: some View {
|
|
15
16
|
return HStack {
|
|
16
|
-
|
|
17
|
-
.
|
|
18
|
-
.
|
|
19
|
-
.font(.system(size: 12))
|
|
20
|
-
.padding(.leading, 8)
|
|
21
|
-
.padding(.trailing, 8)
|
|
17
|
+
MomoText(floatingValue, typography: .labelSMedium, color: self.disabled ? Colors.black12 : Colors.black08)
|
|
18
|
+
.padding(.leading, Spacing.XS)
|
|
19
|
+
.padding(.trailing, Spacing.XS)
|
|
22
20
|
floatingIcon
|
|
23
21
|
}
|
|
24
22
|
.background(Colors.black01)
|
|
@@ -113,84 +111,83 @@ public struct Input: View {
|
|
|
113
111
|
}
|
|
114
112
|
|
|
115
113
|
public var body: some View {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if !isSecure {
|
|
124
|
-
TextField("", text: $value, onEditingChanged: { isChange in
|
|
125
|
-
self.focused = isChange
|
|
126
|
-
})
|
|
127
|
-
.disabled(disabled)
|
|
128
|
-
.font(fontWeight == .bold ? .headline : .body)
|
|
129
|
-
} else {
|
|
130
|
-
DotTextField(value: $value)
|
|
131
|
-
.disabled(disabled)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if loading {
|
|
135
|
-
ActivityIndicator(isAnimating: .constant(true), style: .medium)
|
|
136
|
-
.frame(width: 20, height: 20)
|
|
137
|
-
} else if !value.isEmpty {
|
|
138
|
-
SwiftButton(action: onCancel){
|
|
139
|
-
Image(systemName: "x.circle.fill")
|
|
140
|
-
.foregroundColor(Colors.text)
|
|
141
|
-
.frame(width: 16, height: 16)
|
|
142
|
-
.padding(.leading, -4)
|
|
143
|
-
.padding(.trailing, 8)
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
rightIcon
|
|
114
|
+
VStack(alignment: .leading, spacing: 4) {
|
|
115
|
+
ZStack(alignment: .topLeading) {
|
|
116
|
+
HStack(alignment: .center) {
|
|
117
|
+
if let leadingIcon = leadingIcon {
|
|
118
|
+
Icon(source: leadingIcon, size: 16, color: leadingIconColor)
|
|
148
119
|
}
|
|
149
|
-
.padding(.horizontal, 12)
|
|
150
|
-
.frame(height: size == .small ? 40 : 48)
|
|
151
|
-
.background(disabled ? Colors.black01 : Color.white)
|
|
152
|
-
.overlay(RoundedRectangle(cornerRadius: 8).stroke(focused ? Colors.pink03 : Colors.border, lineWidth: 1))
|
|
153
120
|
|
|
154
|
-
if
|
|
155
|
-
|
|
156
|
-
.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
121
|
+
if !isSecure {
|
|
122
|
+
TextField("", text: $value ,onEditingChanged: { isChange in
|
|
123
|
+
self.focused = isChange
|
|
124
|
+
})
|
|
125
|
+
.disabled(disabled)
|
|
126
|
+
.frame(maxHeight: .infinity)
|
|
127
|
+
.multilineTextAlignment(.leading)
|
|
128
|
+
.font(.system(size: scaleSize(14)))
|
|
129
|
+
} else {
|
|
130
|
+
DotTextField(value: $value)
|
|
131
|
+
.disabled(disabled)
|
|
160
132
|
}
|
|
161
133
|
|
|
162
|
-
if
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
134
|
+
if loading {
|
|
135
|
+
ActivityIndicator(isAnimating: .constant(true), style: .medium)
|
|
136
|
+
.frame(width: 20, height: 20)
|
|
137
|
+
} else if !value.isEmpty {
|
|
138
|
+
SwiftButton(action: onCancel){
|
|
139
|
+
Image(systemName: "x.circle.fill")
|
|
140
|
+
.foregroundColor(Colors.text)
|
|
141
|
+
.frame(width: 16, height: 16)
|
|
142
|
+
.padding(.leading, -4)
|
|
143
|
+
.padding(.trailing, 8)
|
|
144
|
+
}
|
|
169
145
|
}
|
|
146
|
+
|
|
147
|
+
rightIcon
|
|
170
148
|
}
|
|
149
|
+
.padding(.horizontal, 12)
|
|
150
|
+
.frame(height: size == .small ? scaleSize(40) : scaleSize(48))
|
|
151
|
+
.background(disabled ? Colors.black01 : Color.white)
|
|
152
|
+
.overlay(RoundedRectangle(cornerRadius: 8).stroke(focused ? Colors.pink03 : Colors.border, lineWidth: 1))
|
|
171
153
|
|
|
172
|
-
if
|
|
173
|
-
Text(
|
|
174
|
-
.
|
|
175
|
-
.font(.caption)
|
|
176
|
-
} else if let hint = hintText {
|
|
177
|
-
Text(hint)
|
|
154
|
+
if value.isEmpty {
|
|
155
|
+
Text(placeholder + (required ? " *" : ""))
|
|
156
|
+
.font(.system(size: scaleSize(14)))
|
|
178
157
|
.foregroundColor(Colors.black09)
|
|
179
|
-
.
|
|
158
|
+
.padding(.horizontal, leadingIcon == nil ? 12 : 32)
|
|
159
|
+
.padding(.top, 12)
|
|
160
|
+
.allowsHitTesting(false)
|
|
180
161
|
}
|
|
162
|
+
|
|
163
|
+
if !floatingValue.isEmpty {
|
|
164
|
+
FloatingComponent(floatingValue, floatingIcon: floatingIconURL.map { url in
|
|
165
|
+
AnyView(
|
|
166
|
+
Icon(source: url, size: 16, color: floatingIconColor)
|
|
167
|
+
)
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if !errorMessage.isEmpty {
|
|
174
|
+
MomoText(errorMessage, typography: .descriptionDefaultRegular, color: Colors.red03)
|
|
175
|
+
} else if hintText != nil {
|
|
176
|
+
MomoText(errorMessage, typography: .descriptionDefaultRegular, color: Colors.black12)
|
|
181
177
|
}
|
|
182
178
|
}
|
|
179
|
+
}
|
|
183
180
|
}
|
|
184
181
|
|
|
185
182
|
|
|
186
183
|
struct ActivityIndicator: UIViewRepresentable {
|
|
187
184
|
@Binding var isAnimating: Bool
|
|
188
185
|
let style: UIActivityIndicatorView.Style
|
|
189
|
-
|
|
186
|
+
|
|
190
187
|
func makeUIView(context: UIViewRepresentableContext<ActivityIndicator>) -> UIActivityIndicatorView {
|
|
191
188
|
return UIActivityIndicatorView(style: style)
|
|
192
189
|
}
|
|
193
|
-
|
|
190
|
+
|
|
194
191
|
func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext<ActivityIndicator>) {
|
|
195
192
|
isAnimating ? uiView.startAnimating() : uiView.stopAnimating()
|
|
196
193
|
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
public func scaleSize(_ size: CGFloat) -> CGFloat {
|
|
4
|
+
let defaultScreenSize: CGFloat = 375
|
|
5
|
+
let maxFontScale: CGFloat = 1.5
|
|
6
|
+
let maxDeviceScale: CGFloat = 5
|
|
7
|
+
|
|
8
|
+
let deviceWidth = UIScreen.main.bounds.width
|
|
9
|
+
let deviceScale = deviceWidth / defaultScreenSize
|
|
10
|
+
|
|
11
|
+
let defaultFont = UIFont.systemFont(ofSize: UIFont.labelFontSize)
|
|
12
|
+
let scaledFont = UIFontMetrics.default.scaledFont(for: defaultFont)
|
|
13
|
+
let fontScale = scaledFont.pointSize / defaultFont.pointSize
|
|
14
|
+
|
|
15
|
+
var fontSizeDeviceScale = size
|
|
16
|
+
var fontSizeOSScale = size
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
if deviceScale > 1 {
|
|
20
|
+
fontSizeDeviceScale = min(fontSizeDeviceScale * deviceScale, fontSizeDeviceScale + maxDeviceScale)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if fontScale > 1 {
|
|
24
|
+
fontSizeOSScale = min(fontSizeOSScale * fontScale, fontSizeOSScale * maxFontScale)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return max(fontSizeDeviceScale, fontSizeOSScale)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public enum TypographyStyle {
|
|
31
|
+
case headlineDefaultBold
|
|
32
|
+
case headerMBold
|
|
33
|
+
case headerDefaultBold
|
|
34
|
+
case headerSSemibold
|
|
35
|
+
case headerXsSemibold
|
|
36
|
+
case bodyDefaultRegular
|
|
37
|
+
case bodyDefaultRegularStrikethrough
|
|
38
|
+
case descriptionDefaultRegular
|
|
39
|
+
case descriptionDefaultRegularStrikethrough
|
|
40
|
+
case descriptionXsRegular
|
|
41
|
+
case descriptionXsRegularStrikethrough
|
|
42
|
+
case labelDefaultMedium
|
|
43
|
+
case labelSMedium
|
|
44
|
+
case labelXsMedium
|
|
45
|
+
case actionDefaultBold
|
|
46
|
+
case actionSBold
|
|
47
|
+
case actionXsBold
|
|
48
|
+
case actionXxsBold
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public extension TypographyStyle {
|
|
52
|
+
var fontSize: CGFloat {
|
|
53
|
+
switch self {
|
|
54
|
+
case .headlineDefaultBold: return 24
|
|
55
|
+
case .headerMBold: return 18
|
|
56
|
+
case .headerDefaultBold: return 16
|
|
57
|
+
case .headerSSemibold: return 14
|
|
58
|
+
case .headerXsSemibold: return 12
|
|
59
|
+
case .bodyDefaultRegular, .bodyDefaultRegularStrikethrough: return 14
|
|
60
|
+
case .descriptionDefaultRegular, .descriptionDefaultRegularStrikethrough: return 12
|
|
61
|
+
case .descriptionXsRegular, .descriptionXsRegularStrikethrough: return 10
|
|
62
|
+
case .labelDefaultMedium: return 14
|
|
63
|
+
case .labelSMedium: return 12
|
|
64
|
+
case .labelXsMedium: return 10
|
|
65
|
+
case .actionDefaultBold: return 16
|
|
66
|
+
case .actionSBold: return 14
|
|
67
|
+
case .actionXsBold: return 12
|
|
68
|
+
case .actionXxsBold: return 10
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
var lineHeight: CGFloat {
|
|
72
|
+
switch self {
|
|
73
|
+
case .headlineDefaultBold: return 34
|
|
74
|
+
case .headerMBold: return 26
|
|
75
|
+
case .headerDefaultBold: return 22
|
|
76
|
+
case .headerSSemibold: return 20
|
|
77
|
+
case .headerXsSemibold: return 18
|
|
78
|
+
case .bodyDefaultRegular, .bodyDefaultRegularStrikethrough: return 20
|
|
79
|
+
case .descriptionDefaultRegular, .descriptionDefaultRegularStrikethrough: return 18
|
|
80
|
+
case .descriptionXsRegular, .descriptionXsRegularStrikethrough: return 14
|
|
81
|
+
case .labelDefaultMedium: return 20
|
|
82
|
+
case .labelSMedium: return 18
|
|
83
|
+
case .labelXsMedium: return 14
|
|
84
|
+
case .actionDefaultBold: return 22
|
|
85
|
+
case .actionSBold: return 20
|
|
86
|
+
case .actionXsBold: return 18
|
|
87
|
+
case .actionXxsBold: return 14
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
var fontWeight: Font.Weight {
|
|
91
|
+
switch self {
|
|
92
|
+
case .headlineDefaultBold, .headerMBold, .headerDefaultBold,
|
|
93
|
+
.actionDefaultBold, .actionSBold, .actionXsBold, .actionXxsBold:
|
|
94
|
+
return .bold
|
|
95
|
+
case .headerSSemibold, .headerXsSemibold:
|
|
96
|
+
return .semibold
|
|
97
|
+
case .bodyDefaultRegular, .bodyDefaultRegularStrikethrough,
|
|
98
|
+
.descriptionDefaultRegular, .descriptionDefaultRegularStrikethrough,
|
|
99
|
+
.descriptionXsRegular, .descriptionXsRegularStrikethrough:
|
|
100
|
+
return .regular
|
|
101
|
+
case .labelDefaultMedium, .labelSMedium, .labelXsMedium:
|
|
102
|
+
return .medium
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
var isStrikethrough: Bool {
|
|
106
|
+
switch self {
|
|
107
|
+
case .bodyDefaultRegularStrikethrough,
|
|
108
|
+
.descriptionDefaultRegularStrikethrough,
|
|
109
|
+
.descriptionXsRegularStrikethrough:
|
|
110
|
+
return true
|
|
111
|
+
default:
|
|
112
|
+
return false
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public struct MomoText: View {
|
|
118
|
+
private let content: String
|
|
119
|
+
private let typography: TypographyStyle
|
|
120
|
+
private let color: Color
|
|
121
|
+
|
|
122
|
+
public init(
|
|
123
|
+
_ content: String,
|
|
124
|
+
typography: TypographyStyle = .bodyDefaultRegular,
|
|
125
|
+
color: Color = Colors.text
|
|
126
|
+
) {
|
|
127
|
+
self.content = content
|
|
128
|
+
self.typography = typography
|
|
129
|
+
self.color = color
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public var body: some View {
|
|
133
|
+
let text = SwiftUI.Text(content)
|
|
134
|
+
.font(.system(size: scaleSize(typography.fontSize), weight: typography.fontWeight))
|
|
135
|
+
.foregroundColor(color)
|
|
136
|
+
.lineSpacing(scaleSize(typography.lineHeight) - scaleSize(typography.fontSize))
|
|
137
|
+
|
|
138
|
+
return text
|
|
139
|
+
}
|
|
140
|
+
}
|
package/local.properties
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
# Location of the SDK. This is only used by Gradle.
|
|
5
5
|
# For customization when using a Version Control System, please read the
|
|
6
6
|
# header note.
|
|
7
|
-
#
|
|
8
|
-
sdk.dir=/Users/
|
|
7
|
+
#Tue Apr 08 11:13:31 ICT 2025
|
|
8
|
+
sdk.dir=/Users/sonnguyen/Library/Android/sdk
|