@rentlydev/rently-tuya 0.1.0

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 (288) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +281 -0
  3. package/android/build.gradle +182 -0
  4. package/android/gradle.properties +7 -0
  5. package/android/libs/airbrake-android-1.4.0.jar +0 -0
  6. package/android/src/main/AndroidManifest.xml +1 -0
  7. package/android/src/main/AndroidManifestNew.xml +25 -0
  8. package/android/src/main/java/com/rentlytuya/RentlyTuyaModule.kt +503 -0
  9. package/android/src/main/java/com/rentlytuya/RentlyTuyaPackage.kt +18 -0
  10. package/android/src/main/java/com/rentlytuya/TuyaActivity.kt +45 -0
  11. package/android/src/main/java/com/rentlytuya/alerts/FormatSDCardAlert.kt +83 -0
  12. package/android/src/main/java/com/rentlytuya/alerts/PermissionAlert.kt +91 -0
  13. package/android/src/main/java/com/rentlytuya/alerts/RemoveDoorbellAlert.kt +83 -0
  14. package/android/src/main/java/com/rentlytuya/alerts/RemoveDoorbellFailureAlert.kt +71 -0
  15. package/android/src/main/java/com/rentlytuya/alerts/ResetWifiAlert.kt +83 -0
  16. package/android/src/main/java/com/rentlytuya/alerts/SuccessAlert.kt +74 -0
  17. package/android/src/main/java/com/rentlytuya/components/Alert.kt +47 -0
  18. package/android/src/main/java/com/rentlytuya/components/AnchorButton.kt +125 -0
  19. package/android/src/main/java/com/rentlytuya/components/Button.kt +147 -0
  20. package/android/src/main/java/com/rentlytuya/components/CustomDatePicker.kt +275 -0
  21. package/android/src/main/java/com/rentlytuya/components/Divider.kt +53 -0
  22. package/android/src/main/java/com/rentlytuya/components/EncryptedImageView.kt +59 -0
  23. package/android/src/main/java/com/rentlytuya/components/Label.kt +155 -0
  24. package/android/src/main/java/com/rentlytuya/components/LoadingIndicator.kt +75 -0
  25. package/android/src/main/java/com/rentlytuya/components/Slider.kt +66 -0
  26. package/android/src/main/java/com/rentlytuya/components/Switch.kt +51 -0
  27. package/android/src/main/java/com/rentlytuya/components/VideoView.kt +45 -0
  28. package/android/src/main/java/com/rentlytuya/dashboard/AdvancedSettings.kt +259 -0
  29. package/android/src/main/java/com/rentlytuya/dashboard/Dashboard.kt +551 -0
  30. package/android/src/main/java/com/rentlytuya/dashboard/DoorbellSettings.kt +874 -0
  31. package/android/src/main/java/com/rentlytuya/dashboard/Messages.kt +463 -0
  32. package/android/src/main/java/com/rentlytuya/dashboard/NavController.kt +358 -0
  33. package/android/src/main/java/com/rentlytuya/dashboard/OfflineScreen.kt +160 -0
  34. package/android/src/main/java/com/rentlytuya/dashboard/Playback.kt +685 -0
  35. package/android/src/main/java/com/rentlytuya/tuya/Messages.kt +234 -0
  36. package/android/src/main/java/com/rentlytuya/tuya/Playback.kt +385 -0
  37. package/android/src/main/java/com/rentlytuya/tuya/ScreenCapture.kt +244 -0
  38. package/android/src/main/java/com/rentlytuya/tuya/Settings.kt +809 -0
  39. package/android/src/main/java/com/rentlytuya/tuya/Tuya.kt +497 -0
  40. package/android/src/main/java/com/rentlytuya/ui/theme/LocalColor.kt +80 -0
  41. package/android/src/main/java/com/rentlytuya/ui/theme/LocalFont.kt +35 -0
  42. package/android/src/main/java/com/rentlytuya/ui/theme/Theme.kt +34 -0
  43. package/android/src/main/java/com/rentlytuya/ui/theme/Type.kt +34 -0
  44. package/android/src/main/java/com/rentlytuya/util/Airbrake.kt +40 -0
  45. package/android/src/main/java/com/rentlytuya/util/DpConstants.kt +53 -0
  46. package/android/src/main/java/com/rentlytuya/util/FrescoManager.kt +86 -0
  47. package/android/src/main/java/com/rentlytuya/util/HeapAnalytics.kt +16 -0
  48. package/android/src/main/java/com/rentlytuya/util/KeylessModule.kt +81 -0
  49. package/android/src/main/java/com/rentlytuya/util/PermissionHandler.kt +75 -0
  50. package/android/src/main/java/com/rentlytuya/util/ReactParamsCheck.kt +152 -0
  51. package/android/src/main/java/com/rentlytuya/util/Util.kt +128 -0
  52. package/android/src/main/res/drawable/ic_close.xml +12 -0
  53. package/android/src/main/res/drawable/ic_launcher_background.xml +170 -0
  54. package/android/src/main/res/drawable/ic_launcher_foreground.xml +30 -0
  55. package/android/src/main/res/drawable/img_place_holder.png +0 -0
  56. package/android/src/main/res/drawable-hdpi/ic_action_arrow_backward.png +0 -0
  57. package/android/src/main/res/drawable-hdpi/ic_arrow_backward_white.png +0 -0
  58. package/android/src/main/res/drawable-hdpi/ic_arrow_blue_right.png +0 -0
  59. package/android/src/main/res/drawable-hdpi/ic_calendar.png +0 -0
  60. package/android/src/main/res/drawable-hdpi/ic_camera.png +0 -0
  61. package/android/src/main/res/drawable-hdpi/ic_message.png +0 -0
  62. package/android/src/main/res/drawable-hdpi/ic_pause.png +0 -0
  63. package/android/src/main/res/drawable-hdpi/ic_play.png +0 -0
  64. package/android/src/main/res/drawable-hdpi/ic_play_back.png +0 -0
  65. package/android/src/main/res/drawable-hdpi/ic_record.png +0 -0
  66. package/android/src/main/res/drawable-hdpi/ic_retry.png +0 -0
  67. package/android/src/main/res/drawable-hdpi/ic_setting.png +0 -0
  68. package/android/src/main/res/drawable-hdpi/ic_sound_off.png +0 -0
  69. package/android/src/main/res/drawable-hdpi/ic_sound_on.png +0 -0
  70. package/android/src/main/res/drawable-mdpi/ic_action_arrow_backward.png +0 -0
  71. package/android/src/main/res/drawable-mdpi/ic_arrow_backward_white.png +0 -0
  72. package/android/src/main/res/drawable-mdpi/ic_arrow_blue_right.png +0 -0
  73. package/android/src/main/res/drawable-mdpi/ic_battery_average.png +0 -0
  74. package/android/src/main/res/drawable-mdpi/ic_battery_charging.png +0 -0
  75. package/android/src/main/res/drawable-mdpi/ic_battery_full.png +0 -0
  76. package/android/src/main/res/drawable-mdpi/ic_battery_good.png +0 -0
  77. package/android/src/main/res/drawable-mdpi/ic_battery_low.png +0 -0
  78. package/android/src/main/res/drawable-mdpi/ic_calendar.png +0 -0
  79. package/android/src/main/res/drawable-mdpi/ic_camera.png +0 -0
  80. package/android/src/main/res/drawable-mdpi/ic_message.png +0 -0
  81. package/android/src/main/res/drawable-mdpi/ic_pause.png +0 -0
  82. package/android/src/main/res/drawable-mdpi/ic_play.png +0 -0
  83. package/android/src/main/res/drawable-mdpi/ic_play_back.png +0 -0
  84. package/android/src/main/res/drawable-mdpi/ic_record.png +0 -0
  85. package/android/src/main/res/drawable-mdpi/ic_retry.png +0 -0
  86. package/android/src/main/res/drawable-mdpi/ic_setting.png +0 -0
  87. package/android/src/main/res/drawable-mdpi/ic_sound_off.png +0 -0
  88. package/android/src/main/res/drawable-mdpi/ic_sound_on.png +0 -0
  89. package/android/src/main/res/drawable-xhdpi/ic_action_arrow_backward.png +0 -0
  90. package/android/src/main/res/drawable-xhdpi/ic_arrow_backward_white.png +0 -0
  91. package/android/src/main/res/drawable-xhdpi/ic_arrow_blue_right.png +0 -0
  92. package/android/src/main/res/drawable-xhdpi/ic_battery_average.png +0 -0
  93. package/android/src/main/res/drawable-xhdpi/ic_battery_charging.png +0 -0
  94. package/android/src/main/res/drawable-xhdpi/ic_battery_full.png +0 -0
  95. package/android/src/main/res/drawable-xhdpi/ic_battery_good.png +0 -0
  96. package/android/src/main/res/drawable-xhdpi/ic_battery_low.png +0 -0
  97. package/android/src/main/res/drawable-xhdpi/ic_calendar.png +0 -0
  98. package/android/src/main/res/drawable-xhdpi/ic_camera.png +0 -0
  99. package/android/src/main/res/drawable-xhdpi/ic_message.png +0 -0
  100. package/android/src/main/res/drawable-xhdpi/ic_pause.png +0 -0
  101. package/android/src/main/res/drawable-xhdpi/ic_play.png +0 -0
  102. package/android/src/main/res/drawable-xhdpi/ic_play_back.png +0 -0
  103. package/android/src/main/res/drawable-xhdpi/ic_record.png +0 -0
  104. package/android/src/main/res/drawable-xhdpi/ic_retry.png +0 -0
  105. package/android/src/main/res/drawable-xhdpi/ic_setting.png +0 -0
  106. package/android/src/main/res/drawable-xhdpi/ic_sound_off.png +0 -0
  107. package/android/src/main/res/drawable-xhdpi/ic_sound_on.png +0 -0
  108. package/android/src/main/res/drawable-xxhdpi/ic_action_arrow_backward.png +0 -0
  109. package/android/src/main/res/drawable-xxhdpi/ic_arrow_backward_white.png +0 -0
  110. package/android/src/main/res/drawable-xxhdpi/ic_arrow_blue_right.png +0 -0
  111. package/android/src/main/res/drawable-xxhdpi/ic_battery_average.png +0 -0
  112. package/android/src/main/res/drawable-xxhdpi/ic_battery_charging.png +0 -0
  113. package/android/src/main/res/drawable-xxhdpi/ic_battery_full.png +0 -0
  114. package/android/src/main/res/drawable-xxhdpi/ic_battery_good.png +0 -0
  115. package/android/src/main/res/drawable-xxhdpi/ic_battery_low.png +0 -0
  116. package/android/src/main/res/drawable-xxhdpi/ic_calendar.png +0 -0
  117. package/android/src/main/res/drawable-xxhdpi/ic_camera.png +0 -0
  118. package/android/src/main/res/drawable-xxhdpi/ic_message.png +0 -0
  119. package/android/src/main/res/drawable-xxhdpi/ic_pause.png +0 -0
  120. package/android/src/main/res/drawable-xxhdpi/ic_play.png +0 -0
  121. package/android/src/main/res/drawable-xxhdpi/ic_play_back.png +0 -0
  122. package/android/src/main/res/drawable-xxhdpi/ic_record.png +0 -0
  123. package/android/src/main/res/drawable-xxhdpi/ic_retry.png +0 -0
  124. package/android/src/main/res/drawable-xxhdpi/ic_setting.png +0 -0
  125. package/android/src/main/res/drawable-xxhdpi/ic_sound_off.png +0 -0
  126. package/android/src/main/res/drawable-xxhdpi/ic_sound_on.png +0 -0
  127. package/android/src/main/res/drawable-xxxhdpi/ic_action_arrow_backward.png +0 -0
  128. package/android/src/main/res/drawable-xxxhdpi/ic_arrow_backward_white.png +0 -0
  129. package/android/src/main/res/drawable-xxxhdpi/ic_arrow_blue_right.png +0 -0
  130. package/android/src/main/res/drawable-xxxhdpi/ic_calendar.png +0 -0
  131. package/android/src/main/res/drawable-xxxhdpi/ic_camera.png +0 -0
  132. package/android/src/main/res/drawable-xxxhdpi/ic_message.png +0 -0
  133. package/android/src/main/res/drawable-xxxhdpi/ic_pause.png +0 -0
  134. package/android/src/main/res/drawable-xxxhdpi/ic_play.png +0 -0
  135. package/android/src/main/res/drawable-xxxhdpi/ic_play_back.png +0 -0
  136. package/android/src/main/res/drawable-xxxhdpi/ic_record.png +0 -0
  137. package/android/src/main/res/drawable-xxxhdpi/ic_retry.png +0 -0
  138. package/android/src/main/res/drawable-xxxhdpi/ic_setting.png +0 -0
  139. package/android/src/main/res/drawable-xxxhdpi/ic_sound_off.png +0 -0
  140. package/android/src/main/res/drawable-xxxhdpi/ic_sound_on.png +0 -0
  141. package/android/src/main/res/font/quicksand_bold.ttf +0 -0
  142. package/android/src/main/res/font/quicksand_light.ttf +0 -0
  143. package/android/src/main/res/font/quicksand_medium.ttf +0 -0
  144. package/android/src/main/res/font/quicksand_regular.ttf +0 -0
  145. package/android/src/main/res/font/quicksand_semi_bold.ttf +0 -0
  146. package/android/src/main/res/values/strings.xml +121 -0
  147. package/android/thingMapping.gradle +200 -0
  148. package/android/thingMapping.json +2014 -0
  149. package/ios/components/Alert.swift +54 -0
  150. package/ios/components/AnchorButton.swift +100 -0
  151. package/ios/components/Button.swift +118 -0
  152. package/ios/components/CustomDatePicker.swift +275 -0
  153. package/ios/components/Divider.swift +28 -0
  154. package/ios/components/Image.swift +72 -0
  155. package/ios/components/Label.swift +105 -0
  156. package/ios/components/Switch.swift +61 -0
  157. package/ios/dashboard/AdvanceSettings.swift +127 -0
  158. package/ios/dashboard/Dashboard.swift +354 -0
  159. package/ios/dashboard/Messages.swift +361 -0
  160. package/ios/dashboard/NavigationController.swift +173 -0
  161. package/ios/dashboard/OfflineScreen.swift +109 -0
  162. package/ios/dashboard/Playback.swift +543 -0
  163. package/ios/dashboard/PreviewManager.swift +117 -0
  164. package/ios/dashboard/Settings.swift +672 -0
  165. package/ios/icons/Charging.png +0 -0
  166. package/ios/icons/Charging@2x.png +0 -0
  167. package/ios/icons/Charging@3x.png +0 -0
  168. package/ios/icons/back_arraow.png +0 -0
  169. package/ios/icons/back_arraow@2x.png +0 -0
  170. package/ios/icons/back_arraow@3x.png +0 -0
  171. package/ios/icons/battery_average.png +0 -0
  172. package/ios/icons/battery_average@2x.png +0 -0
  173. package/ios/icons/battery_average@3x.png +0 -0
  174. package/ios/icons/battery_full.png +0 -0
  175. package/ios/icons/battery_full@2x.png +0 -0
  176. package/ios/icons/battery_full@3x.png +0 -0
  177. package/ios/icons/battery_good.png +0 -0
  178. package/ios/icons/battery_good@2x.png +0 -0
  179. package/ios/icons/battery_good@3x.png +0 -0
  180. package/ios/icons/battery_low.png +0 -0
  181. package/ios/icons/battery_low@2x.png +0 -0
  182. package/ios/icons/battery_low@3x.png +0 -0
  183. package/ios/icons/calendar.png +0 -0
  184. package/ios/icons/calendar@2x.png +0 -0
  185. package/ios/icons/calendar@3x.png +0 -0
  186. package/ios/icons/cloud.png +0 -0
  187. package/ios/icons/cloud@2x.png +0 -0
  188. package/ios/icons/cloud@3x.png +0 -0
  189. package/ios/icons/down_update.png +0 -0
  190. package/ios/icons/down_update@2x.png +0 -0
  191. package/ios/icons/down_update@3x.png +0 -0
  192. package/ios/icons/exit_fullscreen.png +0 -0
  193. package/ios/icons/exit_fullscreen@2x.png +0 -0
  194. package/ios/icons/exit_fullscreen@3x.png +0 -0
  195. package/ios/icons/expand.png +0 -0
  196. package/ios/icons/fullscreen.png +0 -0
  197. package/ios/icons/fullscreen@2x.png +0 -0
  198. package/ios/icons/fullscreen@3x.png +0 -0
  199. package/ios/icons/image_placeholder.png +0 -0
  200. package/ios/icons/information.png +0 -0
  201. package/ios/icons/information@2x.png +0 -0
  202. package/ios/icons/information@3x.png +0 -0
  203. package/ios/icons/keyless_Setting.png +0 -0
  204. package/ios/icons/keyless_Setting@2x.png +0 -0
  205. package/ios/icons/keyless_Setting@3x.png +0 -0
  206. package/ios/icons/keyless_back@1x.png +0 -0
  207. package/ios/icons/keyless_back@2x.png +0 -0
  208. package/ios/icons/keyless_back@3x.png +0 -0
  209. package/ios/icons/keyless_filter.png +0 -0
  210. package/ios/icons/keyless_filter@2x.png +0 -0
  211. package/ios/icons/keyless_filter@3x.png +0 -0
  212. package/ios/icons/keyless_message.png +0 -0
  213. package/ios/icons/keyless_message@2x.png +0 -0
  214. package/ios/icons/keyless_message@3x.png +0 -0
  215. package/ios/icons/keyless_pause.png +0 -0
  216. package/ios/icons/keyless_pause@2x.png +0 -0
  217. package/ios/icons/keyless_pause@3x.png +0 -0
  218. package/ios/icons/keyless_photo.png +0 -0
  219. package/ios/icons/keyless_photo@2x.png +0 -0
  220. package/ios/icons/keyless_photo@3x.png +0 -0
  221. package/ios/icons/keyless_play.png +0 -0
  222. package/ios/icons/keyless_play@2x.png +0 -0
  223. package/ios/icons/keyless_play@3x.png +0 -0
  224. package/ios/icons/keyless_play_list.png +0 -0
  225. package/ios/icons/keyless_play_list@2x.png +0 -0
  226. package/ios/icons/keyless_play_list@3x.png +0 -0
  227. package/ios/icons/keyless_play_playback.png +0 -0
  228. package/ios/icons/keyless_play_playback@2x.png +0 -0
  229. package/ios/icons/keyless_play_playback@3x.png +0 -0
  230. package/ios/icons/keyless_record.png +0 -0
  231. package/ios/icons/keyless_record@2x.png +0 -0
  232. package/ios/icons/keyless_record@3x.png +0 -0
  233. package/ios/icons/keyless_record_on.png +0 -0
  234. package/ios/icons/keyless_record_on@2x.png +0 -0
  235. package/ios/icons/keyless_record_on@3x.png +0 -0
  236. package/ios/icons/keyless_sound_off.png +0 -0
  237. package/ios/icons/keyless_sound_off@2x.png +0 -0
  238. package/ios/icons/keyless_sound_off@3x.png +0 -0
  239. package/ios/icons/keyless_sound_on.png +0 -0
  240. package/ios/icons/keyless_sound_on@2x.png +0 -0
  241. package/ios/icons/keyless_sound_on@3x.png +0 -0
  242. package/ios/icons/keyless_speak_off.png +0 -0
  243. package/ios/icons/keyless_speak_off@2x.png +0 -0
  244. package/ios/icons/keyless_speak_on.png +0 -0
  245. package/ios/icons/keyless_speak_on@2x.png +0 -0
  246. package/ios/icons/keyless_speak_on@3x.png +0 -0
  247. package/ios/icons/refresh.png +0 -0
  248. package/ios/icons/refresh@2x.png +0 -0
  249. package/ios/icons/refresh@3x.png +0 -0
  250. package/ios/icons/ty_camera_control_hd_normal@2x.png +0 -0
  251. package/ios/icons/ty_camera_control_hd_normal@3x.png +0 -0
  252. package/ios/icons/ty_camera_control_sd_normal@2x.png +0 -0
  253. package/ios/icons/ty_camera_control_sd_normal@3x.png +0 -0
  254. package/ios/icons/upgrade_success.png +0 -0
  255. package/ios/icons/upgrade_success@2x.png +0 -0
  256. package/ios/icons/upgrade_success@3x.png +0 -0
  257. package/ios/listener/KeylessListener.m +8 -0
  258. package/ios/listener/KeylessListener.swift +56 -0
  259. package/ios/listener/RentlyTuya-Bridging-Header.h +5 -0
  260. package/ios/listener/RentlyTuya.mm +56 -0
  261. package/ios/listener/RentlyTuya.swift +544 -0
  262. package/ios/theme/LocalColor.swift +33 -0
  263. package/ios/theme/LocalFont.swift +32 -0
  264. package/ios/theme/Theme.swift +71 -0
  265. package/ios/tuya/CameraManager.swift +296 -0
  266. package/ios/tuya/SettingsManager.swift +475 -0
  267. package/ios/tuya/Tuya.swift +570 -0
  268. package/ios/tuya/TuyaMessages.swift +98 -0
  269. package/ios/utils/Airbrake.swift +31 -0
  270. package/ios/utils/AppUtility.swift +76 -0
  271. package/ios/utils/HeapAnalytics.swift +8 -0
  272. package/ios/utils/OrientationManager.swift +60 -0
  273. package/ios/utils/PromiseHandler.swift +36 -0
  274. package/lib/commonjs/index.js +90 -0
  275. package/lib/commonjs/index.js.map +1 -0
  276. package/lib/commonjs/package.json +1 -0
  277. package/lib/module/index.js +83 -0
  278. package/lib/module/index.js.map +1 -0
  279. package/lib/module/package.json +1 -0
  280. package/lib/typescript/commonjs/package.json +1 -0
  281. package/lib/typescript/commonjs/src/index.d.ts +75 -0
  282. package/lib/typescript/commonjs/src/index.d.ts.map +1 -0
  283. package/lib/typescript/module/package.json +1 -0
  284. package/lib/typescript/module/src/index.d.ts +75 -0
  285. package/lib/typescript/module/src/index.d.ts.map +1 -0
  286. package/package.json +188 -0
  287. package/rently-tuya.podspec +52 -0
  288. package/src/index.tsx +158 -0
@@ -0,0 +1,685 @@
1
+ package com.rentlytuya.dashboard
2
+
3
+ import android.os.Build
4
+ import android.os.Handler
5
+ import android.os.Looper
6
+ import android.util.Log
7
+ import androidx.activity.compose.BackHandler
8
+ import androidx.annotation.RequiresApi
9
+ import androidx.compose.animation.AnimatedVisibility
10
+ import androidx.compose.animation.fadeIn
11
+ import androidx.compose.animation.fadeOut
12
+ import androidx.compose.foundation.background
13
+ import androidx.compose.foundation.clickable
14
+ import androidx.compose.foundation.layout.Arrangement
15
+ import androidx.compose.foundation.layout.Box
16
+ import androidx.compose.foundation.layout.BoxScope
17
+ import androidx.compose.foundation.layout.Column
18
+ import androidx.compose.foundation.layout.Row
19
+ import androidx.compose.foundation.layout.fillMaxHeight
20
+ import androidx.compose.foundation.layout.fillMaxSize
21
+ import androidx.compose.foundation.layout.fillMaxWidth
22
+ import androidx.compose.foundation.layout.height
23
+ import androidx.compose.foundation.layout.padding
24
+ import androidx.compose.foundation.layout.width
25
+ import androidx.compose.foundation.lazy.LazyColumn
26
+ import androidx.compose.foundation.lazy.items
27
+ import androidx.compose.runtime.Composable
28
+ import androidx.compose.runtime.DisposableEffect
29
+ import androidx.compose.runtime.LaunchedEffect
30
+ import androidx.compose.runtime.MutableIntState
31
+ import androidx.compose.runtime.MutableState
32
+ import androidx.compose.runtime.mutableFloatStateOf
33
+ import androidx.compose.runtime.mutableIntStateOf
34
+ import androidx.compose.runtime.mutableLongStateOf
35
+ import androidx.compose.runtime.mutableStateOf
36
+ import androidx.compose.runtime.remember
37
+ import androidx.compose.runtime.rememberCoroutineScope
38
+ import androidx.compose.ui.Alignment
39
+ import androidx.compose.ui.Modifier
40
+ import androidx.compose.ui.draw.shadow
41
+ import androidx.compose.ui.platform.LocalConfiguration
42
+ import androidx.compose.ui.platform.LocalContext
43
+ import androidx.compose.ui.unit.dp
44
+ import androidx.compose.ui.unit.sp
45
+ import androidx.lifecycle.Lifecycle
46
+ import androidx.lifecycle.LifecycleEventObserver
47
+ import androidx.lifecycle.compose.LocalLifecycleOwner
48
+ import androidx.navigation.NavController
49
+ import com.rentlytuya.R
50
+ import com.rentlytuya.alerts.SuccessAlert
51
+ import com.rentlytuya.components.DatePicker
52
+ import com.rentlytuya.components.Label
53
+ import com.rentlytuya.components.LoadingIndicator
54
+ import com.rentlytuya.components.Slider
55
+ import com.rentlytuya.components.VideoView
56
+ import com.rentlytuya.tuya.Playback
57
+ import com.rentlytuya.tuya.ScreenCapture
58
+ import com.rentlytuya.tuya.Tuya
59
+ import com.rentlytuya.tuya.Tuya.TAG
60
+ import com.rentlytuya.ui.theme.LocalColor
61
+ import com.rentlytuya.util.DateUtil
62
+ import com.rentlytuya.util.DateUtil.isNotNull
63
+ import com.rentlytuya.util.Util
64
+ import com.rentlytuya.util.Util.isLandscape
65
+ import com.rentlytuya.util.Util.strings
66
+ import com.thingclips.smart.ipc.camera.autotesting.excute.bean.TimePieceBean
67
+ import kotlinx.coroutines.delay
68
+ import kotlinx.coroutines.launch
69
+ import java.time.Instant
70
+ import java.time.LocalDate
71
+ import java.time.ZoneId
72
+ import java.time.format.DateTimeFormatter
73
+ import java.util.Date
74
+
75
+ @RequiresApi(Build.VERSION_CODES.O)
76
+ @Composable
77
+ fun Playback(
78
+ isMuted: MutableState<Boolean>,
79
+ isSettings: MutableState<Boolean>,
80
+ isDatePickerVisible: MutableState<Boolean>,
81
+ navController: NavController
82
+ ) {
83
+
84
+ val coroutineScope = rememberCoroutineScope()
85
+ val configuration = LocalConfiguration.current
86
+ val lifecycleOwner = LocalLifecycleOwner.current
87
+
88
+ val datePickerLoader = remember { mutableStateOf(false) }
89
+ val availableDates = remember { mutableStateOf(emptyArray<Int>()) }
90
+ val selectedDate = remember { mutableLongStateOf(Date().time) }
91
+ val isLoading = remember { mutableStateOf(false) }
92
+ val videos = remember { mutableStateOf(emptyList<TimePieceBean>()) }
93
+ val selectedItem = remember { mutableStateOf<TimePieceBean?>(null) }
94
+
95
+ val isPlaying = remember { mutableStateOf(false) }
96
+ val isPaused = remember { mutableStateOf(false) }
97
+ val isScreenShotSuccess = remember { mutableStateOf(false) }
98
+ val isRecordingSuccess = remember { mutableStateOf(false) }
99
+ val isRecordingFailure = remember { mutableStateOf(false) }
100
+
101
+ val sliderVisible = remember { mutableStateOf(false) }
102
+ val sliderAlpha = remember { mutableFloatStateOf(0f) }
103
+
104
+ val timerHandler = remember { Handler(Looper.getMainLooper()) }
105
+
106
+ val currentPosition = remember { mutableIntStateOf(0) }
107
+ val isSeeking = remember { mutableStateOf(false) }
108
+
109
+ BackHandler {
110
+ isSettings.value = false
111
+ navController.navigate("Dashboard")
112
+ }
113
+
114
+ DisposableEffect(lifecycleOwner) {
115
+ val observer = LifecycleEventObserver { _, event ->
116
+ if (event == Lifecycle.Event.ON_PAUSE) {
117
+ Playback.pauseSDCardPlayback(isPaused = isPaused)
118
+ }
119
+ }
120
+ lifecycleOwner.lifecycle.addObserver(observer)
121
+ onDispose {
122
+ lifecycleOwner.lifecycle.removeObserver(observer)
123
+ }
124
+ }
125
+
126
+ fun updateSliderValue() {
127
+ val startTime = selectedItem.value?.startTime
128
+ val endTime = selectedItem.value?.endTime
129
+
130
+ if (isPlaying.value && !isPaused.value && !isLoading.value && !isSeeking.value && startTime != null && endTime != null) {
131
+ if (currentPosition.intValue < (endTime - startTime)) {
132
+ currentPosition.intValue += 1
133
+ } else {
134
+ Playback.pauseSDCardPlayback(isPaused = isPaused)
135
+ }
136
+ }
137
+ }
138
+
139
+ val timerRunnable: Runnable = remember {
140
+ object : Runnable {
141
+ override fun run() {
142
+ updateSliderValue()
143
+ timerHandler.postDelayed(this, 1000)
144
+ }
145
+ }
146
+ }
147
+
148
+ fun setupTimer() {
149
+ timerHandler.post(timerRunnable)
150
+ }
151
+
152
+ fun stopTimer() {
153
+ timerHandler.removeCallbacks(timerRunnable)
154
+ }
155
+
156
+ fun showSlider() {
157
+ coroutineScope.launch {
158
+ sliderVisible.value = true
159
+ sliderAlpha.floatValue = 1f
160
+ delay(8000)
161
+ sliderAlpha.floatValue = 0f
162
+ delay(500)
163
+ sliderVisible.value = false
164
+ }
165
+ }
166
+
167
+ LaunchedEffect(Unit) {
168
+ Tuya.stopPreview()
169
+ setupTimer()
170
+ Playback.getAvailableDates(
171
+ year = LocalDate.now().year,
172
+ month = LocalDate.now().monthValue,
173
+ availableDates = availableDates,
174
+ isLoading = datePickerLoader
175
+ )
176
+ showSlider()
177
+ }
178
+
179
+ LaunchedEffect(selectedDate.longValue) {
180
+ val (year, month, day) = DateUtil.getDateComponents(selectedDate.longValue)
181
+
182
+ if (isPlaying.value) {
183
+ Playback.stopSDCardPlayback(
184
+ isPlaying = isPlaying
185
+ )
186
+ }
187
+
188
+ Playback.getAllTheClipsOfTheDay(
189
+ year = year,
190
+ month = month,
191
+ day = day,
192
+ isPaused = isPaused,
193
+ isPlaying = isPlaying,
194
+ isLoading = isLoading,
195
+ videos = videos,
196
+ selectedItem = selectedItem
197
+ )
198
+ }
199
+
200
+ LaunchedEffect(selectedItem.value) {
201
+ selectedItem.value?.let {
202
+ currentPosition.intValue = 0
203
+ }
204
+ }
205
+
206
+ LaunchedEffect(isPlaying.value) {
207
+ if (!isPlaying.value) {
208
+ Tuya.setMute(1,
209
+ setMute = { value ->
210
+ isMuted.value = value
211
+ }
212
+ )
213
+ }
214
+ }
215
+
216
+ LaunchedEffect(configuration.orientation) {
217
+ if (isLandscape(configuration) && isDatePickerVisible.value) {
218
+ isDatePickerVisible.value = false
219
+ }
220
+ }
221
+
222
+ DisposableEffect(Unit) {
223
+ onDispose { stopTimer() }
224
+ }
225
+
226
+ Column(
227
+ modifier = Modifier
228
+ .fillMaxSize(),
229
+ horizontalAlignment = Alignment.CenterHorizontally,
230
+ verticalArrangement = Arrangement.Center
231
+ ) {
232
+ if (isLoading.value) {
233
+ LoadingIndicator()
234
+ }
235
+
236
+ Column(
237
+ modifier = Modifier
238
+ .fillMaxSize()
239
+ ) {
240
+ Box(
241
+ modifier = Modifier
242
+ .fillMaxWidth()
243
+ .fillMaxHeight(if (Util.isLandscape(configuration)) 1f else 0.3f)
244
+ ) {
245
+ if (!isPlaying.value) {
246
+ Box(
247
+ modifier = Modifier
248
+ .fillMaxSize()
249
+ .background(LocalColor.Monochrome.Black)
250
+ )
251
+ } else {
252
+ Box(
253
+ modifier = Modifier
254
+ .fillMaxSize()
255
+ .background(LocalColor.Monochrome.Black)
256
+ ) {
257
+ VideoView(
258
+ onClick = {
259
+ showSlider()
260
+ }
261
+ )
262
+
263
+ SliderView(
264
+ isPaused = isPaused,
265
+ isPlaying = isPlaying,
266
+ isLoading = isLoading,
267
+ isSeeking = isSeeking,
268
+ currentPosition = currentPosition,
269
+ sliderVisible = sliderVisible,
270
+ selectedItem = selectedItem,
271
+ videoList = videos.value
272
+ )
273
+ }
274
+ }
275
+ }
276
+
277
+ if (!Util.isLandscape(configuration)) {
278
+ Column(
279
+ modifier = Modifier
280
+ .fillMaxWidth()
281
+ .fillMaxHeight(0.85f)
282
+ .background(LocalColor.Monochrome.White)
283
+ ) {
284
+ if (videos.value.isEmpty()) {
285
+ Column(
286
+ modifier = Modifier
287
+ .fillMaxSize()
288
+ .background(LocalColor.Main.Light),
289
+ horizontalAlignment = Alignment.CenterHorizontally,
290
+ verticalArrangement = Arrangement.Center
291
+ ) {
292
+ Label(
293
+ modifier = Modifier.padding(horizontal = 30.dp),
294
+ id = "noPlaybackVideos",
295
+ title = strings(id = R.string.noPlaybackVideos),
296
+ l = true,
297
+ center = true,
298
+ black = true,
299
+ medium = true,
300
+ maxLines = 3
301
+ )
302
+ }
303
+ } else {
304
+ LazyColumn(
305
+ modifier = Modifier
306
+ .fillMaxSize()
307
+ .background(LocalColor.Main.Light),
308
+ horizontalAlignment = Alignment.CenterHorizontally
309
+ ) {
310
+ items(videos.value) { video ->
311
+ VideoItem(
312
+ video = video,
313
+ videoList = videos.value,
314
+ isPaused = isPaused,
315
+ isPlaying = isPlaying,
316
+ isLoading = isLoading,
317
+ selectedItem = selectedItem,
318
+ currentSliderValue = currentPosition
319
+ )
320
+ }
321
+ }
322
+ }
323
+ }
324
+
325
+ BottomBar(
326
+ isMuted = isMuted,
327
+ isPaused = isPaused,
328
+ isPlaying = isPlaying,
329
+ isLoading = isLoading,
330
+ videoList = videos.value,
331
+ selectedItem = selectedItem,
332
+ currentSliderValue = currentPosition,
333
+ isRecordingSuccess = isRecordingSuccess,
334
+ isRecordingFailure = isRecordingFailure,
335
+ isScreenShotSuccess = isScreenShotSuccess,
336
+ )
337
+ }
338
+
339
+ DatePicker(
340
+ isVisible = isDatePickerVisible,
341
+ availableDates = availableDates.value,
342
+ minDate = if (Tuya.lastMoveOutDate.isNotNull()) LocalDate.parse(
343
+ Tuya.lastMoveOutDate,
344
+ DateTimeFormatter.ISO_DATE_TIME
345
+ ) else null,
346
+ isLoading = datePickerLoader.value,
347
+ onDateSelected = {
348
+
349
+ },
350
+ onMonthChanged = { newMonth ->
351
+ coroutineScope.launch {
352
+ Playback.getAvailableDates(
353
+ year = newMonth.year,
354
+ month = newMonth.monthValue,
355
+ availableDates = availableDates,
356
+ isLoading = datePickerLoader
357
+ )
358
+ }
359
+ },
360
+ onCancel = {
361
+ isDatePickerVisible.value = false
362
+ },
363
+ onOk = { date ->
364
+ selectedDate.longValue = date
365
+ isDatePickerVisible.value = false
366
+ },
367
+ onAppear = {
368
+ val localDate = Instant.ofEpochMilli(selectedDate.longValue)
369
+ .atZone(ZoneId.systemDefault())
370
+ .toLocalDate()
371
+
372
+ coroutineScope.launch {
373
+ Playback.getAvailableDates(
374
+ year = localDate.year,
375
+ month = localDate.monthValue,
376
+ availableDates = availableDates,
377
+ isLoading = datePickerLoader
378
+ )
379
+ }
380
+ }
381
+ )
382
+ }
383
+ }
384
+ }
385
+
386
+ @Composable
387
+ fun BoxScope.SliderView(
388
+ videoList: List<TimePieceBean>,
389
+ isPaused: MutableState<Boolean>,
390
+ isPlaying: MutableState<Boolean>,
391
+ isLoading: MutableState<Boolean>,
392
+ currentPosition: MutableIntState,
393
+ sliderVisible: MutableState<Boolean>,
394
+ isSeeking: MutableState<Boolean>,
395
+ selectedItem: MutableState<TimePieceBean?>
396
+ ) {
397
+ if (selectedItem.value.isNotNull()) {
398
+ Column(
399
+ modifier = Modifier
400
+ .fillMaxWidth()
401
+ .align(Alignment.BottomCenter)
402
+ ) {
403
+ AnimatedVisibility(
404
+ visible = sliderVisible.value && selectedItem.value != null,
405
+ enter = fadeIn(),
406
+ exit = fadeOut(),
407
+ ) {
408
+ Column(
409
+ modifier = Modifier
410
+ .padding(horizontal = 18.dp)
411
+ ) {
412
+
413
+ Label(
414
+ modifier = Modifier
415
+ .align(Alignment.Start)
416
+ .width(300.dp),
417
+ id = "duration",
418
+ title = "${DateUtil.formatSecondsToHHMMSS(currentPosition.intValue)} / ${
419
+ DateUtil.calculateDuration(
420
+ startTime = selectedItem.value!!.startTime,
421
+ endTime = selectedItem.value!!.endTime
422
+ )
423
+ }",
424
+ s = true,
425
+ white = true,
426
+ bold = true,
427
+ lineHeight = 12.sp
428
+ )
429
+
430
+ Slider(
431
+ id = "playbackSlider",
432
+ sliderValue = currentPosition.intValue.toFloat(),
433
+ onValueChange = { value ->
434
+ isSeeking.value = true
435
+ currentPosition.intValue = value.toInt()
436
+ },
437
+ onSlidingComplete = {
438
+ isSeeking.value = false
439
+ val playTime = DateUtil.incrementTimestampBySeconds(
440
+ timestamp = selectedItem.value!!.startTime,
441
+ secondsToAdd = currentPosition.intValue
442
+ )
443
+ Log.i(
444
+ TAG,
445
+ "PlayTime: $playTime, Start Time: ${selectedItem.value!!.startTime}, End Time: ${selectedItem.value!!.endTime}"
446
+ )
447
+ Playback.startSDCardPlayback(
448
+ videoList = videoList,
449
+ video = selectedItem.value!!,
450
+ playTime = playTime,
451
+ isPaused = isPaused,
452
+ isPlaying = isPlaying,
453
+ isLoading = isLoading,
454
+ selectedItem = selectedItem
455
+ )
456
+ },
457
+ minimumValue = 0f,
458
+ maximumValue = DateUtil.calculateSecondsBetweenTimestamps(
459
+ startTime = selectedItem.value!!.startTime,
460
+ endTime = selectedItem.value!!.endTime
461
+ ).toFloat(),
462
+ modifier = Modifier
463
+ .fillMaxWidth()
464
+ )
465
+ }
466
+ }
467
+ }
468
+ }
469
+ }
470
+
471
+ @Composable
472
+ fun VideoItem(
473
+ video: TimePieceBean,
474
+ videoList: List<TimePieceBean>,
475
+ isPaused: MutableState<Boolean>,
476
+ isLoading: MutableState<Boolean>,
477
+ isPlaying: MutableState<Boolean>,
478
+ currentSliderValue: MutableIntState,
479
+ selectedItem: MutableState<TimePieceBean?>
480
+ ) {
481
+
482
+ Row(
483
+ modifier = Modifier
484
+ .fillMaxWidth(0.93f)
485
+ .height(78.dp)
486
+ .padding(top = 10.dp)
487
+ .shadow(1.dp)
488
+ .background(if (selectedItem.value == video) LocalColor.Secondary.Light else LocalColor.Monochrome.White)
489
+ .clickable {
490
+ if (selectedItem.value == video) {
491
+ currentSliderValue.intValue = 0
492
+ }
493
+ Playback.startSDCardPlayback(
494
+ videoList = videoList,
495
+ video = video,
496
+ isPaused = isPaused,
497
+ isPlaying = isPlaying,
498
+ isLoading = isLoading,
499
+ selectedItem = selectedItem
500
+ )
501
+ },
502
+ horizontalArrangement = Arrangement.SpaceBetween,
503
+ verticalAlignment = Alignment.CenterVertically
504
+ ) {
505
+ Column(
506
+ modifier = Modifier
507
+ .padding(horizontal = 15.dp, vertical = 10.dp)
508
+ ) {
509
+ Label(
510
+ title = DateUtil.convertTimestampToTime(video.startTime).uppercase(),
511
+ primary = true,
512
+ l = true,
513
+ center = true,
514
+ semiBold = true
515
+ )
516
+ Label(
517
+ title = "Duration: ${
518
+ DateUtil.calculateDuration(
519
+ startTime = video.startTime,
520
+ endTime = video.endTime
521
+ )
522
+ }",
523
+ secondary = true,
524
+ semiBold = true,
525
+ center = true,
526
+ s = true,
527
+ )
528
+ }
529
+ }
530
+ }
531
+
532
+ @Composable
533
+ fun BottomBar(
534
+ isMuted: MutableState<Boolean>,
535
+ isPaused: MutableState<Boolean>,
536
+ isPlaying: MutableState<Boolean>,
537
+ isLoading: MutableState<Boolean>,
538
+ videoList: List<TimePieceBean>,
539
+ currentSliderValue: MutableIntState,
540
+ isScreenShotSuccess: MutableState<Boolean>,
541
+ isRecordingSuccess: MutableState<Boolean>,
542
+ isRecordingFailure: MutableState<Boolean>,
543
+ selectedItem: MutableState<TimePieceBean?>
544
+ ) {
545
+ val context = LocalContext.current
546
+ val scope = rememberCoroutineScope()
547
+
548
+ val isRecording = remember {
549
+ mutableStateOf(false)
550
+ }
551
+
552
+ SuccessAlert(
553
+ title = strings(id = R.string.success),
554
+ description = strings(id = R.string.videoSuccess),
555
+ isVisible = isRecordingSuccess.value
556
+ ) {
557
+ isRecordingSuccess.value = false
558
+ }
559
+
560
+ SuccessAlert(
561
+ title = strings(id = R.string.failure),
562
+ description = strings(id = R.string.videoFailure),
563
+ isVisible = isRecordingFailure.value
564
+ ) {
565
+ isRecordingFailure.value = false
566
+ }
567
+
568
+ SuccessAlert(
569
+ title = strings(id = R.string.success),
570
+ description = strings(id = R.string.screenshotSuccess),
571
+ isVisible = isScreenShotSuccess.value
572
+ ) {
573
+ isScreenShotSuccess.value = false
574
+ }
575
+
576
+ Row(
577
+ modifier = Modifier
578
+ .fillMaxSize()
579
+ .background(LocalColor.Monochrome.Grey),
580
+ verticalAlignment = Alignment.CenterVertically,
581
+ horizontalArrangement = Arrangement.SpaceEvenly
582
+ ) {
583
+ SettingsButton(
584
+ modifier = if (!isMuted.value) {
585
+ Modifier.background(LocalColor.Primary.Dark)
586
+ } else {
587
+ Modifier
588
+ },
589
+ imageId = when {
590
+ isPlaying.value && !isMuted.value -> R.drawable.ic_sound_on
591
+ else -> R.drawable.ic_sound_off
592
+ },
593
+ event = if (!isMuted.value) "Playback_SpeakerOff" else "Playback_SpeakerOn"
594
+ ) {
595
+ if (isPlaying.value) {
596
+ Tuya.setMute(
597
+ mute = if (isMuted.value) 0 else 1,
598
+ setMute = { value ->
599
+ isMuted.value = value
600
+ }
601
+ )
602
+ }
603
+ }
604
+
605
+ SettingsButton(
606
+ if (isRecording.value) {
607
+ Modifier.background(LocalColor.Danger.Primary)
608
+ } else {
609
+ Modifier
610
+ },
611
+ imageId = R.drawable.ic_record,
612
+ event = if (isRecording.value) "Playback_StopRecording" else "Playback_StartRecording"
613
+ )
614
+ {
615
+ if (!isRecording.value) {
616
+ val status = ScreenCapture.startRecording(
617
+ context = context
618
+ ) { value ->
619
+ isRecording.value = value
620
+ }
621
+ } else {
622
+ scope.launch {
623
+ val status = ScreenCapture.stopRecording(
624
+ context = context
625
+ ) { value ->
626
+ isRecording.value = value
627
+ }
628
+ if (status) {
629
+ isRecordingSuccess.value = true
630
+ } else {
631
+ isRecordingFailure.value = true
632
+ }
633
+ }
634
+ }
635
+ }
636
+ SettingsButton(
637
+ imageId = if (isPaused.value) {
638
+ R.drawable.ic_play
639
+ } else {
640
+ R.drawable.ic_pause
641
+ },
642
+ event = if (isPaused.value) "Playback_Play" else "Playback_Pause"
643
+ ) {
644
+ if (isPaused.value) {
645
+ scope.launch {
646
+ selectedItem.value?.let {
647
+ val startTime = it.startTime
648
+ val endTime = it.endTime
649
+ val videoDuration = (endTime - startTime).toDouble()
650
+
651
+ if (currentSliderValue.intValue >= videoDuration) {
652
+ currentSliderValue.intValue = 0
653
+
654
+ // Restart playback from the beginning if it is completed
655
+ Playback.startSDCardPlayback(
656
+ videoList = videoList,
657
+ video = selectedItem.value!!,
658
+ isPaused = isPaused,
659
+ isPlaying = isPlaying,
660
+ isLoading = isLoading,
661
+ selectedItem = selectedItem
662
+ )
663
+ } else {
664
+ Playback.resumeSDCardPlayback(isPaused)
665
+ }
666
+ } ?: Playback.resumeSDCardPlayback(isPaused)
667
+ }
668
+ } else {
669
+ Playback.pauseSDCardPlayback(isPaused = isPaused)
670
+ }
671
+ }
672
+ SettingsButton(imageId = R.drawable.ic_camera, event = "Playback_Screenshot") {
673
+ scope.launch {
674
+ if (isPlaying.value) {
675
+ val status = ScreenCapture.screenShot(
676
+ context = context
677
+ )
678
+ if (status) {
679
+ isScreenShotSuccess.value = true
680
+ }
681
+ }
682
+ }
683
+ }
684
+ }
685
+ }