@24i/smartapps-bigscreen-rnv-build-engine 0.17.2 → 0.17.3-alpha.1

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.
Files changed (88) hide show
  1. package/dist/src/__tests__/resolvers/resolveBuildFileBaseName.spec.js +80 -0
  2. package/dist/src/resolvers/index.js +3 -1
  3. package/dist/src/resolvers/resolveBuildFileBaseName.js +34 -0
  4. package/dist/src/resolvers/resolveBuildFileName.js +3 -27
  5. package/dist/src/scripts/releaseAllWithScheme.js +1 -1
  6. package/dist/src/sdks/index.js +5 -1
  7. package/dist/src/sdks/sdk-androidtv/index.js +17 -0
  8. package/dist/src/sdks/sdk-androidtv/sdkAndroidtv.js +264 -0
  9. package/dist/src/sdks/sdk-utils/sdk-installer/services/unzip.js +3 -2
  10. package/dist/src/tasks/task.rnv.build.js +3 -1
  11. package/dist/src/tasks/task.rnv.configure.js +3 -1
  12. package/dist/src/tasks/task.rnv.platform.configure.js +4 -1
  13. package/dist/templates/platforms/_shared/template.js +3 -3
  14. package/dist/templates/platforms/androidtv/project/README.md +196 -0
  15. package/dist/templates/platforms/androidtv/project/amazoniap/build.gradle +37 -0
  16. package/dist/templates/platforms/androidtv/project/amazoniap/consumer-rules.pro +0 -0
  17. package/dist/templates/platforms/androidtv/project/amazoniap/libs/in-app-purchasing-2.0.76.jar +0 -0
  18. package/dist/templates/platforms/androidtv/project/amazoniap/proguard-rules.pro +21 -0
  19. package/dist/templates/platforms/androidtv/project/amazoniap/src/main/AndroidManifest.xml +4 -0
  20. package/dist/templates/platforms/androidtv/project/amazoniap/src/main/java/com/twentyfouri/bridgetv/amazoniap/AmazonIapSevice.kt +106 -0
  21. package/dist/templates/platforms/androidtv/project/app/build.gradle +112 -0
  22. package/dist/templates/platforms/androidtv/project/app/proguard-rules.pro +32 -0
  23. package/dist/templates/platforms/androidtv/project/app/src/bigscreenAppstage/java/com/twentyfouri/tvbridge/Flavor.kt +5 -0
  24. package/dist/templates/platforms/androidtv/project/app/src/bigscreenAppstage/res/raw/configuration.json +9 -0
  25. package/dist/templates/platforms/androidtv/project/app/src/main/AndroidManifest.xml +53 -0
  26. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/FlavorBase.kt +12 -0
  27. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/bridge/BridgeFromJavaScript.kt +392 -0
  28. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/bridge/BridgeToJavaScript.kt +89 -0
  29. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/configuration/BridgeConfig.kt +16 -0
  30. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/configuration/ConfigCallback.kt +6 -0
  31. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/configuration/ConfigProvider.kt +27 -0
  32. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/configuration/Drm.kt +10 -0
  33. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/configuration/UserAgent.kt +6 -0
  34. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/BaseApplication.kt +73 -0
  35. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/di/ActivityComponent.kt +13 -0
  36. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/di/ActivityModule.kt +17 -0
  37. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/di/ApplicationComponent.kt +26 -0
  38. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/di/ApplicationModule.kt +51 -0
  39. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/di/PerActivity.kt +26 -0
  40. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/LocalStorage.kt +30 -0
  41. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/LoggingDrmCallback.kt +38 -0
  42. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/OverriddenHttpMediaDrmCallback.kt +134 -0
  43. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/PlayerFactoryExoPlayerCustomized.kt +84 -0
  44. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/SimpleActivityLifecycleCallback.kt +32 -0
  45. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/global/utils/SourceDrmOverridden.kt +49 -0
  46. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/jump/JumpAnalytics.kt +211 -0
  47. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/sound/TouchPlayer.kt +86 -0
  48. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/client/BridgeWebView.kt +82 -0
  49. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/client/BridgeWebViewClient.kt +114 -0
  50. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/model/AnalyticsPlaybackInfo.kt +10 -0
  51. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/model/ContentModel.kt +14 -0
  52. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/model/DrmModel.kt +20 -0
  53. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/model/MediaEventType.kt +20 -0
  54. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/parser/ContentModelParser.kt +16 -0
  55. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/parser/DeviceInfoParser.kt +22 -0
  56. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/parser/PlaybackInfoParser.kt +10 -0
  57. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/parser/TrackJsonModelParser.kt +58 -0
  58. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/view/Mainview.kt +20 -0
  59. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/view/PlayerCache.kt +15 -0
  60. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/view/WebViewActivity.kt +306 -0
  61. package/dist/templates/platforms/androidtv/project/app/src/main/java/com/twentyfouri/tvbridge/webview/viewmodel/WebViewModel.kt +609 -0
  62. package/dist/templates/platforms/androidtv/project/app/src/main/res/drawable/button_background_focus.xml +10 -0
  63. package/dist/templates/platforms/androidtv/project/app/src/main/res/drawable/button_background_normal.xml +7 -0
  64. package/dist/templates/platforms/androidtv/project/app/src/main/res/drawable/button_background_pressed.xml +7 -0
  65. package/dist/templates/platforms/androidtv/project/app/src/main/res/drawable/button_focus.xml +8 -0
  66. package/dist/templates/platforms/androidtv/project/app/src/main/res/layout/activity_web_view.xml +76 -0
  67. package/dist/templates/platforms/androidtv/project/app/src/main/res/raw/click.ogg +0 -0
  68. package/dist/templates/platforms/androidtv/project/app/src/main/res/values/styles.xml +12 -0
  69. package/dist/templates/platforms/androidtv/project/app/src/main/res/xml/network_security_config.xml +4 -0
  70. package/dist/templates/platforms/androidtv/project/build.gradle +48 -0
  71. package/dist/templates/platforms/androidtv/project/common/build.gradle +36 -0
  72. package/dist/templates/platforms/androidtv/project/common/consumer-rules.pro +0 -0
  73. package/dist/templates/platforms/androidtv/project/common/proguard-rules.pro +21 -0
  74. package/dist/templates/platforms/androidtv/project/common/src/main/AndroidManifest.xml +4 -0
  75. package/dist/templates/platforms/androidtv/project/common/src/main/java/com/twentyfouri/tvbridge/common/IapService.kt +114 -0
  76. package/dist/templates/platforms/androidtv/project/googleiap/build.gradle +38 -0
  77. package/dist/templates/platforms/androidtv/project/googleiap/consumer-rules.pro +0 -0
  78. package/dist/templates/platforms/androidtv/project/googleiap/proguard-rules.pro +21 -0
  79. package/dist/templates/platforms/androidtv/project/googleiap/src/main/AndroidManifest.xml +6 -0
  80. package/dist/templates/platforms/androidtv/project/googleiap/src/main/java/com/twentyfouri/tvbridge/googleiap/GoogleIapService.kt +29 -0
  81. package/dist/templates/platforms/androidtv/project/gradle/wrapper/gradle-wrapper.jar +0 -0
  82. package/dist/templates/platforms/androidtv/project/gradle/wrapper/gradle-wrapper.properties +6 -0
  83. package/dist/templates/platforms/androidtv/project/gradle.properties +4 -0
  84. package/dist/templates/platforms/androidtv/project/gradlew +185 -0
  85. package/dist/templates/platforms/androidtv/project/gradlew.bat +104 -0
  86. package/dist/templates/platforms/androidtv/project/proguard-rules.pro +42 -0
  87. package/dist/templates/platforms/androidtv/project/settings.gradle +4 -0
  88. package/package.json +3 -2
@@ -0,0 +1,306 @@
1
+ package com.twentyfouri.tvbridge.webview.view
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.app.Activity
5
+ import android.content.Intent
6
+ import android.graphics.Bitmap
7
+ import android.graphics.Canvas
8
+ import android.graphics.Point
9
+ import android.media.AudioManager
10
+ import android.media.AudioManager.OnAudioFocusChangeListener
11
+ import android.os.Bundle
12
+ import android.text.Editable
13
+ import android.text.TextWatcher
14
+ import android.util.Log
15
+ import android.view.KeyEvent
16
+ import android.view.View
17
+ import android.view.WindowManager
18
+ import android.view.inputmethod.InputMethodManager
19
+ import android.webkit.WebChromeClient
20
+ import android.webkit.WebView
21
+ import android.widget.RelativeLayout
22
+ import android.widget.Toast
23
+ import androidx.appcompat.app.AppCompatActivity
24
+ import androidx.databinding.DataBindingUtil
25
+ import androidx.media.AudioAttributesCompat
26
+ import androidx.media.AudioFocusRequestCompat
27
+ import androidx.media.AudioManagerCompat
28
+ import com.twentyfouri.tvbridge.BuildConfig
29
+ import com.twentyfouri.tvbridge.R
30
+ import com.twentyfouri.tvbridge.configuration.BridgeConfig
31
+ import com.twentyfouri.tvbridge.databinding.ActivityWebViewBinding
32
+ import com.twentyfouri.tvbridge.global.BaseApplication
33
+ import com.twentyfouri.tvbridge.global.di.ActivityComponent
34
+ import com.twentyfouri.tvbridge.global.di.ActivityModule
35
+ import com.twentyfouri.tvbridge.global.di.DaggerActivityComponent
36
+ import com.twentyfouri.tvbridge.sound.TouchPlayer
37
+ import com.twentyfouri.tvbridge.webview.model.MediaEventType
38
+ import com.twentyfouri.tvbridge.webview.viewmodel.WebViewModel
39
+ import java.net.CookieHandler
40
+ import java.net.CookieManager
41
+ import java.net.CookiePolicy
42
+
43
+ class WebViewActivity : AppCompatActivity(), Mainview {
44
+
45
+ private var initialized: Boolean = false
46
+ private lateinit var component: ActivityComponent
47
+ private lateinit var binding: ActivityWebViewBinding
48
+ private lateinit var config: BridgeConfig
49
+ private lateinit var audioManager: AudioManager
50
+ private lateinit var viewModel: WebViewModel
51
+ private lateinit var touchPlayer: TouchPlayer
52
+
53
+ private val audioFocusListener = OnAudioFocusChangeListener { /* we actually don't care */ }
54
+
55
+ @SuppressLint("SetJavaScriptEnabled")
56
+ override fun onCreate(savedInstanceState: Bundle?) {
57
+ super.onCreate(savedInstanceState)
58
+
59
+ if (!isTaskRoot) {
60
+ finish()
61
+ return
62
+ }
63
+
64
+ component = DaggerActivityComponent.builder()
65
+ .applicationComponent((application as BaseApplication).component)
66
+ .activityModule(ActivityModule(this))
67
+ .build()
68
+ config = component.configuration
69
+ touchPlayer = component.touchPlayer
70
+ viewModel = component.viewModelFactory.get()
71
+ audioManager = getSystemService(AUDIO_SERVICE) as AudioManager
72
+
73
+ window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
74
+ val currentHandler = CookieHandler.getDefault()
75
+ if (currentHandler !== defaultCookieManager) {
76
+ CookieHandler.setDefault(defaultCookieManager)
77
+ }
78
+
79
+ binding = DataBindingUtil.setContentView(this, R.layout.activity_web_view)
80
+ binding.viewModel = viewModel
81
+ binding.exoplayerView.playerFactory = viewModel.playerFactory
82
+ viewModel.attachView(this)
83
+ viewModel.configureWebView(binding.webview)
84
+ viewModel.player = binding.exoplayerView.player
85
+
86
+ binding.url.addTextChangedListener(object : TextWatcher {
87
+ override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
88
+ override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
89
+ viewModel.appUrl = s.toString()
90
+ }
91
+
92
+ override fun afterTextChanged(s: Editable) {}
93
+ })
94
+
95
+ val appUrl = config.baseUrl
96
+ if (config.customizableUrl) {
97
+ viewModel.urlSetterVisible = true
98
+ viewModel.appUrl = appUrl
99
+ setUrlToView(appUrl)
100
+ } else {
101
+ viewModel.urlSetterVisible = false
102
+ loadUrl(appUrl)
103
+ }
104
+
105
+ binding.root.viewTreeObserver.addOnGlobalFocusChangeListener { oldFocus: View, newFocus: View ->
106
+ Log.v("FOCUS", "global focus changed old = $oldFocus")
107
+ Log.v("FOCUS", "global focus changed new = $newFocus")
108
+ }
109
+
110
+ initialized = true
111
+ }
112
+
113
+ override fun hideKeyboard() {
114
+ val imm = getSystemService(Activity.INPUT_METHOD_SERVICE) as? InputMethodManager
115
+ ?: return
116
+ //If imm is null do nothing
117
+ //Find the currently focused view, so we can grab the correct window token from it.
118
+ //If no view currently has focus, create a new one, just so we can grab a window token from it
119
+ val view = currentFocus ?: View(this)
120
+ imm.hideSoftInputFromWindow(view.windowToken, 0)
121
+ }
122
+
123
+ override fun setBlackCurtainVisibility(visibility: Int) {
124
+ runOnUiThread {
125
+ binding.blackCurtain.visibility = visibility
126
+ }
127
+ }
128
+
129
+ override fun showMessage(message: String?) {
130
+ Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
131
+ }
132
+
133
+ override fun requestAudioFocus(): Int {
134
+ return AudioManagerCompat.requestAudioFocus(
135
+ audioManager,
136
+ AudioFocusRequestCompat
137
+ .Builder(AudioManagerCompat.AUDIOFOCUS_GAIN)
138
+ .setAudioAttributes(
139
+ AudioAttributesCompat
140
+ .Builder()
141
+ .setContentType(AudioAttributesCompat.CONTENT_TYPE_MOVIE)
142
+ .build()
143
+ )
144
+ .setOnAudioFocusChangeListener(audioFocusListener)
145
+ .build()
146
+ )
147
+ }
148
+
149
+ override fun abandonAudioFocus() {
150
+ AudioManagerCompat.abandonAudioFocusRequest(
151
+ audioManager,
152
+ AudioFocusRequestCompat
153
+ .Builder(AudioManagerCompat.AUDIOFOCUS_GAIN)
154
+ .setOnAudioFocusChangeListener(audioFocusListener)
155
+ .build()
156
+ )
157
+ }
158
+
159
+ public override fun onResume() {
160
+ super.onResume()
161
+ viewModel.checkNewPurchases()
162
+ viewModel.resumePlayer()
163
+ }
164
+
165
+ public override fun onPause() {
166
+ viewModel.pausePlayer()
167
+ super.onPause()
168
+ }
169
+
170
+ public override fun onDestroy() {
171
+ if (initialized) {
172
+ viewModel.releasePlayer()
173
+ binding.unbind()
174
+ }
175
+ super.onDestroy()
176
+ }
177
+
178
+ override fun goFullscreen() {
179
+ val layoutParams = binding.exoplayerView.layoutParams as RelativeLayout.LayoutParams
180
+ layoutParams.width = -1
181
+ layoutParams.height = -1
182
+ layoutParams.leftMargin = 0
183
+ layoutParams.topMargin = 0
184
+ val wm = getSystemService(WINDOW_SERVICE) as WindowManager
185
+ val display = wm.defaultDisplay
186
+ val size = Point()
187
+ display.getSize(size)
188
+ val width = size.x
189
+ val height = size.y
190
+ binding.exoplayerView.layoutParams = layoutParams
191
+ binding.exoplayerView.layout(width, height, 0, 0)
192
+ }
193
+
194
+ override fun launchIapPurchase(sku: String) {
195
+ viewModel.launchIapPurchase(this, sku)
196
+ }
197
+
198
+ override fun exitFullscreen(width: Int, height: Int, left: Int, top: Int) {
199
+ val layoutParams = binding.exoplayerView.layoutParams as RelativeLayout.LayoutParams
200
+ layoutParams.width = width
201
+ layoutParams.height = height
202
+ layoutParams.leftMargin = left
203
+ layoutParams.topMargin = top
204
+
205
+ //set the layout inner and outer sizes
206
+ binding.exoplayerView.layoutParams = layoutParams
207
+ binding.exoplayerView.layout(width, height, left, top)
208
+ }
209
+
210
+ override fun setUrlToView(urlString: String?) {
211
+ binding.url.requestFocus()
212
+ binding.url.setText(urlString)
213
+ binding.url.setSelection(urlString?.length ?: 0)
214
+ }
215
+
216
+ override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
217
+ when (event.keyCode) {
218
+ KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT -> changeFocus()
219
+ KeyEvent.KEYCODE_DPAD_CENTER -> goToUrl()
220
+ KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_BUTTON_B, KeyEvent.KEYCODE_BUTTON_A, KeyEvent.KEYCODE_MENU, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, KeyEvent.KEYCODE_MEDIA_PLAY, KeyEvent.KEYCODE_MEDIA_PAUSE, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, KeyEvent.KEYCODE_MEDIA_REWIND -> {
221
+ if (!binding.url.hasFocus() && !binding.buttonGo.hasFocus()) {
222
+ // Send these key presses to the WebView, through a JavaScript bridge.
223
+ viewModel.sendKeyCode(event.keyCode, MediaEventType.KEY_DOWN)
224
+ }
225
+ }
226
+ else -> {
227
+ if (!binding.url.hasFocus() && !binding.buttonGo.hasFocus()) {
228
+ viewModel.sendKeyCode(event.keyCode, MediaEventType.KEY_DOWN)
229
+ }
230
+ }
231
+ }
232
+ return super.onKeyDown(keyCode, event)
233
+ }
234
+
235
+ override fun dispatchKeyEvent(event: KeyEvent): Boolean {
236
+ playTouchSounds(event)
237
+ return super.dispatchKeyEvent(event)
238
+ }
239
+
240
+ private fun playTouchSounds(event: KeyEvent) {
241
+ if (event.action != KeyEvent.ACTION_DOWN) {
242
+ return
243
+ }
244
+ when (event.keyCode) {
245
+ KeyEvent.KEYCODE_DPAD_RIGHT,
246
+ KeyEvent.KEYCODE_DPAD_LEFT,
247
+ KeyEvent.KEYCODE_DPAD_UP,
248
+ KeyEvent.KEYCODE_DPAD_DOWN,
249
+ KeyEvent.KEYCODE_DPAD_CENTER ->
250
+ if (config.isEnabledTouchSound) {
251
+ touchPlayer.play()
252
+ }
253
+ }
254
+ }
255
+
256
+ private fun goToUrl() {
257
+ if (binding.buttonGo.hasFocus()) {
258
+ viewModel.onClickGo()
259
+ }
260
+ }
261
+
262
+ private fun changeFocus() {
263
+ if (binding.url.hasFocus()) {
264
+ binding.buttonGo.requestFocus()
265
+ } else if (binding.buttonGo.hasFocus()) {
266
+ binding.url.requestFocus()
267
+ }
268
+ }
269
+
270
+ override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
271
+ if (!binding.url.hasFocus() && !binding.buttonGo.hasFocus()) {
272
+ // Send these key presses to the WebView, through a JavaScript bridge.
273
+ viewModel.sendKeyCode(event.keyCode, MediaEventType.KEY_UP)
274
+ return true
275
+ }
276
+ return super.onKeyUp(keyCode, event)
277
+ }
278
+
279
+ override fun onKeyLongPress(keyCode: Int, event: KeyEvent): Boolean {
280
+ if (!binding.url.hasFocus() && !binding.buttonGo.hasFocus()) {
281
+ // Send these key presses to the WebView, through a JavaScript bridge.
282
+ viewModel.sendKeyCode(event.keyCode, MediaEventType.KEY_PRESS)
283
+ return true
284
+ }
285
+ return super.onKeyLongPress(keyCode, event)
286
+ }
287
+
288
+ override fun loadUrl(url: String?) {
289
+ if (url.isNullOrEmpty()) return
290
+ binding.webview.visibility = View.VISIBLE
291
+ binding.webview.loadUrl(url)
292
+ if (url.startsWith("http")) {
293
+ binding.webview.requestFocus()
294
+ }
295
+ }
296
+
297
+ override fun onBackPressed() {
298
+ super.onBackPressed()
299
+ }
300
+
301
+ companion object {
302
+ private val defaultCookieManager: CookieManager = CookieManager().apply {
303
+ setCookiePolicy(CookiePolicy.ACCEPT_ALL)
304
+ }
305
+ }
306
+ }