ScanFilePage.ets 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. import TitleBar from '../view/TitleBar'
  2. import { router, window } from '@kit.ArkUI'
  3. import { AppUtil, DeviceUtil, DisplayUtil, FileUtil, LogUtil, PreferencesUtil, ToastUtil } from '@pura/harmony-utils'
  4. import lottie from '@ohos/lottie'
  5. import { AnimationItem } from '@ohos/lottie'
  6. import { MessageEvents, taskpool, worker } from '@kit.ArkTS'
  7. import { fileUri, picker } from '@kit.CoreFileKit'
  8. import { LocalMusic } from '../view/LocalMusic'
  9. import { Utility } from '../common/util/Utility'
  10. import MediaTable from '../common/util/MediaTable'
  11. import { VideoItem } from '../viewmodel/VideoItem'
  12. import Logger from '../common/util/Logger'
  13. import { CommonConstants } from '../common/constants/CommonConstants'
  14. import { emitter } from '@kit.BasicServicesKit'
  15. import { BreakpointTypeEnum } from '../common/util/BreakpointSystem'
  16. import { resourceManager } from '@kit.LocalizationKit'
  17. import { ConfigurationConstant } from '@kit.AbilityKit'
  18. import { DialogHelper } from '@pura/harmony-dialog'
  19. @Preview
  20. @Entry
  21. @Component
  22. export struct ScanFilePage{
  23. @State rootPath:string = '' //音频根目录
  24. @State lockPath:string = ''
  25. @State strText:string = ''
  26. @State isStart:boolean = false
  27. @State isStartCover:boolean = false
  28. @State appName:string = ''
  29. //lottie动画构建渲染上下文
  30. private mainRenderingSettings: RenderingContextSettings = new RenderingContextSettings(true)
  31. private path:string = "common/lottie/leida.json"
  32. private successpath:string = "common/lottie/success2.json"
  33. private mainCanvasRenderingContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.mainRenderingSettings)
  34. private animateItem: AnimationItem | null = null;
  35. /** 当前断点类型(如大屏/小屏) */
  36. @StorageProp('currentBreakpoint') currentBreakpoint: string = BreakpointTypeEnum.MD;
  37. @StorageProp('themeColor') themeColor: string = '#FF4081';
  38. @State isDarkMode: boolean = false
  39. @StorageProp('currentColorMode') @Watch('onColorModeChange') currentMode: number =
  40. ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT;
  41. private windowClass: window.Window = globalThis.windowClass
  42. initLottie(lpath:string,isLoop:boolean){
  43. lottie.destroy(); //加载动画前先销毁之前加载的动画
  44. this.animateItem = lottie.loadAnimation({
  45. container: this.mainCanvasRenderingContext, // 渲染上下文
  46. renderer: 'canvas', // 渲染方式
  47. loop: isLoop, // 是否循环播放,默认true
  48. autoplay: true, // 是否自动播放,默认true
  49. name: '2024', // 动画名称
  50. contentMode: 'Contain', // 填充的模式
  51. frameRate: 30, //设置animator的刷帧率为30
  52. imagePath: 'lottie/images/', // 加载读取指定路径下的图片资源
  53. path: lpath, // json路径
  54. // initialSegment: [0,70]
  55. })
  56. lottie.setSpeed(0.7)
  57. }
  58. onPageShow() {
  59. this.initLottie(this.path,true)
  60. setTimeout(()=>{
  61. lottie.pause()
  62. },88)
  63. }
  64. // 组件生命周期
  65. async aboutToAppear() {
  66. Utility.getAppName(getContext(this)).then((appName:string)=>{
  67. this.appName = appName
  68. })
  69. let themeColor = PreferencesUtil.getStringSync('THEME_COLOR', '#FF4081');
  70. AppStorage.setOrCreate('themeColor', themeColor);
  71. this.themeColor = themeColor;
  72. this.isDarkMode = this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK
  73. const documentViewPicker = new picker.DocumentViewPicker()
  74. let documentSaveResult = await documentViewPicker.save({ pickerMode: picker.DocumentPickerMode.DOWNLOAD })
  75. this.rootPath = new fileUri.FileUri(documentSaveResult[0]).path
  76. this.lockPath = this.rootPath +'/'+ LocalMusic.STR_LOCK_VIDEO
  77. LogUtil.info('onecold 文件扫描 aboutToAppear')
  78. }
  79. onColorModeChange() {
  80. this.isDarkMode = this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK
  81. }
  82. endScan(isOnekey:boolean){
  83. const eventData: emitter.EventData = {};
  84. emitter.emit({ eventId: 101 }, eventData); // 发送视频打开广播事件
  85. this.watchStatus(false)
  86. ToastUtil.showToast('扫描文件入库成功!')
  87. if(isOnekey){
  88. this.isStartCover = false
  89. }else{
  90. this.isStart = false
  91. }
  92. this.strText = ''
  93. //结束动画
  94. lottie.pause()
  95. lottie.stop()
  96. this.initLottie(this.successpath,false)
  97. lottie.play()
  98. }
  99. //扫描过程中屏幕要保存常亮
  100. watchStatus(isKeep:boolean) {
  101. this.windowClass.setWindowKeepScreenOn(isKeep);
  102. }
  103. // 组件生命周期
  104. aboutToDisappear() {
  105. lottie.destroy();
  106. }
  107. // @State titleBarModel: TitleBar.Model = new TitleBar.Model()
  108. // .setTitleTextStyle(FontStyle.Normal)
  109. // .setTitleBarStyle(TitleBar.BarStyle.TRANSPARENT)
  110. // .setLeftIcon($r('app.media.left_back_white'))
  111. // .setTitleName("文件扫描")
  112. // .setTitleFontColor(Color.White)
  113. // .setTitleBarBackground(this.themeColor)
  114. // .setLeftTitleBackground(this.themeColor)
  115. // .setTitleBarBottomLineColor(this.themeColor)
  116. // .setOnLeftClickListener(() => {
  117. // router.back()
  118. //
  119. // })
  120. initState(isOnekey:boolean){
  121. this.initLottie(this.path,true)
  122. if(isOnekey){
  123. this.isStartCover = true
  124. }else{
  125. this.isStart = true
  126. }
  127. this.strText = Utility.resourceToString(getContext(this),$r('app.string.scaning'))
  128. lottie.play()
  129. }
  130. doOptimize(isOnekey:boolean){
  131. this.watchStatus(true)
  132. this.initState(isOnekey)
  133. const task = new taskpool.Task(scanDirectoryTask, getContext(this), this.rootPath,
  134. this.lockPath,PreferencesUtil.getStringSync('COVER_API',''));
  135. taskpool.execute(task, taskpool.Priority.HIGH).then(()=>{
  136. this.endScan(isOnekey)
  137. }).catch((e:object)=>{
  138. console.info("task1 catch e: " + e);
  139. })
  140. }
  141. //是不是手机横屏
  142. isPhoneLan() {
  143. if (DeviceUtil.getDeviceType() === resourceManager.DeviceType.DEVICE_TYPE_PHONE
  144. && this.currentBreakpoint !== BreakpointTypeEnum.SM) {
  145. if (DisplayUtil.getFoldStatus() === 0 ) {
  146. return true
  147. }
  148. }
  149. return false
  150. }
  151. build() {
  152. Column() {
  153. // 顶部安全区和自定义标题栏
  154. Column() {
  155. // 顶部安全区
  156. Blank()
  157. .height(this.isPhoneLan() ? 0 : px2vp(AppUtil.getStatusBarHeight()))
  158. .backgroundColor(this.isDarkMode?$r('app.color.title_bar_bg'):this.themeColor)
  159. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
  160. // 自定义标题栏(Stack实现绝对居中)
  161. Stack() {
  162. // 居中标题
  163. Text('文件扫描')
  164. .fontSize(18)
  165. .fontColor(Color.White)
  166. .align(Alignment.Center)
  167. // 左右按钮
  168. Row() {
  169. Image($r('app.media.left_back_white'))
  170. .width(28)
  171. .height(28)
  172. .margin({ left: 12, right: 8 })
  173. .onClick(() => {
  174. router.back();
  175. })
  176. Blank().flexGrow(1)
  177. Blank().width(32)
  178. }
  179. .height(48)
  180. .width('100%')
  181. .alignItems(VerticalAlign.Center)
  182. }
  183. .height(48)
  184. .width('100%')
  185. .backgroundColor(this.isDarkMode?$r('app.color.title_bar_bg'):this.themeColor)
  186. }
  187. // Column(){
  188. // Line().width('100%').height('100%')
  189. // }
  190. // .height(this.isPhoneLan()?0:px2vp(AppUtil.getStatusBarHeight()))
  191. // .backgroundColor(this.themeColor)
  192. // .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
  193. // TitleBar({ model: $titleBarModel })
  194. Scroll(){
  195. Column() {
  196. Stack(){
  197. Image($r('app.media.file_search'))
  198. .width(48)
  199. .height(48)
  200. .fillColor(this.themeColor)
  201. .alignSelf(ItemAlign.Center)
  202. //lottie动画
  203. Canvas(this.mainCanvasRenderingContext)
  204. .width(160)
  205. .height(160)
  206. .onReady(()=>{
  207. //抗锯齿的设置
  208. this.mainCanvasRenderingContext.imageSmoothingEnabled = true;
  209. this.mainCanvasRenderingContext.imageSmoothingQuality = 'medium'
  210. })
  211. }
  212. // Text(this.strText)
  213. // .margin({ right: 20,top:20,bottom:20 })
  214. // .fontSize(16)
  215. // .fontColor(Color.Black)
  216. // .fontWeight(480)
  217. Column() {
  218. Row() {
  219. Image($r('app.media.selected'))
  220. .width(16)
  221. .height(16)
  222. .fillColor(this.themeColor)
  223. .margin({ left:10})
  224. .alignSelf(ItemAlign.Center)
  225. Text(Utility.resourceToString(getContext(this),$r('app.string.file_scan_tip_one'))
  226. +`${this.appName} `+
  227. Utility.resourceToString(getContext(this),$r('app.string.file_scan_tip_one_1')))
  228. .margin({ left: 10, right: 20 })
  229. .fontSize(15)
  230. .fontColor(Color.Gray)
  231. .fontWeight(480)
  232. .layoutWeight(1)
  233. }
  234. .width('100%')
  235. .margin({ left:25,right: 25 ,top:20 })
  236. Row() {
  237. Image($r('app.media.selected'))
  238. .width(16)
  239. .height(16)
  240. .fillColor(this.themeColor)
  241. .margin({ left:10})
  242. .alignSelf(ItemAlign.Center)
  243. Text($r('app.string.file_scan_tip_two'))
  244. .margin({ left: 10, right: 20 })
  245. .fontSize(15)
  246. .fontColor(Color.Gray)
  247. .fontWeight(480)
  248. .layoutWeight(1)
  249. }
  250. .width('100%')
  251. .margin({ left:25,right: 25 ,top:20,bottom:20 })
  252. }
  253. .backgroundColor($r('app.color.index_background'))
  254. .margin({ left:25,right: 25,top:20,bottom:20 })
  255. .borderRadius(20)
  256. Column() {
  257. Button(this.isStart?'正在扫描':$r('app.string.start_scan'), { type: ButtonType.Capsule, stateEffect: false })
  258. .width('60%')
  259. .height(55)
  260. .margin({ top: 20, bottom: 10 })
  261. .linearGradient({
  262. direction: GradientDirection.Right,
  263. colors: [['#ff37a0fc', 0.0], ['#67e667', 0.5], ['#f5856e', 1.0]]
  264. })
  265. .enabled(this.isStart ?false:true)
  266. .onClick(() => {
  267. this.doOptimize(false)
  268. })
  269. .alignSelf(ItemAlign.Center)
  270. Button(this.isStartCover?'正在扫描':$r('app.string.onekey_cover'), { type: ButtonType.Capsule, stateEffect: false })
  271. .width('60%')
  272. .height(55)
  273. .margin({ top: 10, bottom: 20 })
  274. .linearGradient({
  275. direction: GradientDirection.Right,
  276. colors: [['#ff37a0fc', 0.0], ['#67e667', 0.5], ['#f5856e', 1.0]]
  277. })
  278. .enabled(this.isStartCover ?false:true)
  279. .onClick(() => {
  280. if(PreferencesUtil.getStringSync('COVER_API','')===''){
  281. this.showTipsDialog()
  282. return
  283. }
  284. this.doOptimize(true)
  285. })
  286. .alignSelf(ItemAlign.Center)
  287. }
  288. }
  289. .height('100%')
  290. .width('100%')
  291. .align(Alignment.Top)
  292. }
  293. .height(1000)
  294. }
  295. }
  296. showTipsDialog() {
  297. DialogHelper.showCustomContentDialog({
  298. dialogId: 'tips',
  299. title: "友情提示",
  300. autoCancel: false, //点击遮障层时,不关闭弹窗
  301. backCancel: true, //点击返回键,不关闭弹窗
  302. contentBuilder: () => {
  303. this.customTipsBuilder("请到设置界面配置封面服务器地址!")
  304. },
  305. buttons: [],
  306. })
  307. }
  308. @Builder
  309. customTipsBuilder(content: string) {
  310. Column() {
  311. Text(content)
  312. .fontColor(Color.Gray)
  313. .fontSize(16)
  314. .alignSelf(ItemAlign.Start)
  315. .margin({ bottom: 15 })
  316. .fontSize(16)
  317. Row() {
  318. Button('取消')
  319. .fontColor(Color.White)
  320. .backgroundColor($r('app.color.title_bar_bg'))//.linearGradient( { direction: GradientDirection.Right, colors:[['#ff37a0fc',0.0],['#67e667',0.5],['#f5856e',1.0]]})
  321. .height(50)
  322. .layoutWeight(1)
  323. .stateEffect(true)
  324. .margin({ right: 6 })
  325. .onClick(() => {
  326. DialogHelper.closeDialog('tips'); //关闭弹框
  327. })
  328. Button('跳转')
  329. .fontColor(Color.White)//.linearGradient( { direction: GradientDirection.Right, colors:[['#ff37a0fc',0.0],['#67e667',0.5],['#f5856e',1.0]]})
  330. .layoutWeight(1)
  331. .height(50)
  332. .backgroundColor($r('app.color.title_bar_bg'))
  333. .stateEffect(true)
  334. .margin({ left: 6 })
  335. .onClick(() => {
  336. DialogHelper.closeDialog('tips'); //关闭弹框
  337. router.pushUrl({
  338. url: 'pages/SettingPage'
  339. }, router.RouterMode.Single);
  340. })
  341. }
  342. }
  343. .width("100%")
  344. .padding(10)
  345. }
  346. }
  347. @Concurrent
  348. async function scanDirectoryTask(context: Context, dirPath: string, lockPath: string,cover_api:string) {
  349. const stack: string[] = [dirPath];
  350. const table: MediaTable = new MediaTable(context);
  351. while (stack.length > 0) {
  352. const currentPath = stack.pop()!;
  353. const files = FileUtil.listFileSync(currentPath);
  354. await Promise.all(files.map(async (file) => {
  355. const fPath = `${currentPath}/${file}`;
  356. if (fPath === lockPath) return;
  357. if (FileUtil.isDirectory(fPath)) {
  358. stack.push(fPath); // 使用栈结构代替递归
  359. } else {
  360. if (fPath.endsWith('.lrc') || fPath.endsWith('.srt')) return;
  361. if (Utility.isMeidaByExtension(fPath)) {
  362. const mediaItem = await Utility.uriGetMusicAssetsFromFile(
  363. context, fPath, CommonConstants.TYPE_LOCAL, true
  364. );
  365. table.insert(mediaItem, (id: number) => {
  366. },cover_api);
  367. }
  368. }
  369. }));
  370. }
  371. }